blob: 2b68636deed16a990d731601cd6a20a295469b15 [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,
97 bool usesLocalCoords,
98 bool coverageIgnored) {
99 using namespace GrDefaultGeoProcFactory;
100
101 Color color(Color::kAttribute_Type);
102 Coverage::Type coverageType;
103 // TODO remove coverage if coverage is ignored
104 /*if (coverageIgnored) {
105 coverageType = Coverage::kNone_Type;
106 } else*/ if (tweakAlphaForCoverage) {
107 coverageType = Coverage::kSolid_Type;
108 } else {
109 coverageType = Coverage::kAttribute_Type;
110 }
111 Coverage coverage(coverageType);
Brian Salomon6a639042016-12-14 11:08:17 -0500112 LocalCoords localCoords(usesLocalCoords ? LocalCoords::kUsePosition_Type
113 : LocalCoords::kUnused_Type);
bungeman06ca8ec2016-06-09 08:01:03 -0700114 return MakeForDeviceSpace(color, coverage, localCoords, viewMatrix);
joshualitt9ff64252015-08-10 09:03:51 -0700115}
116
Brian Salomon6a639042016-12-14 11:08:17 -0500117class AAStrokeRectOp final : public GrMeshDrawOp {
joshualitt3566d442015-09-18 07:12:55 -0700118public:
Brian Salomon25a88092016-12-01 09:36:50 -0500119 DEFINE_OP_CLASS_ID
joshualitt3566d442015-09-18 07:12:55 -0700120
Brian Salomon6a639042016-12-14 11:08:17 -0500121 AAStrokeRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& devOutside,
122 const SkRect& devInside)
123 : INHERITED(ClassID()), fViewMatrix(viewMatrix) {
caryclarkd6562002016-07-27 12:02:07 -0700124 SkASSERT(!devOutside.isEmpty());
125 SkASSERT(!devInside.isEmpty());
joshualitt3566d442015-09-18 07:12:55 -0700126
bsalomon8b7a9e12016-07-06 13:06:22 -0700127 fGeoData.emplace_back(Geometry{color, devOutside, devOutside, devInside, false});
bsalomon88cf17d2016-07-08 06:40:56 -0700128 this->setBounds(devOutside, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon8b7a9e12016-07-06 13:06:22 -0700129 fMiterStroke = true;
130 }
131
Brian Salomon6a639042016-12-14 11:08:17 -0500132 static sk_sp<GrDrawOp> Make(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
133 const SkStrokeRec& stroke) {
bsalomon8b7a9e12016-07-06 13:06:22 -0700134 bool isMiter;
135 if (!allowed_stroke(stroke, &isMiter)) {
136 return nullptr;
137 }
138
Brian Salomon6a639042016-12-14 11:08:17 -0500139 AAStrokeRectOp* op = new AAStrokeRectOp();
140 op->fMiterStroke = isMiter;
141 Geometry& geo = op->fGeoData.push_back();
bsalomon8b7a9e12016-07-06 13:06:22 -0700142 compute_rects(&geo.fDevOutside, &geo.fDevOutsideAssist, &geo.fDevInside, &geo.fDegenerate,
143 viewMatrix, rect, stroke.getWidth(), isMiter);
144 geo.fColor = color;
Brian Salomon6a639042016-12-14 11:08:17 -0500145 op->setBounds(geo.fDevOutside, HasAABloat::kYes, IsZeroArea::kNo);
146 op->fViewMatrix = viewMatrix;
147 return sk_sp<GrDrawOp>(op);
joshualitt3566d442015-09-18 07:12:55 -0700148 }
149
150 const char* name() const override { return "AAStrokeRect"; }
151
Brian Salomon7c3e7182016-12-01 09:35:30 -0500152 SkString dumpInfo() const override {
153 SkString string;
154 for (const auto& geo : fGeoData) {
Brian Salomon6a639042016-12-14 11:08:17 -0500155 string.appendf(
156 "Color: 0x%08x, ORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
157 "AssistORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
158 "IRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], Degen: %d",
159 geo.fColor, geo.fDevOutside.fLeft, geo.fDevOutside.fTop, geo.fDevOutside.fRight,
160 geo.fDevOutside.fBottom, geo.fDevOutsideAssist.fLeft,
161 geo.fDevOutsideAssist.fTop, geo.fDevOutsideAssist.fRight,
162 geo.fDevOutsideAssist.fBottom, geo.fDevInside.fLeft, geo.fDevInside.fTop,
163 geo.fDevInside.fRight, geo.fDevInside.fBottom, geo.fDegenerate);
Brian Salomon7c3e7182016-12-01 09:35:30 -0500164 }
165 string.append(DumpPipelineInfo(*this->pipeline()));
166 string.append(INHERITED::dumpInfo());
167 return string;
168 }
169
halcanary9d524f22016-03-29 09:03:52 -0700170 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800171 GrInitInvariantOutput* coverage,
172 GrBatchToXPOverrides* overrides) const override {
Brian Salomon6a639042016-12-14 11:08:17 -0500173 // When this is called there is only one rect.
ethannicholasff210322015-11-24 12:10:10 -0800174 color->setKnownFourComponents(fGeoData[0].fColor);
175 coverage->setUnknownSingleComponent();
joshualitt3566d442015-09-18 07:12:55 -0700176 }
177
joshualittaa37a962015-09-18 13:03:25 -0700178private:
Brian Salomon6a639042016-12-14 11:08:17 -0500179 AAStrokeRectOp() : INHERITED(ClassID()) {}
joshualittaa37a962015-09-18 13:03:25 -0700180
joshualitt144c3c82015-11-30 12:30:13 -0800181 void onPrepareDraws(Target*) const override;
ethannicholasff210322015-11-24 12:10:10 -0800182 void initBatchTracker(const GrXPOverridesForBatch&) override;
joshualittaa37a962015-09-18 13:03:25 -0700183
joshualitt3566d442015-09-18 07:12:55 -0700184 static const int kMiterIndexCnt = 3 * 24;
185 static const int kMiterVertexCnt = 16;
186 static const int kNumMiterRectsInIndexBuffer = 256;
187
188 static const int kBevelIndexCnt = 48 + 36 + 24;
189 static const int kBevelVertexCnt = 24;
190 static const int kNumBevelRectsInIndexBuffer = 256;
191
cdalton397536c2016-03-25 12:15:03 -0700192 static const GrBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider, bool miterStroke);
joshualitt3566d442015-09-18 07:12:55 -0700193
194 GrColor color() const { return fBatch.fColor; }
195 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
196 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
197 bool colorIgnored() const { return fBatch.fColorIgnored; }
joshualitt3566d442015-09-18 07:12:55 -0700198 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
joshualittaa37a962015-09-18 13:03:25 -0700199 const SkMatrix& viewMatrix() const { return fViewMatrix; }
200 bool miterStroke() const { return fMiterStroke; }
joshualitt3566d442015-09-18 07:12:55 -0700201
Brian Salomon25a88092016-12-01 09:36:50 -0500202 bool onCombineIfPossible(GrOp* t, const GrCaps&) override;
joshualitt3566d442015-09-18 07:12:55 -0700203
204 void generateAAStrokeRectGeometry(void* vertices,
205 size_t offset,
206 size_t vertexStride,
207 int outerVertexNum,
208 int innerVertexNum,
209 GrColor color,
210 const SkRect& devOutside,
211 const SkRect& devOutsideAssist,
212 const SkRect& devInside,
213 bool miterStroke,
joshualitt11edad92015-09-22 10:32:28 -0700214 bool degenerate,
joshualitt3566d442015-09-18 07:12:55 -0700215 bool tweakAlphaForCoverage) const;
216
217 struct BatchTracker {
joshualitt3566d442015-09-18 07:12:55 -0700218 GrColor fColor;
219 bool fUsesLocalCoords;
220 bool fColorIgnored;
221 bool fCoverageIgnored;
joshualitt3566d442015-09-18 07:12:55 -0700222 bool fCanTweakAlphaForCoverage;
223 };
224
bsalomon8b7a9e12016-07-06 13:06:22 -0700225 // TODO support AA rotated stroke rects by copying around view matrices
226 struct Geometry {
227 GrColor fColor;
228 SkRect fDevOutside;
229 SkRect fDevOutsideAssist;
230 SkRect fDevInside;
231 bool fDegenerate;
232 };
233
joshualitt3566d442015-09-18 07:12:55 -0700234 BatchTracker fBatch;
235 SkSTArray<1, Geometry, true> fGeoData;
joshualittaa37a962015-09-18 13:03:25 -0700236 SkMatrix fViewMatrix;
237 bool fMiterStroke;
joshualitt3566d442015-09-18 07:12:55 -0700238
Brian Salomondad29232016-12-01 16:40:24 -0500239 typedef GrMeshDrawOp INHERITED;
joshualitt3566d442015-09-18 07:12:55 -0700240};
241
Brian Salomon6a639042016-12-14 11:08:17 -0500242void AAStrokeRectOp::initBatchTracker(const GrXPOverridesForBatch& overrides) {
joshualitt9ff64252015-08-10 09:03:51 -0700243 // Handle any color overrides
ethannicholasff210322015-11-24 12:10:10 -0800244 if (!overrides.readsColor()) {
joshualitt9ff64252015-08-10 09:03:51 -0700245 fGeoData[0].fColor = GrColor_ILLEGAL;
246 }
ethannicholasff210322015-11-24 12:10:10 -0800247 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt9ff64252015-08-10 09:03:51 -0700248
249 // setup batch properties
ethannicholasff210322015-11-24 12:10:10 -0800250 fBatch.fColorIgnored = !overrides.readsColor();
joshualitt9ff64252015-08-10 09:03:51 -0700251 fBatch.fColor = fGeoData[0].fColor;
ethannicholasff210322015-11-24 12:10:10 -0800252 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
253 fBatch.fCoverageIgnored = !overrides.readsCoverage();
254 fBatch.fCanTweakAlphaForCoverage = overrides.canTweakAlphaForCoverage();
joshualitt9ff64252015-08-10 09:03:51 -0700255}
256
Brian Salomon6a639042016-12-14 11:08:17 -0500257void AAStrokeRectOp::onPrepareDraws(Target* target) const {
joshualitt9ff64252015-08-10 09:03:51 -0700258 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
259
bungeman06ca8ec2016-06-09 08:01:03 -0700260 sk_sp<GrGeometryProcessor> gp(create_stroke_rect_gp(canTweakAlphaForCoverage,
261 this->viewMatrix(),
262 this->usesLocalCoords(),
263 this->coverageIgnored()));
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 size_t vertexStride = gp->getVertexStride();
270
Brian Salomon6a639042016-12-14 11:08:17 -0500271 SkASSERT(canTweakAlphaForCoverage
272 ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr)
273 : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
joshualitt9ff64252015-08-10 09:03:51 -0700274 int innerVertexNum = 4;
275 int outerVertexNum = this->miterStroke() ? 4 : 8;
276 int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
277 int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
278 int instanceCount = fGeoData.count();
279
Hal Canary144caf52016-11-07 17:57:18 -0500280 const sk_sp<const GrBuffer> indexBuffer(
Brian Salomon6a639042016-12-14 11:08:17 -0500281 GetIndexBuffer(target->resourceProvider(), this->miterStroke()));
joshualitt9ff64252015-08-10 09:03:51 -0700282 InstancedHelper helper;
Brian Salomon6a639042016-12-14 11:08:17 -0500283 void* vertices =
284 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
285 verticesPerInstance, indicesPerInstance, instanceCount);
joshualitt9ff64252015-08-10 09:03:51 -0700286 if (!vertices || !indexBuffer) {
Brian Salomon6a639042016-12-14 11:08:17 -0500287 SkDebugf("Could not allocate vertices\n");
288 return;
289 }
joshualitt9ff64252015-08-10 09:03:51 -0700290
291 for (int i = 0; i < instanceCount; i++) {
292 const Geometry& args = fGeoData[i];
293 this->generateAAStrokeRectGeometry(vertices,
294 i * verticesPerInstance * vertexStride,
295 vertexStride,
296 outerVertexNum,
297 innerVertexNum,
298 args.fColor,
299 args.fDevOutside,
300 args.fDevOutsideAssist,
301 args.fDevInside,
joshualittaa37a962015-09-18 13:03:25 -0700302 fMiterStroke,
joshualitt11edad92015-09-22 10:32:28 -0700303 args.fDegenerate,
joshualitt9ff64252015-08-10 09:03:51 -0700304 canTweakAlphaForCoverage);
305 }
bungeman06ca8ec2016-06-09 08:01:03 -0700306 helper.recordDraw(target, gp.get());
joshualitt9ff64252015-08-10 09:03:51 -0700307}
308
Brian Salomon6a639042016-12-14 11:08:17 -0500309const GrBuffer* AAStrokeRectOp::GetIndexBuffer(GrResourceProvider* resourceProvider,
310 bool miterStroke) {
joshualitt9ff64252015-08-10 09:03:51 -0700311 if (miterStroke) {
Brian Salomon6a639042016-12-14 11:08:17 -0500312 // clang-format off
joshualitt9ff64252015-08-10 09:03:51 -0700313 static const uint16_t gMiterIndices[] = {
314 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
315 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
316 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
317 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
318
319 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
320 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
321 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
322 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
323
324 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
325 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
326 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
327 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
328 };
Brian Salomon6a639042016-12-14 11:08:17 -0500329 // clang-format on
joshualitt9ff64252015-08-10 09:03:51 -0700330 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
331 GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
Brian Salomon6a639042016-12-14 11:08:17 -0500332 return resourceProvider->findOrCreateInstancedIndexBuffer(
333 gMiterIndices, kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
334 gMiterIndexBufferKey);
joshualitt9ff64252015-08-10 09:03:51 -0700335 } else {
336 /**
337 * As in miter-stroke, index = a + b, and a is the current index, b is the shift
338 * from the first index. The index layout:
339 * outer AA line: 0~3, 4~7
340 * outer edge: 8~11, 12~15
341 * inner edge: 16~19
342 * inner AA line: 20~23
343 * Following comes a bevel-stroke rect and its indices:
344 *
345 * 4 7
346 * *********************************
347 * * ______________________________ *
348 * * / 12 15 \ *
349 * * / \ *
350 * 0 * |8 16_____________________19 11 | * 3
351 * * | | | | *
352 * * | | **************** | | *
353 * * | | * 20 23 * | | *
354 * * | | * * | | *
355 * * | | * 21 22 * | | *
356 * * | | **************** | | *
357 * * | |____________________| | *
358 * 1 * |9 17 18 10| * 2
359 * * \ / *
360 * * \13 __________________________14/ *
361 * * *
362 * **********************************
363 * 5 6
364 */
Brian Salomon6a639042016-12-14 11:08:17 -0500365 // clang-format off
joshualitt9ff64252015-08-10 09:03:51 -0700366 static const uint16_t gBevelIndices[] = {
367 // Draw outer AA, from outer AA line to outer edge, shift is 0.
368 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
369 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
370 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
371 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
372 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
373 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
374 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
375 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
376
377 // Draw the stroke, from outer edge to inner edge, shift is 8.
378 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
379 1 + 8, 5 + 8, 9 + 8,
380 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
381 6 + 8, 2 + 8, 10 + 8,
382 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
383 3 + 8, 7 + 8, 11 + 8,
384 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
385 4 + 8, 0 + 8, 8 + 8,
386
387 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
388 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
389 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
390 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
391 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
392 };
Brian Salomon6a639042016-12-14 11:08:17 -0500393 // clang-format on
joshualitt9ff64252015-08-10 09:03:51 -0700394 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
395
396 GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
Brian Salomon6a639042016-12-14 11:08:17 -0500397 return resourceProvider->findOrCreateInstancedIndexBuffer(
398 gBevelIndices, kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
399 gBevelIndexBufferKey);
joshualitt9ff64252015-08-10 09:03:51 -0700400 }
401}
402
Brian Salomon6a639042016-12-14 11:08:17 -0500403bool AAStrokeRectOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
404 AAStrokeRectOp* that = t->cast<AAStrokeRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -0700405
406 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
407 that->bounds(), caps)) {
joshualitt9ff64252015-08-10 09:03:51 -0700408 return false;
409 }
410
joshualitt9ff64252015-08-10 09:03:51 -0700411 // TODO batch across miterstroke changes
412 if (this->miterStroke() != that->miterStroke()) {
413 return false;
414 }
415
416 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
417 // local coords then we won't be able to batch. We could actually upload the viewmatrix
418 // using vertex attributes in these cases, but haven't investigated that
419 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
420 return false;
421 }
422
Brian Salomon6a639042016-12-14 11:08:17 -0500423 // In the event of two ops, one who can tweak, one who cannot, we just fall back to not
424 // tweaking.
joshualitt9ff64252015-08-10 09:03:51 -0700425 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
426 fBatch.fCanTweakAlphaForCoverage = false;
427 }
428
429 if (this->color() != that->color()) {
430 fBatch.fColor = GrColor_ILLEGAL;
431 }
bsalomon8b7a9e12016-07-06 13:06:22 -0700432 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700433 this->joinBounds(*that);
joshualitt9ff64252015-08-10 09:03:51 -0700434 return true;
435}
436
joshualitt11edad92015-09-22 10:32:28 -0700437static void setup_scale(int* scale, SkScalar inset) {
438 if (inset < SK_ScalarHalf) {
439 *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
440 SkASSERT(*scale >= 0 && *scale <= 255);
441 } else {
442 *scale = 0xff;
443 }
444}
445
Brian Salomon6a639042016-12-14 11:08:17 -0500446void AAStrokeRectOp::generateAAStrokeRectGeometry(void* vertices,
447 size_t offset,
448 size_t vertexStride,
449 int outerVertexNum,
450 int innerVertexNum,
451 GrColor color,
452 const SkRect& devOutside,
453 const SkRect& devOutsideAssist,
454 const SkRect& devInside,
455 bool miterStroke,
456 bool degenerate,
457 bool tweakAlphaForCoverage) const {
joshualitt9ff64252015-08-10 09:03:51 -0700458 intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
459
460 // We create vertices for four nested rectangles. There are two ramps from 0 to full
461 // coverage, one on the exterior of the stroke and the other on the interior.
462 // The following pointers refer to the four rects, from outermost to innermost.
463 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
464 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
465 SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
Brian Salomon6a639042016-12-14 11:08:17 -0500466 SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(
467 verts + (2 * outerVertexNum + innerVertexNum) * vertexStride);
joshualitt9ff64252015-08-10 09:03:51 -0700468
469#ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
470 // TODO: this only really works if the X & Y margins are the same all around
471 // the rect (or if they are all >= 1.0).
joshualitt11edad92015-09-22 10:32:28 -0700472 SkScalar inset;
473 if (!degenerate) {
474 inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
475 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
476 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
477 if (miterStroke) {
478 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
479 } else {
Brian Salomon6a639042016-12-14 11:08:17 -0500480 inset = SK_ScalarHalf *
481 SkMinScalar(inset, devOutsideAssist.fBottom - devInside.fBottom);
joshualitt11edad92015-09-22 10:32:28 -0700482 }
483 SkASSERT(inset >= 0);
joshualitt9ff64252015-08-10 09:03:51 -0700484 } else {
joshualitt11edad92015-09-22 10:32:28 -0700485 // TODO use real devRect here
486 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
Brian Salomon6a639042016-12-14 11:08:17 -0500487 inset = SK_ScalarHalf *
488 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height()));
joshualitt9ff64252015-08-10 09:03:51 -0700489 }
joshualitt9ff64252015-08-10 09:03:51 -0700490#else
joshualitt11edad92015-09-22 10:32:28 -0700491 SkScalar inset;
492 if (!degenerate) {
493 inset = SK_ScalarHalf;
494 } else {
495 // TODO use real devRect here
496 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
Brian Salomon6a639042016-12-14 11:08:17 -0500497 inset = SK_ScalarHalf *
498 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height()));
joshualitt11edad92015-09-22 10:32:28 -0700499 }
joshualitt9ff64252015-08-10 09:03:51 -0700500#endif
501
502 if (miterStroke) {
503 // outermost
504 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
505 // inner two
Brian Salomon6a639042016-12-14 11:08:17 -0500506 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
joshualitt11edad92015-09-22 10:32:28 -0700507 if (!degenerate) {
Brian Salomon6a639042016-12-14 11:08:17 -0500508 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
joshualitt11edad92015-09-22 10:32:28 -0700509 // innermost
Brian Salomon6a639042016-12-14 11:08:17 -0500510 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
joshualitt11edad92015-09-22 10:32:28 -0700511 } else {
512 // When the interior rect has become degenerate we smoosh to a single point
Brian Salomon6a639042016-12-14 11:08:17 -0500513 SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
514 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
515 devInside.fBottom, vertexStride);
516 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
517 devInside.fBottom, vertexStride);
joshualitt11edad92015-09-22 10:32:28 -0700518 }
joshualitt9ff64252015-08-10 09:03:51 -0700519 } else {
520 SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
Brian Salomon6a639042016-12-14 11:08:17 -0500521 SkPoint* fan1AssistPos =
522 reinterpret_cast<SkPoint*>(verts + (outerVertexNum + 4) * vertexStride);
joshualitt9ff64252015-08-10 09:03:51 -0700523 // outermost
524 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
525 set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
526 -SK_ScalarHalf);
527 // outer one of the inner two
Brian Salomon6a639042016-12-14 11:08:17 -0500528 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
529 set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset);
joshualitt11edad92015-09-22 10:32:28 -0700530 if (!degenerate) {
531 // inner one of the inner two
Brian Salomon6a639042016-12-14 11:08:17 -0500532 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
joshualitt11edad92015-09-22 10:32:28 -0700533 // innermost
Brian Salomon6a639042016-12-14 11:08:17 -0500534 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
joshualitt11edad92015-09-22 10:32:28 -0700535 } else {
536 // When the interior rect has become degenerate we smoosh to a single point
Brian Salomon6a639042016-12-14 11:08:17 -0500537 SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
538 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
539 devInside.fBottom, vertexStride);
540 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight,
541 devInside.fBottom, vertexStride);
joshualitt11edad92015-09-22 10:32:28 -0700542 }
joshualitt9ff64252015-08-10 09:03:51 -0700543 }
544
545 // Make verts point to vertex color and then set all the color and coverage vertex attrs
546 // values. The outermost rect has 0 coverage
547 verts += sizeof(SkPoint);
548 for (int i = 0; i < outerVertexNum; ++i) {
549 if (tweakAlphaForCoverage) {
550 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
551 } else {
552 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
553 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
554 }
555 }
556
557 // scale is the coverage for the the inner two rects.
558 int scale;
joshualitt11edad92015-09-22 10:32:28 -0700559 setup_scale(&scale, inset);
joshualitt9ff64252015-08-10 09:03:51 -0700560
561 float innerCoverage = GrNormalizeByteToFloat(scale);
562 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
563
564 verts += outerVertexNum * vertexStride;
565 for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
566 if (tweakAlphaForCoverage) {
567 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
568 } else {
569 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
joshualitt11edad92015-09-22 10:32:28 -0700570 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
joshualitt9ff64252015-08-10 09:03:51 -0700571 }
572 }
573
joshualitt11edad92015-09-22 10:32:28 -0700574 // The innermost rect has 0 coverage, unless we are degenerate, in which case we must apply the
575 // scaled coverage
joshualitt9ff64252015-08-10 09:03:51 -0700576 verts += (outerVertexNum + innerVertexNum) * vertexStride;
joshualitt11edad92015-09-22 10:32:28 -0700577 if (!degenerate) {
578 innerCoverage = 0;
579 scaledColor = 0;
580 }
581
joshualitt9ff64252015-08-10 09:03:51 -0700582 for (int i = 0; i < innerVertexNum; ++i) {
583 if (tweakAlphaForCoverage) {
joshualitt11edad92015-09-22 10:32:28 -0700584 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
joshualitt9ff64252015-08-10 09:03:51 -0700585 } else {
586 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
joshualitt11edad92015-09-22 10:32:28 -0700587 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
joshualitt9ff64252015-08-10 09:03:51 -0700588 }
589 }
590}
591
Brian Salomon6a639042016-12-14 11:08:17 -0500592namespace GrAAStrokeRectOp {
joshualitt3566d442015-09-18 07:12:55 -0700593
Brian Salomon6a639042016-12-14 11:08:17 -0500594sk_sp<GrDrawOp> MakeFillBetweenRects(GrColor color,
595 const SkMatrix& viewMatrix,
596 const SkRect& devOutside,
597 const SkRect& devInside) {
598 return sk_sp<GrDrawOp>(new AAStrokeRectOp(color, viewMatrix, devOutside, devInside));
joshualittaa37a962015-09-18 13:03:25 -0700599}
600
Brian Salomon6a639042016-12-14 11:08:17 -0500601sk_sp<GrDrawOp> Make(GrColor color,
602 const SkMatrix& viewMatrix,
603 const SkRect& rect,
604 const SkStrokeRec& stroke) {
605 return AAStrokeRectOp::Make(color, viewMatrix, rect, stroke);
joshualitt10cae832015-09-22 12:50:33 -0700606}
bsalomon8b7a9e12016-07-06 13:06:22 -0700607}
joshualitt3566d442015-09-18 07:12:55 -0700608
joshualitt9ff64252015-08-10 09:03:51 -0700609///////////////////////////////////////////////////////////////////////////////////////////////////
610
611#ifdef GR_TEST_UTILS
612
613#include "GrBatchTest.h"
614
Brian Salomon6a639042016-12-14 11:08:17 -0500615DRAW_BATCH_TEST_DEFINE(AAStrokeRectOp) {
joshualitt9ff64252015-08-10 09:03:51 -0700616 bool miterStroke = random->nextBool();
617
bsalomon40ef4852016-05-02 13:22:13 -0700618 // Create either a empty rect or a non-empty rect.
Brian Salomon6a639042016-12-14 11:08:17 -0500619 SkRect rect =
620 random->nextBool() ? SkRect::MakeXYWH(10, 10, 50, 40) : SkRect::MakeXYWH(6, 7, 0, 0);
bsalomon40ef4852016-05-02 13:22:13 -0700621 SkScalar minDim = SkMinScalar(rect.width(), rect.height());
622 SkScalar strokeWidth = random->nextUScalar1() * minDim;
joshualitt9ff64252015-08-10 09:03:51 -0700623
joshualitt3566d442015-09-18 07:12:55 -0700624 GrColor color = GrRandomColor(random);
joshualitt9ff64252015-08-10 09:03:51 -0700625
bsalomon40ef4852016-05-02 13:22:13 -0700626 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
627 rec.setStrokeStyle(strokeWidth);
628 rec.setStrokeParams(SkPaint::kButt_Cap,
Brian Salomon6a639042016-12-14 11:08:17 -0500629 miterStroke ? SkPaint::kMiter_Join : SkPaint::kBevel_Join, 1.f);
bsalomon40ef4852016-05-02 13:22:13 -0700630 SkMatrix matrix = GrTest::TestMatrixRectStaysRect(random);
Brian Salomon6a639042016-12-14 11:08:17 -0500631 return GrAAStrokeRectOp::Make(color, matrix, rect, rec).release();
joshualitt9ff64252015-08-10 09:03:51 -0700632}
633
634#endif