blob: 3c603bb228485f9776cc24f239b875f9a3d4cd0e [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
Brian Salomon6a639042016-12-14 11:08:17 -05008#include "GrAAStrokeRectOp.h"
joshualitt9ff64252015-08-10 09:03:51 -07009
10#include "GrDefaultGeoProcFactory.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050011#include "GrOpFlushState.h"
joshualitt9ff64252015-08-10 09:03:51 -070012#include "GrResourceKey.h"
13#include "GrResourceProvider.h"
14
15GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
16GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
17
Brian Salomon6a639042016-12-14 11:08:17 -050018static void set_inset_fan(SkPoint* pts, size_t stride, const SkRect& r, SkScalar dx, SkScalar dy) {
19 pts->setRectFan(r.fLeft + dx, r.fTop + dy, r.fRight - dx, r.fBottom - dy, stride);
joshualitt9ff64252015-08-10 09:03:51 -070020}
21
bsalomon8b7a9e12016-07-06 13:06:22 -070022// We support all hairlines, bevels, and miters, but not round joins. Also, check whether the miter
23// limit makes a miter join effectively beveled.
24inline static bool allowed_stroke(const SkStrokeRec& stroke, bool* isMiter) {
25 SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style ||
26 stroke.getStyle() == SkStrokeRec::kHairline_Style);
27 // For hairlines, make bevel and round joins appear the same as mitered ones.
28 if (!stroke.getWidth()) {
29 *isMiter = true;
30 return true;
31 }
32 if (stroke.getJoin() == SkPaint::kBevel_Join) {
33 *isMiter = false;
34 return true;
35 }
36 if (stroke.getJoin() == SkPaint::kMiter_Join) {
37 *isMiter = stroke.getMiter() >= SK_ScalarSqrt2;
38 return true;
39 }
40 return false;
41}
42
43static void compute_rects(SkRect* devOutside, SkRect* devOutsideAssist, SkRect* devInside,
44 bool* isDegenerate, const SkMatrix& viewMatrix, const SkRect& rect,
45 SkScalar strokeWidth, bool miterStroke) {
46 SkRect devRect;
47 viewMatrix.mapRect(&devRect, rect);
48
49 SkVector devStrokeSize;
50 if (strokeWidth > 0) {
51 devStrokeSize.set(strokeWidth, strokeWidth);
52 viewMatrix.mapVectors(&devStrokeSize, 1);
53 devStrokeSize.setAbs(devStrokeSize);
54 } else {
55 devStrokeSize.set(SK_Scalar1, SK_Scalar1);
56 }
57
58 const SkScalar dx = devStrokeSize.fX;
59 const SkScalar dy = devStrokeSize.fY;
60 const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf);
61 const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf);
62
63 *devOutside = devRect;
64 *devOutsideAssist = devRect;
65 *devInside = devRect;
66
67 devOutside->outset(rx, ry);
68 devInside->inset(rx, ry);
69
70 // If we have a degenerate stroking rect(ie the stroke is larger than inner rect) then we
71 // make a degenerate inside rect to avoid double hitting. We will also jam all of the points
72 // together when we render these rects.
73 SkScalar spare;
74 {
75 SkScalar w = devRect.width() - dx;
76 SkScalar h = devRect.height() - dy;
77 spare = SkTMin(w, h);
78 }
79
80 *isDegenerate = spare <= 0;
81 if (*isDegenerate) {
82 devInside->fLeft = devInside->fRight = devRect.centerX();
83 devInside->fTop = devInside->fBottom = devRect.centerY();
84 }
85
86 // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
87 // to draw the outside of the octagon. Because there are 8 vertices on the outer
88 // edge, while vertex number of inner edge is 4, the same as miter-stroke.
89 if (!miterStroke) {
90 devOutside->inset(0, ry);
91 devOutsideAssist->outset(0, ry);
92 }
93}
94
bungeman06ca8ec2016-06-09 08:01:03 -070095static sk_sp<GrGeometryProcessor> create_stroke_rect_gp(bool tweakAlphaForCoverage,
joshualitt9ff64252015-08-10 09:03:51 -070096 const SkMatrix& viewMatrix,
Brian Salomon8c5bad32016-12-20 14:43:36 -050097 bool usesLocalCoords) {
joshualitt9ff64252015-08-10 09:03:51 -070098 using namespace GrDefaultGeoProcFactory;
99
joshualitt9ff64252015-08-10 09:03:51 -0700100 Coverage::Type coverageType;
Brian Salomon8c5bad32016-12-20 14:43:36 -0500101 if (tweakAlphaForCoverage) {
joshualitt9ff64252015-08-10 09:03:51 -0700102 coverageType = Coverage::kSolid_Type;
103 } else {
104 coverageType = Coverage::kAttribute_Type;
105 }
Brian Salomon8c852be2017-01-04 10:44:42 -0500106 LocalCoords::Type localCoordsType =
107 usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
108 return MakeForDeviceSpace(Color::kAttribute_Type, coverageType, localCoordsType, viewMatrix);
joshualitt9ff64252015-08-10 09:03:51 -0700109}
110
Brian Salomon6a639042016-12-14 11:08:17 -0500111class AAStrokeRectOp final : public GrMeshDrawOp {
joshualitt3566d442015-09-18 07:12:55 -0700112public:
Brian Salomon25a88092016-12-01 09:36:50 -0500113 DEFINE_OP_CLASS_ID
joshualitt3566d442015-09-18 07:12:55 -0700114
Brian Salomon6a639042016-12-14 11:08:17 -0500115 AAStrokeRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& devOutside,
116 const SkRect& devInside)
117 : INHERITED(ClassID()), fViewMatrix(viewMatrix) {
caryclarkd6562002016-07-27 12:02:07 -0700118 SkASSERT(!devOutside.isEmpty());
119 SkASSERT(!devInside.isEmpty());
joshualitt3566d442015-09-18 07:12:55 -0700120
Brian Salomon8c5bad32016-12-20 14:43:36 -0500121 fRects.emplace_back(RectInfo{color, devOutside, devOutside, devInside, false});
bsalomon88cf17d2016-07-08 06:40:56 -0700122 this->setBounds(devOutside, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon8b7a9e12016-07-06 13:06:22 -0700123 fMiterStroke = true;
124 }
125
Brian Salomonf8334782017-01-03 09:42:58 -0500126 static std::unique_ptr<GrDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
127 const SkRect& rect, const SkStrokeRec& stroke) {
bsalomon8b7a9e12016-07-06 13:06:22 -0700128 bool isMiter;
129 if (!allowed_stroke(stroke, &isMiter)) {
130 return nullptr;
131 }
132
Brian Salomon6a639042016-12-14 11:08:17 -0500133 AAStrokeRectOp* op = new AAStrokeRectOp();
134 op->fMiterStroke = isMiter;
Brian Salomon8c5bad32016-12-20 14:43:36 -0500135 RectInfo& info = op->fRects.push_back();
136 compute_rects(&info.fDevOutside, &info.fDevOutsideAssist, &info.fDevInside,
137 &info.fDegenerate, viewMatrix, rect, stroke.getWidth(), isMiter);
138 info.fColor = color;
139 op->setBounds(info.fDevOutside, HasAABloat::kYes, IsZeroArea::kNo);
Brian Salomon6a639042016-12-14 11:08:17 -0500140 op->fViewMatrix = viewMatrix;
Brian Salomonf8334782017-01-03 09:42:58 -0500141 return std::unique_ptr<GrDrawOp>(op);
joshualitt3566d442015-09-18 07:12:55 -0700142 }
143
144 const char* name() const override { return "AAStrokeRect"; }
145
Brian Salomon7c3e7182016-12-01 09:35:30 -0500146 SkString dumpInfo() const override {
147 SkString string;
Brian Salomon8c5bad32016-12-20 14:43:36 -0500148 for (const auto& info : fRects) {
Brian Salomon6a639042016-12-14 11:08:17 -0500149 string.appendf(
150 "Color: 0x%08x, ORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
151 "AssistORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
152 "IRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], Degen: %d",
Brian Salomon8c5bad32016-12-20 14:43:36 -0500153 info.fColor, info.fDevOutside.fLeft, info.fDevOutside.fTop,
154 info.fDevOutside.fRight, info.fDevOutside.fBottom, info.fDevOutsideAssist.fLeft,
155 info.fDevOutsideAssist.fTop, info.fDevOutsideAssist.fRight,
156 info.fDevOutsideAssist.fBottom, info.fDevInside.fLeft, info.fDevInside.fTop,
157 info.fDevInside.fRight, info.fDevInside.fBottom, info.fDegenerate);
Brian Salomon7c3e7182016-12-01 09:35:30 -0500158 }
159 string.append(DumpPipelineInfo(*this->pipeline()));
160 string.append(INHERITED::dumpInfo());
161 return string;
162 }
163
joshualittaa37a962015-09-18 13:03:25 -0700164private:
Brian Salomon6a639042016-12-14 11:08:17 -0500165 AAStrokeRectOp() : INHERITED(ClassID()) {}
joshualittaa37a962015-09-18 13:03:25 -0700166
Brian Salomon92aee3d2016-12-21 09:20:25 -0500167 void getPipelineAnalysisInput(GrPipelineAnalysisDrawOpInput* input) const override {
168 input->pipelineColorInput()->setKnownFourComponents(fRects[0].fColor);
169 input->pipelineCoverageInput()->setUnknownSingleComponent();
170 }
171 void applyPipelineOptimizations(const GrPipelineOptimizations&) override;
joshualitt144c3c82015-11-30 12:30:13 -0800172 void onPrepareDraws(Target*) const override;
joshualittaa37a962015-09-18 13:03:25 -0700173
joshualitt3566d442015-09-18 07:12:55 -0700174 static const int kMiterIndexCnt = 3 * 24;
175 static const int kMiterVertexCnt = 16;
176 static const int kNumMiterRectsInIndexBuffer = 256;
177
178 static const int kBevelIndexCnt = 48 + 36 + 24;
179 static const int kBevelVertexCnt = 24;
180 static const int kNumBevelRectsInIndexBuffer = 256;
181
cdalton397536c2016-03-25 12:15:03 -0700182 static const GrBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider, bool miterStroke);
joshualitt3566d442015-09-18 07:12:55 -0700183
Brian Salomon8c5bad32016-12-20 14:43:36 -0500184 bool usesLocalCoords() const { return fUsesLocalCoords; }
185 bool canTweakAlphaForCoverage() const { return fCanTweakAlphaForCoverage; }
joshualittaa37a962015-09-18 13:03:25 -0700186 const SkMatrix& viewMatrix() const { return fViewMatrix; }
187 bool miterStroke() const { return fMiterStroke; }
joshualitt3566d442015-09-18 07:12:55 -0700188
Brian Salomon25a88092016-12-01 09:36:50 -0500189 bool onCombineIfPossible(GrOp* t, const GrCaps&) override;
joshualitt3566d442015-09-18 07:12:55 -0700190
191 void generateAAStrokeRectGeometry(void* vertices,
192 size_t offset,
193 size_t vertexStride,
194 int outerVertexNum,
195 int innerVertexNum,
196 GrColor color,
197 const SkRect& devOutside,
198 const SkRect& devOutsideAssist,
199 const SkRect& devInside,
200 bool miterStroke,
joshualitt11edad92015-09-22 10:32:28 -0700201 bool degenerate,
joshualitt3566d442015-09-18 07:12:55 -0700202 bool tweakAlphaForCoverage) const;
203
bsalomon8b7a9e12016-07-06 13:06:22 -0700204 // TODO support AA rotated stroke rects by copying around view matrices
Brian Salomon8c5bad32016-12-20 14:43:36 -0500205 struct RectInfo {
bsalomon8b7a9e12016-07-06 13:06:22 -0700206 GrColor fColor;
207 SkRect fDevOutside;
208 SkRect fDevOutsideAssist;
209 SkRect fDevInside;
210 bool fDegenerate;
211 };
212
Brian Salomon8c5bad32016-12-20 14:43:36 -0500213 SkSTArray<1, RectInfo, true> fRects;
214 bool fUsesLocalCoords;
215 bool fCanTweakAlphaForCoverage;
joshualittaa37a962015-09-18 13:03:25 -0700216 SkMatrix fViewMatrix;
217 bool fMiterStroke;
joshualitt3566d442015-09-18 07:12:55 -0700218
Brian Salomondad29232016-12-01 16:40:24 -0500219 typedef GrMeshDrawOp INHERITED;
joshualitt3566d442015-09-18 07:12:55 -0700220};
221
Brian Salomon92aee3d2016-12-21 09:20:25 -0500222void AAStrokeRectOp::applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) {
223 if (!optimizations.readsColor()) {
Brian Salomon8c5bad32016-12-20 14:43:36 -0500224 fRects[0].fColor = GrColor_ILLEGAL;
joshualitt9ff64252015-08-10 09:03:51 -0700225 }
Brian Salomon92aee3d2016-12-21 09:20:25 -0500226 optimizations.getOverrideColorIfSet(&fRects[0].fColor);
joshualitt9ff64252015-08-10 09:03:51 -0700227
Brian Salomon92aee3d2016-12-21 09:20:25 -0500228 fUsesLocalCoords = optimizations.readsLocalCoords();
229 fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
230 fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
joshualitt9ff64252015-08-10 09:03:51 -0700231}
232
Brian Salomon6a639042016-12-14 11:08:17 -0500233void AAStrokeRectOp::onPrepareDraws(Target* target) const {
joshualitt9ff64252015-08-10 09:03:51 -0700234 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
235
bungeman06ca8ec2016-06-09 08:01:03 -0700236 sk_sp<GrGeometryProcessor> gp(create_stroke_rect_gp(canTweakAlphaForCoverage,
237 this->viewMatrix(),
Brian Salomon8c5bad32016-12-20 14:43:36 -0500238 this->usesLocalCoords()));
joshualitt9ff64252015-08-10 09:03:51 -0700239 if (!gp) {
240 SkDebugf("Couldn't create GrGeometryProcessor\n");
241 return;
242 }
243
joshualitt9ff64252015-08-10 09:03:51 -0700244 size_t vertexStride = gp->getVertexStride();
245
Brian Salomon6a639042016-12-14 11:08:17 -0500246 SkASSERT(canTweakAlphaForCoverage
247 ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr)
248 : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
joshualitt9ff64252015-08-10 09:03:51 -0700249 int innerVertexNum = 4;
250 int outerVertexNum = this->miterStroke() ? 4 : 8;
251 int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
252 int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
Brian Salomon8c5bad32016-12-20 14:43:36 -0500253 int instanceCount = fRects.count();
joshualitt9ff64252015-08-10 09:03:51 -0700254
Hal Canary144caf52016-11-07 17:57:18 -0500255 const sk_sp<const GrBuffer> indexBuffer(
Brian Salomon6a639042016-12-14 11:08:17 -0500256 GetIndexBuffer(target->resourceProvider(), this->miterStroke()));
joshualitt9ff64252015-08-10 09:03:51 -0700257 InstancedHelper helper;
Brian Salomon6a639042016-12-14 11:08:17 -0500258 void* vertices =
259 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
260 verticesPerInstance, indicesPerInstance, instanceCount);
joshualitt9ff64252015-08-10 09:03:51 -0700261 if (!vertices || !indexBuffer) {
Brian Salomon6a639042016-12-14 11:08:17 -0500262 SkDebugf("Could not allocate vertices\n");
263 return;
264 }
joshualitt9ff64252015-08-10 09:03:51 -0700265
266 for (int i = 0; i < instanceCount; i++) {
Brian Salomon8c5bad32016-12-20 14:43:36 -0500267 const RectInfo& info = fRects[i];
joshualitt9ff64252015-08-10 09:03:51 -0700268 this->generateAAStrokeRectGeometry(vertices,
269 i * verticesPerInstance * vertexStride,
270 vertexStride,
271 outerVertexNum,
272 innerVertexNum,
Brian Salomon8c5bad32016-12-20 14:43:36 -0500273 info.fColor,
274 info.fDevOutside,
275 info.fDevOutsideAssist,
276 info.fDevInside,
joshualittaa37a962015-09-18 13:03:25 -0700277 fMiterStroke,
Brian Salomon8c5bad32016-12-20 14:43:36 -0500278 info.fDegenerate,
joshualitt9ff64252015-08-10 09:03:51 -0700279 canTweakAlphaForCoverage);
280 }
bungeman06ca8ec2016-06-09 08:01:03 -0700281 helper.recordDraw(target, gp.get());
joshualitt9ff64252015-08-10 09:03:51 -0700282}
283
Brian Salomon6a639042016-12-14 11:08:17 -0500284const GrBuffer* AAStrokeRectOp::GetIndexBuffer(GrResourceProvider* resourceProvider,
285 bool miterStroke) {
joshualitt9ff64252015-08-10 09:03:51 -0700286 if (miterStroke) {
Brian Salomon6a639042016-12-14 11:08:17 -0500287 // clang-format off
joshualitt9ff64252015-08-10 09:03:51 -0700288 static const uint16_t gMiterIndices[] = {
289 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
290 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
291 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
292 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
293
294 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
295 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
296 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
297 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
298
299 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
300 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
301 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
302 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
303 };
Brian Salomon6a639042016-12-14 11:08:17 -0500304 // clang-format on
joshualitt9ff64252015-08-10 09:03:51 -0700305 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
306 GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
Brian Salomon6a639042016-12-14 11:08:17 -0500307 return resourceProvider->findOrCreateInstancedIndexBuffer(
308 gMiterIndices, kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
309 gMiterIndexBufferKey);
joshualitt9ff64252015-08-10 09:03:51 -0700310 } else {
311 /**
312 * As in miter-stroke, index = a + b, and a is the current index, b is the shift
313 * from the first index. The index layout:
314 * outer AA line: 0~3, 4~7
315 * outer edge: 8~11, 12~15
316 * inner edge: 16~19
317 * inner AA line: 20~23
318 * Following comes a bevel-stroke rect and its indices:
319 *
320 * 4 7
321 * *********************************
322 * * ______________________________ *
323 * * / 12 15 \ *
324 * * / \ *
325 * 0 * |8 16_____________________19 11 | * 3
326 * * | | | | *
327 * * | | **************** | | *
328 * * | | * 20 23 * | | *
329 * * | | * * | | *
330 * * | | * 21 22 * | | *
331 * * | | **************** | | *
332 * * | |____________________| | *
333 * 1 * |9 17 18 10| * 2
334 * * \ / *
335 * * \13 __________________________14/ *
336 * * *
337 * **********************************
338 * 5 6
339 */
Brian Salomon6a639042016-12-14 11:08:17 -0500340 // clang-format off
joshualitt9ff64252015-08-10 09:03:51 -0700341 static const uint16_t gBevelIndices[] = {
342 // Draw outer AA, from outer AA line to outer edge, shift is 0.
343 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
344 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
345 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
346 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
347 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
348 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
349 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
350 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
351
352 // Draw the stroke, from outer edge to inner edge, shift is 8.
353 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
354 1 + 8, 5 + 8, 9 + 8,
355 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
356 6 + 8, 2 + 8, 10 + 8,
357 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
358 3 + 8, 7 + 8, 11 + 8,
359 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
360 4 + 8, 0 + 8, 8 + 8,
361
362 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
363 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
364 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
365 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
366 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
367 };
Brian Salomon6a639042016-12-14 11:08:17 -0500368 // clang-format on
joshualitt9ff64252015-08-10 09:03:51 -0700369 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
370
371 GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
Brian Salomon6a639042016-12-14 11:08:17 -0500372 return resourceProvider->findOrCreateInstancedIndexBuffer(
373 gBevelIndices, kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
374 gBevelIndexBufferKey);
joshualitt9ff64252015-08-10 09:03:51 -0700375 }
376}
377
Brian Salomon6a639042016-12-14 11:08:17 -0500378bool AAStrokeRectOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
379 AAStrokeRectOp* that = t->cast<AAStrokeRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -0700380
381 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
382 that->bounds(), caps)) {
joshualitt9ff64252015-08-10 09:03:51 -0700383 return false;
384 }
385
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500386 // TODO combine across miterstroke changes
joshualitt9ff64252015-08-10 09:03:51 -0700387 if (this->miterStroke() != that->miterStroke()) {
388 return false;
389 }
390
391 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500392 // local coords then we won't be able to combine. We could actually upload the viewmatrix
joshualitt9ff64252015-08-10 09:03:51 -0700393 // using vertex attributes in these cases, but haven't investigated that
394 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
395 return false;
396 }
397
Brian Salomon6a639042016-12-14 11:08:17 -0500398 // In the event of two ops, one who can tweak, one who cannot, we just fall back to not
399 // tweaking.
joshualitt9ff64252015-08-10 09:03:51 -0700400 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
Brian Salomon8c5bad32016-12-20 14:43:36 -0500401 fCanTweakAlphaForCoverage = false;
joshualitt9ff64252015-08-10 09:03:51 -0700402 }
403
Brian Salomon8c5bad32016-12-20 14:43:36 -0500404 fRects.push_back_n(that->fRects.count(), that->fRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700405 this->joinBounds(*that);
joshualitt9ff64252015-08-10 09:03:51 -0700406 return true;
407}
408
joshualitt11edad92015-09-22 10:32:28 -0700409static void setup_scale(int* scale, SkScalar inset) {
410 if (inset < SK_ScalarHalf) {
411 *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
412 SkASSERT(*scale >= 0 && *scale <= 255);
413 } else {
414 *scale = 0xff;
415 }
416}
417
Brian Salomon6a639042016-12-14 11:08:17 -0500418void AAStrokeRectOp::generateAAStrokeRectGeometry(void* vertices,
419 size_t offset,
420 size_t vertexStride,
421 int outerVertexNum,
422 int innerVertexNum,
423 GrColor color,
424 const SkRect& devOutside,
425 const SkRect& devOutsideAssist,
426 const SkRect& devInside,
427 bool miterStroke,
428 bool degenerate,
429 bool tweakAlphaForCoverage) const {
joshualitt9ff64252015-08-10 09:03:51 -0700430 intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
431
432 // We create vertices for four nested rectangles. There are two ramps from 0 to full
433 // coverage, one on the exterior of the stroke and the other on the interior.
434 // The following pointers refer to the four rects, from outermost to innermost.
435 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
436 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
437 SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
Brian Salomon6a639042016-12-14 11:08:17 -0500438 SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(
439 verts + (2 * outerVertexNum + innerVertexNum) * vertexStride);
joshualitt9ff64252015-08-10 09:03:51 -0700440
441#ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
442 // TODO: this only really works if the X & Y margins are the same all around
443 // the rect (or if they are all >= 1.0).
joshualitt11edad92015-09-22 10:32:28 -0700444 SkScalar inset;
445 if (!degenerate) {
446 inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
447 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
448 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
449 if (miterStroke) {
450 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
451 } else {
Brian Salomon6a639042016-12-14 11:08:17 -0500452 inset = SK_ScalarHalf *
453 SkMinScalar(inset, devOutsideAssist.fBottom - devInside.fBottom);
joshualitt11edad92015-09-22 10:32:28 -0700454 }
455 SkASSERT(inset >= 0);
joshualitt9ff64252015-08-10 09:03:51 -0700456 } else {
joshualitt11edad92015-09-22 10:32:28 -0700457 // TODO use real devRect here
458 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
Brian Salomon6a639042016-12-14 11:08:17 -0500459 inset = SK_ScalarHalf *
460 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height()));
joshualitt9ff64252015-08-10 09:03:51 -0700461 }
joshualitt9ff64252015-08-10 09:03:51 -0700462#else
joshualitt11edad92015-09-22 10:32:28 -0700463 SkScalar inset;
464 if (!degenerate) {
465 inset = SK_ScalarHalf;
466 } else {
467 // TODO use real devRect here
468 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
Brian Salomon6a639042016-12-14 11:08:17 -0500469 inset = SK_ScalarHalf *
470 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height()));
joshualitt11edad92015-09-22 10:32:28 -0700471 }
joshualitt9ff64252015-08-10 09:03:51 -0700472#endif
473
474 if (miterStroke) {
475 // outermost
476 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
477 // inner two
Brian Salomon6a639042016-12-14 11:08:17 -0500478 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
joshualitt11edad92015-09-22 10:32:28 -0700479 if (!degenerate) {
Brian Salomon6a639042016-12-14 11:08:17 -0500480 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
joshualitt11edad92015-09-22 10:32:28 -0700481 // innermost
Brian Salomon6a639042016-12-14 11:08:17 -0500482 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
joshualitt11edad92015-09-22 10:32:28 -0700483 } else {
484 // When the interior rect has become degenerate we smoosh to a single point
Brian Salomon6a639042016-12-14 11:08:17 -0500485 SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
486 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
487 devInside.fBottom, vertexStride);
488 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
489 devInside.fBottom, vertexStride);
joshualitt11edad92015-09-22 10:32:28 -0700490 }
joshualitt9ff64252015-08-10 09:03:51 -0700491 } else {
492 SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
Brian Salomon6a639042016-12-14 11:08:17 -0500493 SkPoint* fan1AssistPos =
494 reinterpret_cast<SkPoint*>(verts + (outerVertexNum + 4) * vertexStride);
joshualitt9ff64252015-08-10 09:03:51 -0700495 // outermost
496 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
497 set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
498 -SK_ScalarHalf);
499 // outer one of the inner two
Brian Salomon6a639042016-12-14 11:08:17 -0500500 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
501 set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset);
joshualitt11edad92015-09-22 10:32:28 -0700502 if (!degenerate) {
503 // inner one of the inner two
Brian Salomon6a639042016-12-14 11:08:17 -0500504 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
joshualitt11edad92015-09-22 10:32:28 -0700505 // innermost
Brian Salomon6a639042016-12-14 11:08:17 -0500506 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
joshualitt11edad92015-09-22 10:32:28 -0700507 } else {
508 // When the interior rect has become degenerate we smoosh to a single point
Brian Salomon6a639042016-12-14 11:08:17 -0500509 SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
510 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
511 devInside.fBottom, vertexStride);
512 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
513 devInside.fBottom, vertexStride);
joshualitt11edad92015-09-22 10:32:28 -0700514 }
joshualitt9ff64252015-08-10 09:03:51 -0700515 }
516
517 // Make verts point to vertex color and then set all the color and coverage vertex attrs
518 // values. The outermost rect has 0 coverage
519 verts += sizeof(SkPoint);
520 for (int i = 0; i < outerVertexNum; ++i) {
521 if (tweakAlphaForCoverage) {
522 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
523 } else {
524 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
525 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
526 }
527 }
528
529 // scale is the coverage for the the inner two rects.
530 int scale;
joshualitt11edad92015-09-22 10:32:28 -0700531 setup_scale(&scale, inset);
joshualitt9ff64252015-08-10 09:03:51 -0700532
533 float innerCoverage = GrNormalizeByteToFloat(scale);
534 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
535
536 verts += outerVertexNum * vertexStride;
537 for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
538 if (tweakAlphaForCoverage) {
539 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
540 } else {
541 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
joshualitt11edad92015-09-22 10:32:28 -0700542 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
joshualitt9ff64252015-08-10 09:03:51 -0700543 }
544 }
545
joshualitt11edad92015-09-22 10:32:28 -0700546 // The innermost rect has 0 coverage, unless we are degenerate, in which case we must apply the
547 // scaled coverage
joshualitt9ff64252015-08-10 09:03:51 -0700548 verts += (outerVertexNum + innerVertexNum) * vertexStride;
joshualitt11edad92015-09-22 10:32:28 -0700549 if (!degenerate) {
550 innerCoverage = 0;
551 scaledColor = 0;
552 }
553
joshualitt9ff64252015-08-10 09:03:51 -0700554 for (int i = 0; i < innerVertexNum; ++i) {
555 if (tweakAlphaForCoverage) {
joshualitt11edad92015-09-22 10:32:28 -0700556 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
joshualitt9ff64252015-08-10 09:03:51 -0700557 } else {
558 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
joshualitt11edad92015-09-22 10:32:28 -0700559 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
joshualitt9ff64252015-08-10 09:03:51 -0700560 }
561 }
562}
563
Brian Salomon6a639042016-12-14 11:08:17 -0500564namespace GrAAStrokeRectOp {
joshualitt3566d442015-09-18 07:12:55 -0700565
Brian Salomonf8334782017-01-03 09:42:58 -0500566std::unique_ptr<GrDrawOp> MakeFillBetweenRects(GrColor color,
567 const SkMatrix& viewMatrix,
568 const SkRect& devOutside,
569 const SkRect& devInside) {
570 return std::unique_ptr<GrDrawOp>(new AAStrokeRectOp(color, viewMatrix, devOutside, devInside));
joshualittaa37a962015-09-18 13:03:25 -0700571}
572
Brian Salomonf8334782017-01-03 09:42:58 -0500573std::unique_ptr<GrDrawOp> Make(GrColor color,
574 const SkMatrix& viewMatrix,
575 const SkRect& rect,
576 const SkStrokeRec& stroke) {
Brian Salomon6a639042016-12-14 11:08:17 -0500577 return AAStrokeRectOp::Make(color, viewMatrix, rect, stroke);
joshualitt10cae832015-09-22 12:50:33 -0700578}
bsalomon8b7a9e12016-07-06 13:06:22 -0700579}
joshualitt3566d442015-09-18 07:12:55 -0700580
joshualitt9ff64252015-08-10 09:03:51 -0700581///////////////////////////////////////////////////////////////////////////////////////////////////
582
583#ifdef GR_TEST_UTILS
584
Brian Salomon5ec9def2016-12-20 15:34:05 -0500585#include "GrDrawOpTest.h"
joshualitt9ff64252015-08-10 09:03:51 -0700586
Brian Salomon5ec9def2016-12-20 15:34:05 -0500587DRAW_OP_TEST_DEFINE(AAStrokeRectOp) {
joshualitt9ff64252015-08-10 09:03:51 -0700588 bool miterStroke = random->nextBool();
589
bsalomon40ef4852016-05-02 13:22:13 -0700590 // Create either a empty rect or a non-empty rect.
Brian Salomon6a639042016-12-14 11:08:17 -0500591 SkRect rect =
592 random->nextBool() ? SkRect::MakeXYWH(10, 10, 50, 40) : SkRect::MakeXYWH(6, 7, 0, 0);
bsalomon40ef4852016-05-02 13:22:13 -0700593 SkScalar minDim = SkMinScalar(rect.width(), rect.height());
594 SkScalar strokeWidth = random->nextUScalar1() * minDim;
joshualitt9ff64252015-08-10 09:03:51 -0700595
joshualitt3566d442015-09-18 07:12:55 -0700596 GrColor color = GrRandomColor(random);
joshualitt9ff64252015-08-10 09:03:51 -0700597
bsalomon40ef4852016-05-02 13:22:13 -0700598 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
599 rec.setStrokeStyle(strokeWidth);
600 rec.setStrokeParams(SkPaint::kButt_Cap,
Brian Salomon6a639042016-12-14 11:08:17 -0500601 miterStroke ? SkPaint::kMiter_Join : SkPaint::kBevel_Join, 1.f);
bsalomon40ef4852016-05-02 13:22:13 -0700602 SkMatrix matrix = GrTest::TestMatrixRectStaysRect(random);
Brian Salomon5ec9def2016-12-20 15:34:05 -0500603 return GrAAStrokeRectOp::Make(color, matrix, rect, rec);
joshualitt9ff64252015-08-10 09:03:51 -0700604}
605
606#endif