blob: d6c4d9711dec3fa8d00d26c7416407c755eaecf2 [file] [log] [blame]
joshualitt9ff64252015-08-10 09:03:51 -07001/*
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
joshualitt9ff64252015-08-10 09:03:51 -07008#include "GrDefaultGeoProcFactory.h"
Brian Salomon742e31d2016-12-07 17:06:19 -05009#include "GrOpFlushState.h"
Brian Salomonbaaf4392017-06-15 09:59:23 -040010#include "GrRectOpFactory.h"
joshualitt9ff64252015-08-10 09:03:51 -070011#include "GrResourceKey.h"
12#include "GrResourceProvider.h"
Brian Salomonbaaf4392017-06-15 09:59:23 -040013#include "GrSimpleMeshDrawOpHelper.h"
Brian Osmancfec9d52018-11-20 11:39:15 -050014#include "GrVertexWriter.h"
Hal Canary6f6961e2017-01-31 13:50:44 -050015#include "SkStrokeRec.h"
joshualitt9ff64252015-08-10 09:03:51 -070016
17GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
18GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
19
bsalomon8b7a9e12016-07-06 13:06:22 -070020// We support all hairlines, bevels, and miters, but not round joins. Also, check whether the miter
21// limit makes a miter join effectively beveled.
22inline static bool allowed_stroke(const SkStrokeRec& stroke, bool* isMiter) {
23 SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style ||
24 stroke.getStyle() == SkStrokeRec::kHairline_Style);
25 // For hairlines, make bevel and round joins appear the same as mitered ones.
26 if (!stroke.getWidth()) {
27 *isMiter = true;
28 return true;
29 }
30 if (stroke.getJoin() == SkPaint::kBevel_Join) {
31 *isMiter = false;
32 return true;
33 }
34 if (stroke.getJoin() == SkPaint::kMiter_Join) {
35 *isMiter = stroke.getMiter() >= SK_ScalarSqrt2;
36 return true;
37 }
38 return false;
39}
40
41static void compute_rects(SkRect* devOutside, SkRect* devOutsideAssist, SkRect* devInside,
42 bool* isDegenerate, const SkMatrix& viewMatrix, const SkRect& rect,
43 SkScalar strokeWidth, bool miterStroke) {
44 SkRect devRect;
45 viewMatrix.mapRect(&devRect, rect);
46
47 SkVector devStrokeSize;
48 if (strokeWidth > 0) {
49 devStrokeSize.set(strokeWidth, strokeWidth);
50 viewMatrix.mapVectors(&devStrokeSize, 1);
51 devStrokeSize.setAbs(devStrokeSize);
52 } else {
53 devStrokeSize.set(SK_Scalar1, SK_Scalar1);
54 }
55
56 const SkScalar dx = devStrokeSize.fX;
57 const SkScalar dy = devStrokeSize.fY;
Mike Reed8be952a2017-02-13 20:44:33 -050058 const SkScalar rx = SkScalarHalf(dx);
59 const SkScalar ry = SkScalarHalf(dy);
bsalomon8b7a9e12016-07-06 13:06:22 -070060
61 *devOutside = devRect;
62 *devOutsideAssist = devRect;
63 *devInside = devRect;
64
65 devOutside->outset(rx, ry);
66 devInside->inset(rx, ry);
67
68 // If we have a degenerate stroking rect(ie the stroke is larger than inner rect) then we
69 // make a degenerate inside rect to avoid double hitting. We will also jam all of the points
70 // together when we render these rects.
71 SkScalar spare;
72 {
73 SkScalar w = devRect.width() - dx;
74 SkScalar h = devRect.height() - dy;
75 spare = SkTMin(w, h);
76 }
77
78 *isDegenerate = spare <= 0;
79 if (*isDegenerate) {
80 devInside->fLeft = devInside->fRight = devRect.centerX();
81 devInside->fTop = devInside->fBottom = devRect.centerY();
82 }
83
84 // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
85 // to draw the outside of the octagon. Because there are 8 vertices on the outer
86 // edge, while vertex number of inner edge is 4, the same as miter-stroke.
87 if (!miterStroke) {
88 devOutside->inset(0, ry);
89 devOutsideAssist->outset(0, ry);
90 }
91}
92
Ruiqi Maob609e6d2018-07-17 10:19:38 -040093static sk_sp<GrGeometryProcessor> create_stroke_rect_gp(const GrShaderCaps* shaderCaps,
94 bool tweakAlphaForCoverage,
joshualitt9ff64252015-08-10 09:03:51 -070095 const SkMatrix& viewMatrix,
Brian Salomon8c5bad32016-12-20 14:43:36 -050096 bool usesLocalCoords) {
joshualitt9ff64252015-08-10 09:03:51 -070097 using namespace GrDefaultGeoProcFactory;
98
joshualitt9ff64252015-08-10 09:03:51 -070099 Coverage::Type coverageType;
Brian Salomon8c5bad32016-12-20 14:43:36 -0500100 if (tweakAlphaForCoverage) {
joshualitt9ff64252015-08-10 09:03:51 -0700101 coverageType = Coverage::kSolid_Type;
102 } else {
103 coverageType = Coverage::kAttribute_Type;
104 }
Brian Salomon8c852be2017-01-04 10:44:42 -0500105 LocalCoords::Type localCoordsType =
106 usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
Ruiqi Maob609e6d2018-07-17 10:19:38 -0400107 return MakeForDeviceSpace(shaderCaps,
108 Color::kPremulGrColorAttribute_Type,
109 coverageType,
110 localCoordsType,
Brian Salomon3de0aee2017-01-29 09:34:17 -0500111 viewMatrix);
joshualitt9ff64252015-08-10 09:03:51 -0700112}
113
Brian Salomonbaaf4392017-06-15 09:59:23 -0400114namespace {
115
116class AAStrokeRectOp final : public GrMeshDrawOp {
117private:
118 using Helper = GrSimpleMeshDrawOpHelper;
119
joshualitt3566d442015-09-18 07:12:55 -0700120public:
Brian Salomon25a88092016-12-01 09:36:50 -0500121 DEFINE_OP_CLASS_ID
joshualitt3566d442015-09-18 07:12:55 -0700122
Robert Phillips7c525e62018-06-12 10:11:12 -0400123 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
124 GrPaint&& paint,
125 const SkMatrix& viewMatrix,
126 const SkRect& devOutside,
127 const SkRect& devInside) {
128 return Helper::FactoryHelper<AAStrokeRectOp>(context, std::move(paint), viewMatrix,
129 devOutside, devInside);
Brian Salomonbaaf4392017-06-15 09:59:23 -0400130 }
131
Brian Osmancf860852018-10-31 14:04:39 -0400132 AAStrokeRectOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -0400133 const SkMatrix& viewMatrix, const SkRect& devOutside, const SkRect& devInside)
Brian Salomonbaaf4392017-06-15 09:59:23 -0400134 : INHERITED(ClassID())
135 , fHelper(helperArgs, GrAAType::kCoverage)
136 , fViewMatrix(viewMatrix) {
caryclarkd6562002016-07-27 12:02:07 -0700137 SkASSERT(!devOutside.isEmpty());
138 SkASSERT(!devInside.isEmpty());
joshualitt3566d442015-09-18 07:12:55 -0700139
Brian Salomon8c5bad32016-12-20 14:43:36 -0500140 fRects.emplace_back(RectInfo{color, devOutside, devOutside, devInside, false});
bsalomon88cf17d2016-07-08 06:40:56 -0700141 this->setBounds(devOutside, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon8b7a9e12016-07-06 13:06:22 -0700142 fMiterStroke = true;
143 }
144
Robert Phillips7c525e62018-06-12 10:11:12 -0400145 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
146 GrPaint&& paint,
147 const SkMatrix& viewMatrix,
148 const SkRect& rect,
149 const SkStrokeRec& stroke) {
bsalomon8b7a9e12016-07-06 13:06:22 -0700150 bool isMiter;
151 if (!allowed_stroke(stroke, &isMiter)) {
152 return nullptr;
153 }
Robert Phillips7c525e62018-06-12 10:11:12 -0400154 return Helper::FactoryHelper<AAStrokeRectOp>(context, std::move(paint), viewMatrix, rect,
155 stroke, isMiter);
Brian Salomonbaaf4392017-06-15 09:59:23 -0400156 }
bsalomon8b7a9e12016-07-06 13:06:22 -0700157
Brian Osmancf860852018-10-31 14:04:39 -0400158 AAStrokeRectOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -0400159 const SkMatrix& viewMatrix, const SkRect& rect, const SkStrokeRec& stroke,
160 bool isMiter)
Brian Salomonbaaf4392017-06-15 09:59:23 -0400161 : INHERITED(ClassID())
162 , fHelper(helperArgs, GrAAType::kCoverage)
163 , fViewMatrix(viewMatrix) {
164 fMiterStroke = isMiter;
165 RectInfo& info = fRects.push_back();
Brian Salomon8c5bad32016-12-20 14:43:36 -0500166 compute_rects(&info.fDevOutside, &info.fDevOutsideAssist, &info.fDevInside,
167 &info.fDegenerate, viewMatrix, rect, stroke.getWidth(), isMiter);
168 info.fColor = color;
Brian Salomon510dd422017-03-16 12:15:22 -0400169 if (isMiter) {
Brian Salomonbaaf4392017-06-15 09:59:23 -0400170 this->setBounds(info.fDevOutside, HasAABloat::kYes, IsZeroArea::kNo);
Brian Salomon510dd422017-03-16 12:15:22 -0400171 } else {
172 // The outer polygon of the bevel stroke is an octagon specified by the points of a
173 // pair of overlapping rectangles where one is wide and the other is narrow.
174 SkRect bounds = info.fDevOutside;
175 bounds.joinPossiblyEmptyRect(info.fDevOutsideAssist);
Brian Salomonbaaf4392017-06-15 09:59:23 -0400176 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
Brian Salomon510dd422017-03-16 12:15:22 -0400177 }
joshualitt3566d442015-09-18 07:12:55 -0700178 }
179
180 const char* name() const override { return "AAStrokeRect"; }
181
Brian Salomon7d94bb52018-10-12 14:37:19 -0400182 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400183 fHelper.visitProxies(func);
184 }
185
Brian Osman9a390ac2018-11-12 09:47:48 -0500186#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -0500187 SkString dumpInfo() const override {
188 SkString string;
Brian Salomon8c5bad32016-12-20 14:43:36 -0500189 for (const auto& info : fRects) {
Brian Salomon6a639042016-12-14 11:08:17 -0500190 string.appendf(
191 "Color: 0x%08x, ORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
192 "AssistORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
193 "IRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], Degen: %d",
Brian Osmancf860852018-10-31 14:04:39 -0400194 info.fColor.toBytes_RGBA(), info.fDevOutside.fLeft, info.fDevOutside.fTop,
Brian Salomon8c5bad32016-12-20 14:43:36 -0500195 info.fDevOutside.fRight, info.fDevOutside.fBottom, info.fDevOutsideAssist.fLeft,
196 info.fDevOutsideAssist.fTop, info.fDevOutsideAssist.fRight,
197 info.fDevOutsideAssist.fBottom, info.fDevInside.fLeft, info.fDevInside.fTop,
198 info.fDevInside.fRight, info.fDevInside.fBottom, info.fDegenerate);
Brian Salomon7c3e7182016-12-01 09:35:30 -0500199 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -0400200 string += fHelper.dumpInfo();
201 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -0500202 return string;
203 }
Brian Osman9a390ac2018-11-12 09:47:48 -0500204#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -0500205
Brian Salomonbaaf4392017-06-15 09:59:23 -0400206 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
Brian Salomona0485d92017-06-14 19:08:01 -0400207
Brian Osman532b3f92018-07-11 10:02:07 -0400208 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
209 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
Brian Salomonbaaf4392017-06-15 09:59:23 -0400210 &fRects.back().fColor);
Brian Salomona0485d92017-06-14 19:08:01 -0400211 }
Brian Salomonbaaf4392017-06-15 09:59:23 -0400212
213private:
Brian Salomon91326c32017-08-09 16:02:19 -0400214 void onPrepareDraws(Target*) override;
joshualittaa37a962015-09-18 13:03:25 -0700215
joshualitt3566d442015-09-18 07:12:55 -0700216 static const int kMiterIndexCnt = 3 * 24;
217 static const int kMiterVertexCnt = 16;
218 static const int kNumMiterRectsInIndexBuffer = 256;
219
220 static const int kBevelIndexCnt = 48 + 36 + 24;
221 static const int kBevelVertexCnt = 24;
222 static const int kNumBevelRectsInIndexBuffer = 256;
223
Brian Salomond28a79d2017-10-16 13:01:07 -0400224 static sk_sp<const GrBuffer> GetIndexBuffer(GrResourceProvider*, bool miterStroke);
joshualitt3566d442015-09-18 07:12:55 -0700225
joshualittaa37a962015-09-18 13:03:25 -0700226 const SkMatrix& viewMatrix() const { return fViewMatrix; }
227 bool miterStroke() const { return fMiterStroke; }
joshualitt3566d442015-09-18 07:12:55 -0700228
Brian Salomon7eae3e02018-08-07 14:02:38 +0000229 CombineResult onCombineIfPossible(GrOp* t, const GrCaps&) override;
joshualitt3566d442015-09-18 07:12:55 -0700230
Brian Osmancfec9d52018-11-20 11:39:15 -0500231 void generateAAStrokeRectGeometry(GrVertexWriter& vertices,
joshualitt3566d442015-09-18 07:12:55 -0700232 GrColor color,
233 const SkRect& devOutside,
234 const SkRect& devOutsideAssist,
235 const SkRect& devInside,
236 bool miterStroke,
joshualitt11edad92015-09-22 10:32:28 -0700237 bool degenerate,
joshualitt3566d442015-09-18 07:12:55 -0700238 bool tweakAlphaForCoverage) const;
239
bsalomon8b7a9e12016-07-06 13:06:22 -0700240 // TODO support AA rotated stroke rects by copying around view matrices
Brian Salomon8c5bad32016-12-20 14:43:36 -0500241 struct RectInfo {
Brian Osmancf860852018-10-31 14:04:39 -0400242 SkPMColor4f fColor;
bsalomon8b7a9e12016-07-06 13:06:22 -0700243 SkRect fDevOutside;
244 SkRect fDevOutsideAssist;
245 SkRect fDevInside;
246 bool fDegenerate;
247 };
248
Brian Salomonbaaf4392017-06-15 09:59:23 -0400249 Helper fHelper;
Brian Salomon8c5bad32016-12-20 14:43:36 -0500250 SkSTArray<1, RectInfo, true> fRects;
joshualittaa37a962015-09-18 13:03:25 -0700251 SkMatrix fViewMatrix;
252 bool fMiterStroke;
joshualitt3566d442015-09-18 07:12:55 -0700253
Brian Salomonbaaf4392017-06-15 09:59:23 -0400254 typedef GrMeshDrawOp INHERITED;
joshualitt3566d442015-09-18 07:12:55 -0700255};
256
Brian Salomonbaaf4392017-06-15 09:59:23 -0400257} // anonymous namespace
joshualitt9ff64252015-08-10 09:03:51 -0700258
Brian Salomon91326c32017-08-09 16:02:19 -0400259void AAStrokeRectOp::onPrepareDraws(Target* target) {
Ruiqi Maob609e6d2018-07-17 10:19:38 -0400260 sk_sp<GrGeometryProcessor> gp(create_stroke_rect_gp(target->caps().shaderCaps(),
261 fHelper.compatibleWithAlphaAsCoverage(),
bungeman06ca8ec2016-06-09 08:01:03 -0700262 this->viewMatrix(),
Brian Salomonbaaf4392017-06-15 09:59:23 -0400263 fHelper.usesLocalCoords()));
joshualitt9ff64252015-08-10 09:03:51 -0700264 if (!gp) {
265 SkDebugf("Couldn't create GrGeometryProcessor\n");
266 return;
267 }
268
joshualitt9ff64252015-08-10 09:03:51 -0700269 int innerVertexNum = 4;
270 int outerVertexNum = this->miterStroke() ? 4 : 8;
271 int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
272 int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
Brian Salomon8c5bad32016-12-20 14:43:36 -0500273 int instanceCount = fRects.count();
joshualitt9ff64252015-08-10 09:03:51 -0700274
Brian Salomon7eae3e02018-08-07 14:02:38 +0000275 sk_sp<const GrBuffer> indexBuffer =
276 GetIndexBuffer(target->resourceProvider(), this->miterStroke());
Brian Osmancfec9d52018-11-20 11:39:15 -0500277 PatternHelper helper(target, GrPrimitiveType::kTriangles, gp->vertexStride(), indexBuffer.get(),
Brian Salomon7eae3e02018-08-07 14:02:38 +0000278 verticesPerInstance, indicesPerInstance, instanceCount);
Brian Osmancfec9d52018-11-20 11:39:15 -0500279 GrVertexWriter vertices{ helper.vertices() };
280 if (!vertices.fPtr || !indexBuffer) {
Brian Salomon6a639042016-12-14 11:08:17 -0500281 SkDebugf("Could not allocate vertices\n");
282 return;
283 }
joshualitt9ff64252015-08-10 09:03:51 -0700284
285 for (int i = 0; i < instanceCount; i++) {
Brian Salomon8c5bad32016-12-20 14:43:36 -0500286 const RectInfo& info = fRects[i];
joshualitt9ff64252015-08-10 09:03:51 -0700287 this->generateAAStrokeRectGeometry(vertices,
Brian Osmancf860852018-10-31 14:04:39 -0400288 info.fColor.toBytes_RGBA(), // TODO4F
Brian Salomon8c5bad32016-12-20 14:43:36 -0500289 info.fDevOutside,
290 info.fDevOutsideAssist,
291 info.fDevInside,
joshualittaa37a962015-09-18 13:03:25 -0700292 fMiterStroke,
Brian Salomon8c5bad32016-12-20 14:43:36 -0500293 info.fDegenerate,
Brian Salomonbaaf4392017-06-15 09:59:23 -0400294 fHelper.compatibleWithAlphaAsCoverage());
joshualitt9ff64252015-08-10 09:03:51 -0700295 }
Brian Salomon49348902018-06-26 09:12:38 -0400296 auto pipe = fHelper.makePipeline(target);
Brian Salomon7eae3e02018-08-07 14:02:38 +0000297 helper.recordDraw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState);
joshualitt9ff64252015-08-10 09:03:51 -0700298}
299
Brian Salomond28a79d2017-10-16 13:01:07 -0400300sk_sp<const GrBuffer> AAStrokeRectOp::GetIndexBuffer(GrResourceProvider* resourceProvider,
301 bool miterStroke) {
joshualitt9ff64252015-08-10 09:03:51 -0700302 if (miterStroke) {
Brian Salomon6a639042016-12-14 11:08:17 -0500303 // clang-format off
joshualitt9ff64252015-08-10 09:03:51 -0700304 static const uint16_t gMiterIndices[] = {
305 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
306 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
307 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
308 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
309
310 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
311 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
312 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
313 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
314
315 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
316 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
317 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
318 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
319 };
Brian Salomon6a639042016-12-14 11:08:17 -0500320 // clang-format on
joshualitt9ff64252015-08-10 09:03:51 -0700321 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
322 GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
Chris Daltonff926502017-05-03 14:36:54 -0400323 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon6a639042016-12-14 11:08:17 -0500324 gMiterIndices, kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
325 gMiterIndexBufferKey);
joshualitt9ff64252015-08-10 09:03:51 -0700326 } else {
327 /**
328 * As in miter-stroke, index = a + b, and a is the current index, b is the shift
329 * from the first index. The index layout:
330 * outer AA line: 0~3, 4~7
331 * outer edge: 8~11, 12~15
332 * inner edge: 16~19
333 * inner AA line: 20~23
334 * Following comes a bevel-stroke rect and its indices:
335 *
336 * 4 7
337 * *********************************
338 * * ______________________________ *
339 * * / 12 15 \ *
340 * * / \ *
341 * 0 * |8 16_____________________19 11 | * 3
342 * * | | | | *
343 * * | | **************** | | *
344 * * | | * 20 23 * | | *
345 * * | | * * | | *
346 * * | | * 21 22 * | | *
347 * * | | **************** | | *
348 * * | |____________________| | *
349 * 1 * |9 17 18 10| * 2
350 * * \ / *
351 * * \13 __________________________14/ *
352 * * *
353 * **********************************
354 * 5 6
355 */
Brian Salomon6a639042016-12-14 11:08:17 -0500356 // clang-format off
joshualitt9ff64252015-08-10 09:03:51 -0700357 static const uint16_t gBevelIndices[] = {
358 // Draw outer AA, from outer AA line to outer edge, shift is 0.
359 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
360 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
361 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
362 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
363 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
364 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
365 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
366 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
367
368 // Draw the stroke, from outer edge to inner edge, shift is 8.
369 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
370 1 + 8, 5 + 8, 9 + 8,
371 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
372 6 + 8, 2 + 8, 10 + 8,
373 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
374 3 + 8, 7 + 8, 11 + 8,
375 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
376 4 + 8, 0 + 8, 8 + 8,
377
378 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
379 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
380 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
381 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
382 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
383 };
Brian Salomon6a639042016-12-14 11:08:17 -0500384 // clang-format on
joshualitt9ff64252015-08-10 09:03:51 -0700385 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
386
387 GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
Chris Daltonff926502017-05-03 14:36:54 -0400388 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon6a639042016-12-14 11:08:17 -0500389 gBevelIndices, kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
390 gBevelIndexBufferKey);
joshualitt9ff64252015-08-10 09:03:51 -0700391 }
392}
393
Brian Salomon7eae3e02018-08-07 14:02:38 +0000394GrOp::CombineResult AAStrokeRectOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
Brian Salomon6a639042016-12-14 11:08:17 -0500395 AAStrokeRectOp* that = t->cast<AAStrokeRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -0700396
Brian Salomonbaaf4392017-06-15 09:59:23 -0400397 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000398 return CombineResult::kCannotCombine;
joshualitt9ff64252015-08-10 09:03:51 -0700399 }
400
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500401 // TODO combine across miterstroke changes
joshualitt9ff64252015-08-10 09:03:51 -0700402 if (this->miterStroke() != that->miterStroke()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000403 return CombineResult::kCannotCombine;
joshualitt9ff64252015-08-10 09:03:51 -0700404 }
405
406 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
Brian Salomonbaaf4392017-06-15 09:59:23 -0400407 // local coords then we won't be able to combine. TODO: Upload local coords as an attribute.
408 if (fHelper.usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000409 return CombineResult::kCannotCombine;
joshualitt9ff64252015-08-10 09:03:51 -0700410 }
411
Brian Salomon8c5bad32016-12-20 14:43:36 -0500412 fRects.push_back_n(that->fRects.count(), that->fRects.begin());
Brian Salomon7eae3e02018-08-07 14:02:38 +0000413 return CombineResult::kMerged;
joshualitt9ff64252015-08-10 09:03:51 -0700414}
415
joshualitt11edad92015-09-22 10:32:28 -0700416static void setup_scale(int* scale, SkScalar inset) {
417 if (inset < SK_ScalarHalf) {
418 *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
419 SkASSERT(*scale >= 0 && *scale <= 255);
420 } else {
421 *scale = 0xff;
422 }
423}
424
Brian Osmancfec9d52018-11-20 11:39:15 -0500425void AAStrokeRectOp::generateAAStrokeRectGeometry(GrVertexWriter& vertices,
Brian Salomon6a639042016-12-14 11:08:17 -0500426 GrColor color,
427 const SkRect& devOutside,
428 const SkRect& devOutsideAssist,
429 const SkRect& devInside,
430 bool miterStroke,
431 bool degenerate,
432 bool tweakAlphaForCoverage) const {
joshualitt9ff64252015-08-10 09:03:51 -0700433 // We create vertices for four nested rectangles. There are two ramps from 0 to full
434 // coverage, one on the exterior of the stroke and the other on the interior.
joshualitt9ff64252015-08-10 09:03:51 -0700435
joshualitt9ff64252015-08-10 09:03:51 -0700436 // TODO: this only really works if the X & Y margins are the same all around
437 // the rect (or if they are all >= 1.0).
joshualitt11edad92015-09-22 10:32:28 -0700438 SkScalar inset;
439 if (!degenerate) {
440 inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
441 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
442 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
443 if (miterStroke) {
444 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
445 } else {
Brian Salomon6a639042016-12-14 11:08:17 -0500446 inset = SK_ScalarHalf *
447 SkMinScalar(inset, devOutsideAssist.fBottom - devInside.fBottom);
joshualitt11edad92015-09-22 10:32:28 -0700448 }
449 SkASSERT(inset >= 0);
joshualitt9ff64252015-08-10 09:03:51 -0700450 } else {
joshualitt11edad92015-09-22 10:32:28 -0700451 // TODO use real devRect here
452 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
Brian Salomon6a639042016-12-14 11:08:17 -0500453 inset = SK_ScalarHalf *
454 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height()));
joshualitt9ff64252015-08-10 09:03:51 -0700455 }
joshualitt9ff64252015-08-10 09:03:51 -0700456
Brian Osmancfec9d52018-11-20 11:39:15 -0500457 auto inset_fan = [](const SkRect& r, SkScalar dx, SkScalar dy) {
458 return GrVertexWriter::TriFanFromRect(r.makeInset(dx, dy));
459 };
joshualitt9ff64252015-08-10 09:03:51 -0700460
Brian Osmancfec9d52018-11-20 11:39:15 -0500461 auto maybe_coverage = [tweakAlphaForCoverage](float coverage) {
462 return GrVertexWriter::If(!tweakAlphaForCoverage, coverage);
463 };
464
465 GrColor outerColor = tweakAlphaForCoverage ? 0 : color;
466
467 // Outermost rect
468 vertices.writeQuad(inset_fan(devOutside, -SK_ScalarHalf, -SK_ScalarHalf),
469 outerColor,
470 maybe_coverage(0.0f));
471
472 if (!miterStroke) {
473 // Second outermost
474 vertices.writeQuad(inset_fan(devOutsideAssist, -SK_ScalarHalf, -SK_ScalarHalf),
475 outerColor,
476 maybe_coverage(0.0f));
joshualitt9ff64252015-08-10 09:03:51 -0700477 }
478
479 // scale is the coverage for the the inner two rects.
480 int scale;
joshualitt11edad92015-09-22 10:32:28 -0700481 setup_scale(&scale, inset);
joshualitt9ff64252015-08-10 09:03:51 -0700482
483 float innerCoverage = GrNormalizeByteToFloat(scale);
484 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
Brian Osmancfec9d52018-11-20 11:39:15 -0500485 GrColor innerColor = tweakAlphaForCoverage ? scaledColor : color;
joshualitt9ff64252015-08-10 09:03:51 -0700486
Brian Osmancfec9d52018-11-20 11:39:15 -0500487 // Inner rect
488 vertices.writeQuad(inset_fan(devOutside, inset, inset),
489 innerColor,
490 maybe_coverage(innerCoverage));
491
492 if (!miterStroke) {
493 // Second inner
494 vertices.writeQuad(inset_fan(devOutsideAssist, inset, inset),
495 innerColor,
496 maybe_coverage(innerCoverage));
joshualitt9ff64252015-08-10 09:03:51 -0700497 }
498
joshualitt11edad92015-09-22 10:32:28 -0700499 if (!degenerate) {
Brian Osmancfec9d52018-11-20 11:39:15 -0500500 vertices.writeQuad(inset_fan(devInside, -inset, -inset),
501 innerColor,
502 maybe_coverage(innerCoverage));
joshualitt11edad92015-09-22 10:32:28 -0700503
Brian Osmancfec9d52018-11-20 11:39:15 -0500504 // The innermost rect has 0 coverage...
505 vertices.writeQuad(inset_fan(devInside, SK_ScalarHalf, SK_ScalarHalf),
506 (GrColor)0,
507 maybe_coverage(0.0f));
508 } else {
509 // When the interior rect has become degenerate we smoosh to a single point
510 SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
511
512 vertices.writeQuad(GrVertexWriter::TriFanFromRect(devInside),
513 innerColor,
514 maybe_coverage(innerCoverage));
515
516 // ... unless we are degenerate, in which case we must apply the scaled coverage
517 vertices.writeQuad(GrVertexWriter::TriFanFromRect(devInside),
518 innerColor,
519 maybe_coverage(innerCoverage));
joshualitt9ff64252015-08-10 09:03:51 -0700520 }
521}
522
Brian Salomonbaaf4392017-06-15 09:59:23 -0400523namespace GrRectOpFactory {
joshualitt3566d442015-09-18 07:12:55 -0700524
Robert Phillips7c525e62018-06-12 10:11:12 -0400525std::unique_ptr<GrDrawOp> MakeAAFillNestedRects(GrContext* context,
526 GrPaint&& paint,
Brian Salomonbaaf4392017-06-15 09:59:23 -0400527 const SkMatrix& viewMatrix,
528 const SkRect rects[2]) {
529 SkASSERT(viewMatrix.rectStaysRect());
530 SkASSERT(!rects[0].isEmpty() && !rects[1].isEmpty());
531
532 SkRect devOutside, devInside;
533 viewMatrix.mapRect(&devOutside, rects[0]);
534 viewMatrix.mapRect(&devInside, rects[1]);
535 if (devInside.isEmpty()) {
536 if (devOutside.isEmpty()) {
537 return nullptr;
538 }
Robert Phillips7c525e62018-06-12 10:11:12 -0400539 return MakeAAFill(context, std::move(paint), viewMatrix, rects[0]);
Brian Salomonbaaf4392017-06-15 09:59:23 -0400540 }
541
Robert Phillips7c525e62018-06-12 10:11:12 -0400542 return AAStrokeRectOp::Make(context, std::move(paint), viewMatrix, devOutside, devInside);
joshualittaa37a962015-09-18 13:03:25 -0700543}
544
Robert Phillips7c525e62018-06-12 10:11:12 -0400545std::unique_ptr<GrDrawOp> MakeAAStroke(GrContext* context,
546 GrPaint&& paint,
Brian Salomonbaaf4392017-06-15 09:59:23 -0400547 const SkMatrix& viewMatrix,
548 const SkRect& rect,
549 const SkStrokeRec& stroke) {
Robert Phillips7c525e62018-06-12 10:11:12 -0400550 return AAStrokeRectOp::Make(context, std::move(paint), viewMatrix, rect, stroke);
joshualitt10cae832015-09-22 12:50:33 -0700551}
Brian Salomonbaaf4392017-06-15 09:59:23 -0400552
553} // namespace GrRectOpFactory
joshualitt3566d442015-09-18 07:12:55 -0700554
joshualitt9ff64252015-08-10 09:03:51 -0700555///////////////////////////////////////////////////////////////////////////////////////////////////
556
Hal Canary6f6961e2017-01-31 13:50:44 -0500557#if GR_TEST_UTILS
joshualitt9ff64252015-08-10 09:03:51 -0700558
Brian Salomon5ec9def2016-12-20 15:34:05 -0500559#include "GrDrawOpTest.h"
joshualitt9ff64252015-08-10 09:03:51 -0700560
Brian Salomonbaaf4392017-06-15 09:59:23 -0400561GR_DRAW_OP_TEST_DEFINE(AAStrokeRectOp) {
joshualitt9ff64252015-08-10 09:03:51 -0700562 bool miterStroke = random->nextBool();
563
bsalomon40ef4852016-05-02 13:22:13 -0700564 // Create either a empty rect or a non-empty rect.
Brian Salomon6a639042016-12-14 11:08:17 -0500565 SkRect rect =
566 random->nextBool() ? SkRect::MakeXYWH(10, 10, 50, 40) : SkRect::MakeXYWH(6, 7, 0, 0);
bsalomon40ef4852016-05-02 13:22:13 -0700567 SkScalar minDim = SkMinScalar(rect.width(), rect.height());
568 SkScalar strokeWidth = random->nextUScalar1() * minDim;
joshualitt9ff64252015-08-10 09:03:51 -0700569
bsalomon40ef4852016-05-02 13:22:13 -0700570 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
571 rec.setStrokeStyle(strokeWidth);
572 rec.setStrokeParams(SkPaint::kButt_Cap,
Brian Salomon6a639042016-12-14 11:08:17 -0500573 miterStroke ? SkPaint::kMiter_Join : SkPaint::kBevel_Join, 1.f);
bsalomon40ef4852016-05-02 13:22:13 -0700574 SkMatrix matrix = GrTest::TestMatrixRectStaysRect(random);
Robert Phillips7c525e62018-06-12 10:11:12 -0400575 return GrRectOpFactory::MakeAAStroke(context, std::move(paint), matrix, rect, rec);
joshualitt9ff64252015-08-10 09:03:51 -0700576}
577
578#endif