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