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