• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "GrAAStrokeRectOp.h"
9 
10 #include "GrDefaultGeoProcFactory.h"
11 #include "GrOpFlushState.h"
12 #include "GrResourceKey.h"
13 #include "GrResourceProvider.h"
14 #include "SkStrokeRec.h"
15 
16 GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
17 GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
18 
set_inset_fan(SkPoint * pts,size_t stride,const SkRect & r,SkScalar dx,SkScalar dy)19 static void set_inset_fan(SkPoint* pts, size_t stride, const SkRect& r, SkScalar dx, SkScalar dy) {
20     pts->setRectFan(r.fLeft + dx, r.fTop + dy, r.fRight - dx, r.fBottom - dy, stride);
21 }
22 
23 // We support all hairlines, bevels, and miters, but not round joins. Also, check whether the miter
24 // limit makes a miter join effectively beveled.
allowed_stroke(const SkStrokeRec & stroke,bool * isMiter)25 inline static bool allowed_stroke(const SkStrokeRec& stroke, bool* isMiter) {
26     SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style ||
27              stroke.getStyle() == SkStrokeRec::kHairline_Style);
28     // For hairlines, make bevel and round joins appear the same as mitered ones.
29     if (!stroke.getWidth()) {
30         *isMiter = true;
31         return true;
32     }
33     if (stroke.getJoin() == SkPaint::kBevel_Join) {
34         *isMiter = false;
35         return true;
36     }
37     if (stroke.getJoin() == SkPaint::kMiter_Join) {
38         *isMiter = stroke.getMiter() >= SK_ScalarSqrt2;
39         return true;
40     }
41     return false;
42 }
43 
compute_rects(SkRect * devOutside,SkRect * devOutsideAssist,SkRect * devInside,bool * isDegenerate,const SkMatrix & viewMatrix,const SkRect & rect,SkScalar strokeWidth,bool miterStroke)44 static void compute_rects(SkRect* devOutside, SkRect* devOutsideAssist, SkRect* devInside,
45                           bool* isDegenerate, const SkMatrix& viewMatrix, const SkRect& rect,
46                           SkScalar strokeWidth, bool miterStroke) {
47     SkRect devRect;
48     viewMatrix.mapRect(&devRect, rect);
49 
50     SkVector devStrokeSize;
51     if (strokeWidth > 0) {
52         devStrokeSize.set(strokeWidth, strokeWidth);
53         viewMatrix.mapVectors(&devStrokeSize, 1);
54         devStrokeSize.setAbs(devStrokeSize);
55     } else {
56         devStrokeSize.set(SK_Scalar1, SK_Scalar1);
57     }
58 
59     const SkScalar dx = devStrokeSize.fX;
60     const SkScalar dy = devStrokeSize.fY;
61     const SkScalar rx = SkScalarHalf(dx);
62     const SkScalar ry = SkScalarHalf(dy);
63 
64     *devOutside = devRect;
65     *devOutsideAssist = devRect;
66     *devInside = devRect;
67 
68     devOutside->outset(rx, ry);
69     devInside->inset(rx, ry);
70 
71     // If we have a degenerate stroking rect(ie the stroke is larger than inner rect) then we
72     // make a degenerate inside rect to avoid double hitting.  We will also jam all of the points
73     // together when we render these rects.
74     SkScalar spare;
75     {
76         SkScalar w = devRect.width() - dx;
77         SkScalar h = devRect.height() - dy;
78         spare = SkTMin(w, h);
79     }
80 
81     *isDegenerate = spare <= 0;
82     if (*isDegenerate) {
83         devInside->fLeft = devInside->fRight = devRect.centerX();
84         devInside->fTop = devInside->fBottom = devRect.centerY();
85     }
86 
87     // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
88     // to draw the outside of the octagon. Because there are 8 vertices on the outer
89     // edge, while vertex number of inner edge is 4, the same as miter-stroke.
90     if (!miterStroke) {
91         devOutside->inset(0, ry);
92         devOutsideAssist->outset(0, ry);
93     }
94 }
95 
create_stroke_rect_gp(bool tweakAlphaForCoverage,const SkMatrix & viewMatrix,bool usesLocalCoords)96 static sk_sp<GrGeometryProcessor> create_stroke_rect_gp(bool tweakAlphaForCoverage,
97                                                         const SkMatrix& viewMatrix,
98                                                         bool usesLocalCoords) {
99     using namespace GrDefaultGeoProcFactory;
100 
101     Coverage::Type coverageType;
102     if (tweakAlphaForCoverage) {
103         coverageType = Coverage::kSolid_Type;
104     } else {
105         coverageType = Coverage::kAttribute_Type;
106     }
107     LocalCoords::Type localCoordsType =
108             usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
109     return MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type, coverageType, localCoordsType,
110                               viewMatrix);
111 }
112 
113 class AAStrokeRectOp final : public GrMeshDrawOp {
114 public:
115     DEFINE_OP_CLASS_ID
116 
AAStrokeRectOp(GrColor color,const SkMatrix & viewMatrix,const SkRect & devOutside,const SkRect & devInside)117     AAStrokeRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& devOutside,
118                    const SkRect& devInside)
119             : INHERITED(ClassID()), fViewMatrix(viewMatrix) {
120         SkASSERT(!devOutside.isEmpty());
121         SkASSERT(!devInside.isEmpty());
122 
123         fRects.emplace_back(RectInfo{color, devOutside, devOutside, devInside, false});
124         this->setBounds(devOutside, HasAABloat::kYes, IsZeroArea::kNo);
125         fMiterStroke = true;
126     }
127 
Make(GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkStrokeRec & stroke)128     static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
129                                               const SkRect& rect, const SkStrokeRec& stroke) {
130         bool isMiter;
131         if (!allowed_stroke(stroke, &isMiter)) {
132             return nullptr;
133         }
134 
135         AAStrokeRectOp* op = new AAStrokeRectOp();
136         op->fMiterStroke = isMiter;
137         RectInfo& info = op->fRects.push_back();
138         compute_rects(&info.fDevOutside, &info.fDevOutsideAssist, &info.fDevInside,
139                       &info.fDegenerate, viewMatrix, rect, stroke.getWidth(), isMiter);
140         info.fColor = color;
141         if (isMiter) {
142             op->setBounds(info.fDevOutside, HasAABloat::kYes, IsZeroArea::kNo);
143         } else {
144             // The outer polygon of the bevel stroke is an octagon specified by the points of a
145             // pair of overlapping rectangles where one is wide and the other is narrow.
146             SkRect bounds = info.fDevOutside;
147             bounds.joinPossiblyEmptyRect(info.fDevOutsideAssist);
148             op->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
149         }
150         op->fViewMatrix = viewMatrix;
151         return std::unique_ptr<GrMeshDrawOp>(op);
152     }
153 
name() const154     const char* name() const override { return "AAStrokeRect"; }
155 
dumpInfo() const156     SkString dumpInfo() const override {
157         SkString string;
158         for (const auto& info : fRects) {
159             string.appendf(
160                     "Color: 0x%08x, ORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
161                     "AssistORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
162                     "IRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], Degen: %d",
163                     info.fColor, info.fDevOutside.fLeft, info.fDevOutside.fTop,
164                     info.fDevOutside.fRight, info.fDevOutside.fBottom, info.fDevOutsideAssist.fLeft,
165                     info.fDevOutsideAssist.fTop, info.fDevOutsideAssist.fRight,
166                     info.fDevOutsideAssist.fBottom, info.fDevInside.fLeft, info.fDevInside.fTop,
167                     info.fDevInside.fRight, info.fDevInside.fBottom, info.fDegenerate);
168         }
169         string.append(DumpPipelineInfo(*this->pipeline()));
170         string.append(INHERITED::dumpInfo());
171         return string;
172     }
173 
174 private:
AAStrokeRectOp()175     AAStrokeRectOp() : INHERITED(ClassID()) {}
176 
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const177     void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
178                                             GrPipelineAnalysisCoverage* coverage) const override {
179         color->setToConstant(fRects[0].fColor);
180         *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
181     }
182     void applyPipelineOptimizations(const GrPipelineOptimizations&) override;
183     void onPrepareDraws(Target*) const override;
184 
185     static const int kMiterIndexCnt = 3 * 24;
186     static const int kMiterVertexCnt = 16;
187     static const int kNumMiterRectsInIndexBuffer = 256;
188 
189     static const int kBevelIndexCnt = 48 + 36 + 24;
190     static const int kBevelVertexCnt = 24;
191     static const int kNumBevelRectsInIndexBuffer = 256;
192 
193     static const GrBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider, bool miterStroke);
194 
usesLocalCoords() const195     bool usesLocalCoords() const { return fUsesLocalCoords; }
canTweakAlphaForCoverage() const196     bool canTweakAlphaForCoverage() const { return fCanTweakAlphaForCoverage; }
viewMatrix() const197     const SkMatrix& viewMatrix() const { return fViewMatrix; }
miterStroke() const198     bool miterStroke() const { return fMiterStroke; }
199 
200     bool onCombineIfPossible(GrOp* t, const GrCaps&) override;
201 
202     void generateAAStrokeRectGeometry(void* vertices,
203                                       size_t offset,
204                                       size_t vertexStride,
205                                       int outerVertexNum,
206                                       int innerVertexNum,
207                                       GrColor color,
208                                       const SkRect& devOutside,
209                                       const SkRect& devOutsideAssist,
210                                       const SkRect& devInside,
211                                       bool miterStroke,
212                                       bool degenerate,
213                                       bool tweakAlphaForCoverage) const;
214 
215     // TODO support AA rotated stroke rects by copying around view matrices
216     struct RectInfo {
217         GrColor fColor;
218         SkRect fDevOutside;
219         SkRect fDevOutsideAssist;
220         SkRect fDevInside;
221         bool fDegenerate;
222     };
223 
224     SkSTArray<1, RectInfo, true> fRects;
225     bool fUsesLocalCoords;
226     bool fCanTweakAlphaForCoverage;
227     SkMatrix fViewMatrix;
228     bool fMiterStroke;
229 
230     typedef GrMeshDrawOp INHERITED;
231 };
232 
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)233 void AAStrokeRectOp::applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) {
234     optimizations.getOverrideColorIfSet(&fRects[0].fColor);
235 
236     fUsesLocalCoords = optimizations.readsLocalCoords();
237     fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
238     fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
239 }
240 
onPrepareDraws(Target * target) const241 void AAStrokeRectOp::onPrepareDraws(Target* target) const {
242     bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
243 
244     sk_sp<GrGeometryProcessor> gp(create_stroke_rect_gp(canTweakAlphaForCoverage,
245                                                         this->viewMatrix(),
246                                                         this->usesLocalCoords()));
247     if (!gp) {
248         SkDebugf("Couldn't create GrGeometryProcessor\n");
249         return;
250     }
251 
252     size_t vertexStride = gp->getVertexStride();
253 
254     SkASSERT(canTweakAlphaForCoverage
255                      ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr)
256                      : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
257     int innerVertexNum = 4;
258     int outerVertexNum = this->miterStroke() ? 4 : 8;
259     int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
260     int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
261     int instanceCount = fRects.count();
262 
263     const sk_sp<const GrBuffer> indexBuffer(
264             GetIndexBuffer(target->resourceProvider(), this->miterStroke()));
265     InstancedHelper helper;
266     void* vertices =
267             helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
268                         verticesPerInstance, indicesPerInstance, instanceCount);
269     if (!vertices || !indexBuffer) {
270         SkDebugf("Could not allocate vertices\n");
271         return;
272     }
273 
274     for (int i = 0; i < instanceCount; i++) {
275         const RectInfo& info = fRects[i];
276         this->generateAAStrokeRectGeometry(vertices,
277                                            i * verticesPerInstance * vertexStride,
278                                            vertexStride,
279                                            outerVertexNum,
280                                            innerVertexNum,
281                                            info.fColor,
282                                            info.fDevOutside,
283                                            info.fDevOutsideAssist,
284                                            info.fDevInside,
285                                            fMiterStroke,
286                                            info.fDegenerate,
287                                            canTweakAlphaForCoverage);
288     }
289     helper.recordDraw(target, gp.get());
290 }
291 
GetIndexBuffer(GrResourceProvider * resourceProvider,bool miterStroke)292 const GrBuffer* AAStrokeRectOp::GetIndexBuffer(GrResourceProvider* resourceProvider,
293                                                bool miterStroke) {
294     if (miterStroke) {
295         // clang-format off
296         static const uint16_t gMiterIndices[] = {
297             0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
298             1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
299             2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
300             3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
301 
302             0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
303             1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
304             2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
305             3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
306 
307             0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
308             1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
309             2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
310             3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
311         };
312         // clang-format on
313         GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
314         GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
315         return resourceProvider->findOrCreateInstancedIndexBuffer(
316                 gMiterIndices, kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
317                 gMiterIndexBufferKey);
318     } else {
319         /**
320          * As in miter-stroke, index = a + b, and a is the current index, b is the shift
321          * from the first index. The index layout:
322          * outer AA line: 0~3, 4~7
323          * outer edge:    8~11, 12~15
324          * inner edge:    16~19
325          * inner AA line: 20~23
326          * Following comes a bevel-stroke rect and its indices:
327          *
328          *           4                                 7
329          *            *********************************
330          *          *   ______________________________  *
331          *         *  / 12                          15 \  *
332          *        *  /                                  \  *
333          *     0 *  |8     16_____________________19  11 |  * 3
334          *       *  |       |                    |       |  *
335          *       *  |       |  ****************  |       |  *
336          *       *  |       |  * 20        23 *  |       |  *
337          *       *  |       |  *              *  |       |  *
338          *       *  |       |  * 21        22 *  |       |  *
339          *       *  |       |  ****************  |       |  *
340          *       *  |       |____________________|       |  *
341          *     1 *  |9    17                      18   10|  * 2
342          *        *  \                                  /  *
343          *         *  \13 __________________________14/  *
344          *          *                                   *
345          *           **********************************
346          *          5                                  6
347          */
348         // clang-format off
349         static const uint16_t gBevelIndices[] = {
350             // Draw outer AA, from outer AA line to outer edge, shift is 0.
351             0 + 0, 1 + 0,  9 + 0,  9 + 0,  8 + 0, 0 + 0,
352             1 + 0, 5 + 0, 13 + 0, 13 + 0,  9 + 0, 1 + 0,
353             5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
354             6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
355             2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
356             3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
357             7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
358             4 + 0, 0 + 0,  8 + 0,  8 + 0, 12 + 0, 4 + 0,
359 
360             // Draw the stroke, from outer edge to inner edge, shift is 8.
361             0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
362             1 + 8, 5 + 8, 9 + 8,
363             5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
364             6 + 8, 2 + 8, 10 + 8,
365             2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
366             3 + 8, 7 + 8, 11 + 8,
367             7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
368             4 + 8, 0 + 8, 8 + 8,
369 
370             // Draw the inner AA, from inner edge to inner AA line, shift is 16.
371             0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
372             1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
373             2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
374             3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
375         };
376         // clang-format on
377         GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
378 
379         GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
380         return resourceProvider->findOrCreateInstancedIndexBuffer(
381                 gBevelIndices, kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
382                 gBevelIndexBufferKey);
383     }
384 }
385 
onCombineIfPossible(GrOp * t,const GrCaps & caps)386 bool AAStrokeRectOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
387     AAStrokeRectOp* that = t->cast<AAStrokeRectOp>();
388 
389     if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
390                                 that->bounds(), caps)) {
391         return false;
392     }
393 
394     // TODO combine across miterstroke changes
395     if (this->miterStroke() != that->miterStroke()) {
396         return false;
397     }
398 
399     // We apply the viewmatrix to the rect points on the cpu.  However, if the pipeline uses
400     // local coords then we won't be able to combine.  We could actually upload the viewmatrix
401     // using vertex attributes in these cases, but haven't investigated that
402     if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
403         return false;
404     }
405 
406     // In the event of two ops, one who can tweak, one who cannot, we just fall back to not
407     // tweaking.
408     if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
409         fCanTweakAlphaForCoverage = false;
410     }
411 
412     fRects.push_back_n(that->fRects.count(), that->fRects.begin());
413     this->joinBounds(*that);
414     return true;
415 }
416 
setup_scale(int * scale,SkScalar inset)417 static void setup_scale(int* scale, SkScalar inset) {
418     if (inset < SK_ScalarHalf) {
419         *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
420         SkASSERT(*scale >= 0 && *scale <= 255);
421     } else {
422         *scale = 0xff;
423     }
424 }
425 
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) const426 void AAStrokeRectOp::generateAAStrokeRectGeometry(void* vertices,
427                                                   size_t offset,
428                                                   size_t vertexStride,
429                                                   int outerVertexNum,
430                                                   int innerVertexNum,
431                                                   GrColor color,
432                                                   const SkRect& devOutside,
433                                                   const SkRect& devOutsideAssist,
434                                                   const SkRect& devInside,
435                                                   bool miterStroke,
436                                                   bool degenerate,
437                                                   bool tweakAlphaForCoverage) const {
438     intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
439 
440     // We create vertices for four nested rectangles. There are two ramps from 0 to full
441     // coverage, one on the exterior of the stroke and the other on the interior.
442     // The following pointers refer to the four rects, from outermost to innermost.
443     SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
444     SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
445     SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
446     SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(
447             verts + (2 * outerVertexNum + innerVertexNum) * vertexStride);
448 
449 #ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
450     // TODO: this only really works if the X & Y margins are the same all around
451     // the rect (or if they are all >= 1.0).
452     SkScalar inset;
453     if (!degenerate) {
454         inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
455         inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
456         inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
457         if (miterStroke) {
458             inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
459         } else {
460             inset = SK_ScalarHalf *
461                     SkMinScalar(inset, devOutsideAssist.fBottom - devInside.fBottom);
462         }
463         SkASSERT(inset >= 0);
464     } else {
465         // TODO use real devRect here
466         inset = SkMinScalar(devOutside.width(), SK_Scalar1);
467         inset = SK_ScalarHalf *
468                 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height()));
469     }
470 #else
471     SkScalar inset;
472     if (!degenerate) {
473         inset = SK_ScalarHalf;
474     } else {
475         // TODO use real devRect here
476         inset = SkMinScalar(devOutside.width(), SK_Scalar1);
477         inset = SK_ScalarHalf *
478                 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height()));
479     }
480 #endif
481 
482     if (miterStroke) {
483         // outermost
484         set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
485         // inner two
486         set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
487         if (!degenerate) {
488             set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
489             // innermost
490             set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
491         } else {
492             // When the interior rect has become degenerate we smoosh to a single point
493             SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
494             fan2Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
495                                 devInside.fBottom, vertexStride);
496             fan3Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
497                                 devInside.fBottom, vertexStride);
498         }
499     } else {
500         SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
501         SkPoint* fan1AssistPos =
502                 reinterpret_cast<SkPoint*>(verts + (outerVertexNum + 4) * vertexStride);
503         // outermost
504         set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
505         set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
506                       -SK_ScalarHalf);
507         // outer one of the inner two
508         set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
509         set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset);
510         if (!degenerate) {
511             // inner one of the inner two
512             set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
513             // innermost
514             set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
515         } else {
516             // When the interior rect has become degenerate we smoosh to a single point
517             SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
518             fan2Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
519                                 devInside.fBottom, vertexStride);
520             fan3Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
521                                 devInside.fBottom, vertexStride);
522         }
523     }
524 
525     // Make verts point to vertex color and then set all the color and coverage vertex attrs
526     // values. The outermost rect has 0 coverage
527     verts += sizeof(SkPoint);
528     for (int i = 0; i < outerVertexNum; ++i) {
529         if (tweakAlphaForCoverage) {
530             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
531         } else {
532             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
533             *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
534         }
535     }
536 
537     // scale is the coverage for the the inner two rects.
538     int scale;
539     setup_scale(&scale, inset);
540 
541     float innerCoverage = GrNormalizeByteToFloat(scale);
542     GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
543 
544     verts += outerVertexNum * vertexStride;
545     for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
546         if (tweakAlphaForCoverage) {
547             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
548         } else {
549             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
550             *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
551         }
552     }
553 
554     // The innermost rect has 0 coverage, unless we are degenerate, in which case we must apply the
555     // scaled coverage
556     verts += (outerVertexNum + innerVertexNum) * vertexStride;
557     if (!degenerate) {
558         innerCoverage = 0;
559         scaledColor = 0;
560     }
561 
562     for (int i = 0; i < innerVertexNum; ++i) {
563         if (tweakAlphaForCoverage) {
564             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
565         } else {
566             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
567             *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
568         }
569     }
570 }
571 
572 namespace GrAAStrokeRectOp {
573 
MakeFillBetweenRects(GrColor color,const SkMatrix & viewMatrix,const SkRect & devOutside,const SkRect & devInside)574 std::unique_ptr<GrMeshDrawOp> MakeFillBetweenRects(GrColor color,
575                                                    const SkMatrix& viewMatrix,
576                                                    const SkRect& devOutside,
577                                                    const SkRect& devInside) {
578     return std::unique_ptr<GrMeshDrawOp>(
579             new AAStrokeRectOp(color, viewMatrix, devOutside, devInside));
580 }
581 
Make(GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkStrokeRec & stroke)582 std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
583                                    const SkMatrix& viewMatrix,
584                                    const SkRect& rect,
585                                    const SkStrokeRec& stroke) {
586     return AAStrokeRectOp::Make(color, viewMatrix, rect, stroke);
587 }
588 }
589 
590 ///////////////////////////////////////////////////////////////////////////////////////////////////
591 
592 #if GR_TEST_UTILS
593 
594 #include "GrDrawOpTest.h"
595 
DRAW_OP_TEST_DEFINE(AAStrokeRectOp)596 DRAW_OP_TEST_DEFINE(AAStrokeRectOp) {
597     bool miterStroke = random->nextBool();
598 
599     // Create either a empty rect or a non-empty rect.
600     SkRect rect =
601             random->nextBool() ? SkRect::MakeXYWH(10, 10, 50, 40) : SkRect::MakeXYWH(6, 7, 0, 0);
602     SkScalar minDim = SkMinScalar(rect.width(), rect.height());
603     SkScalar strokeWidth = random->nextUScalar1() * minDim;
604 
605     GrColor color = GrRandomColor(random);
606 
607     SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
608     rec.setStrokeStyle(strokeWidth);
609     rec.setStrokeParams(SkPaint::kButt_Cap,
610                         miterStroke ? SkPaint::kMiter_Join : SkPaint::kBevel_Join, 1.f);
611     SkMatrix matrix = GrTest::TestMatrixRectStaysRect(random);
612     return GrAAStrokeRectOp::Make(color, matrix, rect, rec);
613 }
614 
615 #endif
616