blob: 1c700599dba0574a510d8c0428344e66f8bdb07b [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
100 Color color(Color::kAttribute_Type);
101 Coverage::Type coverageType;
Brian Salomon8c5bad32016-12-20 14:43:36 -0500102 if (tweakAlphaForCoverage) {
joshualitt9ff64252015-08-10 09:03:51 -0700103 coverageType = Coverage::kSolid_Type;
104 } else {
105 coverageType = Coverage::kAttribute_Type;
106 }
107 Coverage coverage(coverageType);
Brian Salomon6a639042016-12-14 11:08:17 -0500108 LocalCoords localCoords(usesLocalCoords ? LocalCoords::kUsePosition_Type
109 : LocalCoords::kUnused_Type);
bungeman06ca8ec2016-06-09 08:01:03 -0700110 return MakeForDeviceSpace(color, coverage, localCoords, viewMatrix);
joshualitt9ff64252015-08-10 09:03:51 -0700111}
112
Brian Salomon6a639042016-12-14 11:08:17 -0500113class AAStrokeRectOp final : public GrMeshDrawOp {
joshualitt3566d442015-09-18 07:12:55 -0700114public:
Brian Salomon25a88092016-12-01 09:36:50 -0500115 DEFINE_OP_CLASS_ID
joshualitt3566d442015-09-18 07:12:55 -0700116
Brian Salomon6a639042016-12-14 11:08:17 -0500117 AAStrokeRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& devOutside,
118 const SkRect& devInside)
119 : INHERITED(ClassID()), fViewMatrix(viewMatrix) {
caryclarkd6562002016-07-27 12:02:07 -0700120 SkASSERT(!devOutside.isEmpty());
121 SkASSERT(!devInside.isEmpty());
joshualitt3566d442015-09-18 07:12:55 -0700122
Brian Salomon8c5bad32016-12-20 14:43:36 -0500123 fRects.emplace_back(RectInfo{color, devOutside, devOutside, devInside, false});
bsalomon88cf17d2016-07-08 06:40:56 -0700124 this->setBounds(devOutside, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon8b7a9e12016-07-06 13:06:22 -0700125 fMiterStroke = true;
126 }
127
Brian Salomon6a639042016-12-14 11:08:17 -0500128 static sk_sp<GrDrawOp> Make(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
129 const SkStrokeRec& stroke) {
bsalomon8b7a9e12016-07-06 13:06:22 -0700130 bool isMiter;
131 if (!allowed_stroke(stroke, &isMiter)) {
132 return nullptr;
133 }
134
Brian Salomon6a639042016-12-14 11:08:17 -0500135 AAStrokeRectOp* op = new AAStrokeRectOp();
136 op->fMiterStroke = isMiter;
Brian Salomon8c5bad32016-12-20 14:43:36 -0500137 RectInfo& info = op->fRects.push_back();
138 compute_rects(&info.fDevOutside, &info.fDevOutsideAssist, &info.fDevInside,
139 &info.fDegenerate, viewMatrix, rect, stroke.getWidth(), isMiter);
140 info.fColor = color;
141 op->setBounds(info.fDevOutside, HasAABloat::kYes, IsZeroArea::kNo);
Brian Salomon6a639042016-12-14 11:08:17 -0500142 op->fViewMatrix = viewMatrix;
143 return sk_sp<GrDrawOp>(op);
joshualitt3566d442015-09-18 07:12:55 -0700144 }
145
146 const char* name() const override { return "AAStrokeRect"; }
147
Brian Salomon7c3e7182016-12-01 09:35:30 -0500148 SkString dumpInfo() const override {
149 SkString string;
Brian Salomon8c5bad32016-12-20 14:43:36 -0500150 for (const auto& info : fRects) {
Brian Salomon6a639042016-12-14 11:08:17 -0500151 string.appendf(
152 "Color: 0x%08x, ORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
153 "AssistORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
154 "IRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], Degen: %d",
Brian Salomon8c5bad32016-12-20 14:43:36 -0500155 info.fColor, info.fDevOutside.fLeft, info.fDevOutside.fTop,
156 info.fDevOutside.fRight, info.fDevOutside.fBottom, info.fDevOutsideAssist.fLeft,
157 info.fDevOutsideAssist.fTop, info.fDevOutsideAssist.fRight,
158 info.fDevOutsideAssist.fBottom, info.fDevInside.fLeft, info.fDevInside.fTop,
159 info.fDevInside.fRight, info.fDevInside.fBottom, info.fDegenerate);
Brian Salomon7c3e7182016-12-01 09:35:30 -0500160 }
161 string.append(DumpPipelineInfo(*this->pipeline()));
162 string.append(INHERITED::dumpInfo());
163 return string;
164 }
165
joshualittaa37a962015-09-18 13:03:25 -0700166private:
Brian Salomon6a639042016-12-14 11:08:17 -0500167 AAStrokeRectOp() : INHERITED(ClassID()) {}
joshualittaa37a962015-09-18 13:03:25 -0700168
Brian Salomon92aee3d2016-12-21 09:20:25 -0500169 void getPipelineAnalysisInput(GrPipelineAnalysisDrawOpInput* input) const override {
170 input->pipelineColorInput()->setKnownFourComponents(fRects[0].fColor);
171 input->pipelineCoverageInput()->setUnknownSingleComponent();
172 }
173 void applyPipelineOptimizations(const GrPipelineOptimizations&) override;
joshualitt144c3c82015-11-30 12:30:13 -0800174 void onPrepareDraws(Target*) const override;
joshualittaa37a962015-09-18 13:03:25 -0700175
joshualitt3566d442015-09-18 07:12:55 -0700176 static const int kMiterIndexCnt = 3 * 24;
177 static const int kMiterVertexCnt = 16;
178 static const int kNumMiterRectsInIndexBuffer = 256;
179
180 static const int kBevelIndexCnt = 48 + 36 + 24;
181 static const int kBevelVertexCnt = 24;
182 static const int kNumBevelRectsInIndexBuffer = 256;
183
cdalton397536c2016-03-25 12:15:03 -0700184 static const GrBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider, bool miterStroke);
joshualitt3566d442015-09-18 07:12:55 -0700185
Brian Salomon8c5bad32016-12-20 14:43:36 -0500186 bool usesLocalCoords() const { return fUsesLocalCoords; }
187 bool canTweakAlphaForCoverage() const { return fCanTweakAlphaForCoverage; }
joshualittaa37a962015-09-18 13:03:25 -0700188 const SkMatrix& viewMatrix() const { return fViewMatrix; }
189 bool miterStroke() const { return fMiterStroke; }
joshualitt3566d442015-09-18 07:12:55 -0700190
Brian Salomon25a88092016-12-01 09:36:50 -0500191 bool onCombineIfPossible(GrOp* t, const GrCaps&) override;
joshualitt3566d442015-09-18 07:12:55 -0700192
193 void generateAAStrokeRectGeometry(void* vertices,
194 size_t offset,
195 size_t vertexStride,
196 int outerVertexNum,
197 int innerVertexNum,
198 GrColor color,
199 const SkRect& devOutside,
200 const SkRect& devOutsideAssist,
201 const SkRect& devInside,
202 bool miterStroke,
joshualitt11edad92015-09-22 10:32:28 -0700203 bool degenerate,
joshualitt3566d442015-09-18 07:12:55 -0700204 bool tweakAlphaForCoverage) const;
205
bsalomon8b7a9e12016-07-06 13:06:22 -0700206 // TODO support AA rotated stroke rects by copying around view matrices
Brian Salomon8c5bad32016-12-20 14:43:36 -0500207 struct RectInfo {
bsalomon8b7a9e12016-07-06 13:06:22 -0700208 GrColor fColor;
209 SkRect fDevOutside;
210 SkRect fDevOutsideAssist;
211 SkRect fDevInside;
212 bool fDegenerate;
213 };
214
Brian Salomon8c5bad32016-12-20 14:43:36 -0500215 SkSTArray<1, RectInfo, true> fRects;
216 bool fUsesLocalCoords;
217 bool fCanTweakAlphaForCoverage;
joshualittaa37a962015-09-18 13:03:25 -0700218 SkMatrix fViewMatrix;
219 bool fMiterStroke;
joshualitt3566d442015-09-18 07:12:55 -0700220
Brian Salomondad29232016-12-01 16:40:24 -0500221 typedef GrMeshDrawOp INHERITED;
joshualitt3566d442015-09-18 07:12:55 -0700222};
223
Brian Salomon92aee3d2016-12-21 09:20:25 -0500224void AAStrokeRectOp::applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) {
225 if (!optimizations.readsColor()) {
Brian Salomon8c5bad32016-12-20 14:43:36 -0500226 fRects[0].fColor = GrColor_ILLEGAL;
joshualitt9ff64252015-08-10 09:03:51 -0700227 }
Brian Salomon92aee3d2016-12-21 09:20:25 -0500228 optimizations.getOverrideColorIfSet(&fRects[0].fColor);
joshualitt9ff64252015-08-10 09:03:51 -0700229
Brian Salomon92aee3d2016-12-21 09:20:25 -0500230 fUsesLocalCoords = optimizations.readsLocalCoords();
231 fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
232 fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
joshualitt9ff64252015-08-10 09:03:51 -0700233}
234
Brian Salomon6a639042016-12-14 11:08:17 -0500235void AAStrokeRectOp::onPrepareDraws(Target* target) const {
joshualitt9ff64252015-08-10 09:03:51 -0700236 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
237
bungeman06ca8ec2016-06-09 08:01:03 -0700238 sk_sp<GrGeometryProcessor> gp(create_stroke_rect_gp(canTweakAlphaForCoverage,
239 this->viewMatrix(),
Brian Salomon8c5bad32016-12-20 14:43:36 -0500240 this->usesLocalCoords()));
joshualitt9ff64252015-08-10 09:03:51 -0700241 if (!gp) {
242 SkDebugf("Couldn't create GrGeometryProcessor\n");
243 return;
244 }
245
joshualitt9ff64252015-08-10 09:03:51 -0700246 size_t vertexStride = gp->getVertexStride();
247
Brian Salomon6a639042016-12-14 11:08:17 -0500248 SkASSERT(canTweakAlphaForCoverage
249 ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr)
250 : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
joshualitt9ff64252015-08-10 09:03:51 -0700251 int innerVertexNum = 4;
252 int outerVertexNum = this->miterStroke() ? 4 : 8;
253 int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
254 int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
Brian Salomon8c5bad32016-12-20 14:43:36 -0500255 int instanceCount = fRects.count();
joshualitt9ff64252015-08-10 09:03:51 -0700256
Hal Canary144caf52016-11-07 17:57:18 -0500257 const sk_sp<const GrBuffer> indexBuffer(
Brian Salomon6a639042016-12-14 11:08:17 -0500258 GetIndexBuffer(target->resourceProvider(), this->miterStroke()));
joshualitt9ff64252015-08-10 09:03:51 -0700259 InstancedHelper helper;
Brian Salomon6a639042016-12-14 11:08:17 -0500260 void* vertices =
261 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
262 verticesPerInstance, indicesPerInstance, instanceCount);
joshualitt9ff64252015-08-10 09:03:51 -0700263 if (!vertices || !indexBuffer) {
Brian Salomon6a639042016-12-14 11:08:17 -0500264 SkDebugf("Could not allocate vertices\n");
265 return;
266 }
joshualitt9ff64252015-08-10 09:03:51 -0700267
268 for (int i = 0; i < instanceCount; i++) {
Brian Salomon8c5bad32016-12-20 14:43:36 -0500269 const RectInfo& info = fRects[i];
joshualitt9ff64252015-08-10 09:03:51 -0700270 this->generateAAStrokeRectGeometry(vertices,
271 i * verticesPerInstance * vertexStride,
272 vertexStride,
273 outerVertexNum,
274 innerVertexNum,
Brian Salomon8c5bad32016-12-20 14:43:36 -0500275 info.fColor,
276 info.fDevOutside,
277 info.fDevOutsideAssist,
278 info.fDevInside,
joshualittaa37a962015-09-18 13:03:25 -0700279 fMiterStroke,
Brian Salomon8c5bad32016-12-20 14:43:36 -0500280 info.fDegenerate,
joshualitt9ff64252015-08-10 09:03:51 -0700281 canTweakAlphaForCoverage);
282 }
bungeman06ca8ec2016-06-09 08:01:03 -0700283 helper.recordDraw(target, gp.get());
joshualitt9ff64252015-08-10 09:03:51 -0700284}
285
Brian Salomon6a639042016-12-14 11:08:17 -0500286const GrBuffer* AAStrokeRectOp::GetIndexBuffer(GrResourceProvider* resourceProvider,
287 bool miterStroke) {
joshualitt9ff64252015-08-10 09:03:51 -0700288 if (miterStroke) {
Brian Salomon6a639042016-12-14 11:08:17 -0500289 // clang-format off
joshualitt9ff64252015-08-10 09:03:51 -0700290 static const uint16_t gMiterIndices[] = {
291 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
292 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
293 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
294 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
295
296 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
297 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
298 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
299 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
300
301 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
302 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
303 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
304 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
305 };
Brian Salomon6a639042016-12-14 11:08:17 -0500306 // clang-format on
joshualitt9ff64252015-08-10 09:03:51 -0700307 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
308 GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
Brian Salomon6a639042016-12-14 11:08:17 -0500309 return resourceProvider->findOrCreateInstancedIndexBuffer(
310 gMiterIndices, kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
311 gMiterIndexBufferKey);
joshualitt9ff64252015-08-10 09:03:51 -0700312 } else {
313 /**
314 * As in miter-stroke, index = a + b, and a is the current index, b is the shift
315 * from the first index. The index layout:
316 * outer AA line: 0~3, 4~7
317 * outer edge: 8~11, 12~15
318 * inner edge: 16~19
319 * inner AA line: 20~23
320 * Following comes a bevel-stroke rect and its indices:
321 *
322 * 4 7
323 * *********************************
324 * * ______________________________ *
325 * * / 12 15 \ *
326 * * / \ *
327 * 0 * |8 16_____________________19 11 | * 3
328 * * | | | | *
329 * * | | **************** | | *
330 * * | | * 20 23 * | | *
331 * * | | * * | | *
332 * * | | * 21 22 * | | *
333 * * | | **************** | | *
334 * * | |____________________| | *
335 * 1 * |9 17 18 10| * 2
336 * * \ / *
337 * * \13 __________________________14/ *
338 * * *
339 * **********************************
340 * 5 6
341 */
Brian Salomon6a639042016-12-14 11:08:17 -0500342 // clang-format off
joshualitt9ff64252015-08-10 09:03:51 -0700343 static const uint16_t gBevelIndices[] = {
344 // Draw outer AA, from outer AA line to outer edge, shift is 0.
345 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
346 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
347 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
348 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
349 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
350 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
351 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
352 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
353
354 // Draw the stroke, from outer edge to inner edge, shift is 8.
355 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
356 1 + 8, 5 + 8, 9 + 8,
357 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
358 6 + 8, 2 + 8, 10 + 8,
359 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
360 3 + 8, 7 + 8, 11 + 8,
361 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
362 4 + 8, 0 + 8, 8 + 8,
363
364 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
365 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
366 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
367 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
368 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
369 };
Brian Salomon6a639042016-12-14 11:08:17 -0500370 // clang-format on
joshualitt9ff64252015-08-10 09:03:51 -0700371 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
372
373 GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
Brian Salomon6a639042016-12-14 11:08:17 -0500374 return resourceProvider->findOrCreateInstancedIndexBuffer(
375 gBevelIndices, kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
376 gBevelIndexBufferKey);
joshualitt9ff64252015-08-10 09:03:51 -0700377 }
378}
379
Brian Salomon6a639042016-12-14 11:08:17 -0500380bool AAStrokeRectOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
381 AAStrokeRectOp* that = t->cast<AAStrokeRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -0700382
383 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
384 that->bounds(), caps)) {
joshualitt9ff64252015-08-10 09:03:51 -0700385 return false;
386 }
387
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500388 // TODO combine across miterstroke changes
joshualitt9ff64252015-08-10 09:03:51 -0700389 if (this->miterStroke() != that->miterStroke()) {
390 return false;
391 }
392
393 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500394 // local coords then we won't be able to combine. We could actually upload the viewmatrix
joshualitt9ff64252015-08-10 09:03:51 -0700395 // using vertex attributes in these cases, but haven't investigated that
396 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
397 return false;
398 }
399
Brian Salomon6a639042016-12-14 11:08:17 -0500400 // In the event of two ops, one who can tweak, one who cannot, we just fall back to not
401 // tweaking.
joshualitt9ff64252015-08-10 09:03:51 -0700402 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
Brian Salomon8c5bad32016-12-20 14:43:36 -0500403 fCanTweakAlphaForCoverage = false;
joshualitt9ff64252015-08-10 09:03:51 -0700404 }
405
Brian Salomon8c5bad32016-12-20 14:43:36 -0500406 fRects.push_back_n(that->fRects.count(), that->fRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700407 this->joinBounds(*that);
joshualitt9ff64252015-08-10 09:03:51 -0700408 return true;
409}
410
joshualitt11edad92015-09-22 10:32:28 -0700411static void setup_scale(int* scale, SkScalar inset) {
412 if (inset < SK_ScalarHalf) {
413 *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
414 SkASSERT(*scale >= 0 && *scale <= 255);
415 } else {
416 *scale = 0xff;
417 }
418}
419
Brian Salomon6a639042016-12-14 11:08:17 -0500420void AAStrokeRectOp::generateAAStrokeRectGeometry(void* vertices,
421 size_t offset,
422 size_t vertexStride,
423 int outerVertexNum,
424 int innerVertexNum,
425 GrColor color,
426 const SkRect& devOutside,
427 const SkRect& devOutsideAssist,
428 const SkRect& devInside,
429 bool miterStroke,
430 bool degenerate,
431 bool tweakAlphaForCoverage) const {
joshualitt9ff64252015-08-10 09:03:51 -0700432 intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
433
434 // We create vertices for four nested rectangles. There are two ramps from 0 to full
435 // coverage, one on the exterior of the stroke and the other on the interior.
436 // The following pointers refer to the four rects, from outermost to innermost.
437 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
438 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
439 SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
Brian Salomon6a639042016-12-14 11:08:17 -0500440 SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(
441 verts + (2 * outerVertexNum + innerVertexNum) * vertexStride);
joshualitt9ff64252015-08-10 09:03:51 -0700442
443#ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
444 // TODO: this only really works if the X & Y margins are the same all around
445 // the rect (or if they are all >= 1.0).
joshualitt11edad92015-09-22 10:32:28 -0700446 SkScalar inset;
447 if (!degenerate) {
448 inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
449 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
450 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
451 if (miterStroke) {
452 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
453 } else {
Brian Salomon6a639042016-12-14 11:08:17 -0500454 inset = SK_ScalarHalf *
455 SkMinScalar(inset, devOutsideAssist.fBottom - devInside.fBottom);
joshualitt11edad92015-09-22 10:32:28 -0700456 }
457 SkASSERT(inset >= 0);
joshualitt9ff64252015-08-10 09:03:51 -0700458 } else {
joshualitt11edad92015-09-22 10:32:28 -0700459 // TODO use real devRect here
460 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
Brian Salomon6a639042016-12-14 11:08:17 -0500461 inset = SK_ScalarHalf *
462 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height()));
joshualitt9ff64252015-08-10 09:03:51 -0700463 }
joshualitt9ff64252015-08-10 09:03:51 -0700464#else
joshualitt11edad92015-09-22 10:32:28 -0700465 SkScalar inset;
466 if (!degenerate) {
467 inset = SK_ScalarHalf;
468 } else {
469 // TODO use real devRect here
470 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
Brian Salomon6a639042016-12-14 11:08:17 -0500471 inset = SK_ScalarHalf *
472 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height()));
joshualitt11edad92015-09-22 10:32:28 -0700473 }
joshualitt9ff64252015-08-10 09:03:51 -0700474#endif
475
476 if (miterStroke) {
477 // outermost
478 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
479 // inner two
Brian Salomon6a639042016-12-14 11:08:17 -0500480 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
joshualitt11edad92015-09-22 10:32:28 -0700481 if (!degenerate) {
Brian Salomon6a639042016-12-14 11:08:17 -0500482 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
joshualitt11edad92015-09-22 10:32:28 -0700483 // innermost
Brian Salomon6a639042016-12-14 11:08:17 -0500484 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
joshualitt11edad92015-09-22 10:32:28 -0700485 } else {
486 // When the interior rect has become degenerate we smoosh to a single point
Brian Salomon6a639042016-12-14 11:08:17 -0500487 SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
488 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
489 devInside.fBottom, vertexStride);
490 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
491 devInside.fBottom, vertexStride);
joshualitt11edad92015-09-22 10:32:28 -0700492 }
joshualitt9ff64252015-08-10 09:03:51 -0700493 } else {
494 SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
Brian Salomon6a639042016-12-14 11:08:17 -0500495 SkPoint* fan1AssistPos =
496 reinterpret_cast<SkPoint*>(verts + (outerVertexNum + 4) * vertexStride);
joshualitt9ff64252015-08-10 09:03:51 -0700497 // outermost
498 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
499 set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
500 -SK_ScalarHalf);
501 // outer one of the inner two
Brian Salomon6a639042016-12-14 11:08:17 -0500502 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
503 set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset);
joshualitt11edad92015-09-22 10:32:28 -0700504 if (!degenerate) {
505 // inner one of the inner two
Brian Salomon6a639042016-12-14 11:08:17 -0500506 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
joshualitt11edad92015-09-22 10:32:28 -0700507 // innermost
Brian Salomon6a639042016-12-14 11:08:17 -0500508 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
joshualitt11edad92015-09-22 10:32:28 -0700509 } else {
510 // When the interior rect has become degenerate we smoosh to a single point
Brian Salomon6a639042016-12-14 11:08:17 -0500511 SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
512 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
513 devInside.fBottom, vertexStride);
514 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
515 devInside.fBottom, vertexStride);
joshualitt11edad92015-09-22 10:32:28 -0700516 }
joshualitt9ff64252015-08-10 09:03:51 -0700517 }
518
519 // Make verts point to vertex color and then set all the color and coverage vertex attrs
520 // values. The outermost rect has 0 coverage
521 verts += sizeof(SkPoint);
522 for (int i = 0; i < outerVertexNum; ++i) {
523 if (tweakAlphaForCoverage) {
524 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
525 } else {
526 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
527 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
528 }
529 }
530
531 // scale is the coverage for the the inner two rects.
532 int scale;
joshualitt11edad92015-09-22 10:32:28 -0700533 setup_scale(&scale, inset);
joshualitt9ff64252015-08-10 09:03:51 -0700534
535 float innerCoverage = GrNormalizeByteToFloat(scale);
536 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
537
538 verts += outerVertexNum * vertexStride;
539 for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
540 if (tweakAlphaForCoverage) {
541 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
542 } else {
543 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
joshualitt11edad92015-09-22 10:32:28 -0700544 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
joshualitt9ff64252015-08-10 09:03:51 -0700545 }
546 }
547
joshualitt11edad92015-09-22 10:32:28 -0700548 // The innermost rect has 0 coverage, unless we are degenerate, in which case we must apply the
549 // scaled coverage
joshualitt9ff64252015-08-10 09:03:51 -0700550 verts += (outerVertexNum + innerVertexNum) * vertexStride;
joshualitt11edad92015-09-22 10:32:28 -0700551 if (!degenerate) {
552 innerCoverage = 0;
553 scaledColor = 0;
554 }
555
joshualitt9ff64252015-08-10 09:03:51 -0700556 for (int i = 0; i < innerVertexNum; ++i) {
557 if (tweakAlphaForCoverage) {
joshualitt11edad92015-09-22 10:32:28 -0700558 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
joshualitt9ff64252015-08-10 09:03:51 -0700559 } else {
560 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
joshualitt11edad92015-09-22 10:32:28 -0700561 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
joshualitt9ff64252015-08-10 09:03:51 -0700562 }
563 }
564}
565
Brian Salomon6a639042016-12-14 11:08:17 -0500566namespace GrAAStrokeRectOp {
joshualitt3566d442015-09-18 07:12:55 -0700567
Brian Salomon6a639042016-12-14 11:08:17 -0500568sk_sp<GrDrawOp> MakeFillBetweenRects(GrColor color,
569 const SkMatrix& viewMatrix,
570 const SkRect& devOutside,
571 const SkRect& devInside) {
572 return sk_sp<GrDrawOp>(new AAStrokeRectOp(color, viewMatrix, devOutside, devInside));
joshualittaa37a962015-09-18 13:03:25 -0700573}
574
Brian Salomon6a639042016-12-14 11:08:17 -0500575sk_sp<GrDrawOp> Make(GrColor color,
576 const SkMatrix& viewMatrix,
577 const SkRect& rect,
578 const SkStrokeRec& stroke) {
579 return AAStrokeRectOp::Make(color, viewMatrix, rect, stroke);
joshualitt10cae832015-09-22 12:50:33 -0700580}
bsalomon8b7a9e12016-07-06 13:06:22 -0700581}
joshualitt3566d442015-09-18 07:12:55 -0700582
joshualitt9ff64252015-08-10 09:03:51 -0700583///////////////////////////////////////////////////////////////////////////////////////////////////
584
585#ifdef GR_TEST_UTILS
586
Brian Salomon5ec9def2016-12-20 15:34:05 -0500587#include "GrDrawOpTest.h"
joshualitt9ff64252015-08-10 09:03:51 -0700588
Brian Salomon5ec9def2016-12-20 15:34:05 -0500589DRAW_OP_TEST_DEFINE(AAStrokeRectOp) {
joshualitt9ff64252015-08-10 09:03:51 -0700590 bool miterStroke = random->nextBool();
591
bsalomon40ef4852016-05-02 13:22:13 -0700592 // Create either a empty rect or a non-empty rect.
Brian Salomon6a639042016-12-14 11:08:17 -0500593 SkRect rect =
594 random->nextBool() ? SkRect::MakeXYWH(10, 10, 50, 40) : SkRect::MakeXYWH(6, 7, 0, 0);
bsalomon40ef4852016-05-02 13:22:13 -0700595 SkScalar minDim = SkMinScalar(rect.width(), rect.height());
596 SkScalar strokeWidth = random->nextUScalar1() * minDim;
joshualitt9ff64252015-08-10 09:03:51 -0700597
joshualitt3566d442015-09-18 07:12:55 -0700598 GrColor color = GrRandomColor(random);
joshualitt9ff64252015-08-10 09:03:51 -0700599
bsalomon40ef4852016-05-02 13:22:13 -0700600 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
601 rec.setStrokeStyle(strokeWidth);
602 rec.setStrokeParams(SkPaint::kButt_Cap,
Brian Salomon6a639042016-12-14 11:08:17 -0500603 miterStroke ? SkPaint::kMiter_Join : SkPaint::kBevel_Join, 1.f);
bsalomon40ef4852016-05-02 13:22:13 -0700604 SkMatrix matrix = GrTest::TestMatrixRectStaysRect(random);
Brian Salomon5ec9def2016-12-20 15:34:05 -0500605 return GrAAStrokeRectOp::Make(color, matrix, rect, rec);
joshualitt9ff64252015-08-10 09:03:51 -0700606}
607
608#endif