blob: da77dca6b33fe0d6f3c989356ed267ff0f71d95e [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
8#include "GrAAStrokeRectBatch.h"
9
bsalomon75398562015-08-17 12:55:38 -070010#include "GrBatchFlushState.h"
joshualitt9ff64252015-08-10 09:03:51 -070011#include "GrDefaultGeoProcFactory.h"
12#include "GrResourceKey.h"
13#include "GrResourceProvider.h"
14
15GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
16GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
17
18static void set_inset_fan(SkPoint* pts, size_t stride,
19 const SkRect& r, SkScalar dx, SkScalar dy) {
20 pts->setRectFan(r.fLeft + dx, r.fTop + dy,
21 r.fRight - dx, r.fBottom - dy, stride);
22}
23
bsalomon8b7a9e12016-07-06 13:06:22 -070024// We support all hairlines, bevels, and miters, but not round joins. Also, check whether the miter
25// limit makes a miter join effectively beveled.
26inline static bool allowed_stroke(const SkStrokeRec& stroke, bool* isMiter) {
27 SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style ||
28 stroke.getStyle() == SkStrokeRec::kHairline_Style);
29 // For hairlines, make bevel and round joins appear the same as mitered ones.
30 if (!stroke.getWidth()) {
31 *isMiter = true;
32 return true;
33 }
34 if (stroke.getJoin() == SkPaint::kBevel_Join) {
35 *isMiter = false;
36 return true;
37 }
38 if (stroke.getJoin() == SkPaint::kMiter_Join) {
39 *isMiter = stroke.getMiter() >= SK_ScalarSqrt2;
40 return true;
41 }
42 return false;
43}
44
45static void compute_rects(SkRect* devOutside, SkRect* devOutsideAssist, SkRect* devInside,
46 bool* isDegenerate, const SkMatrix& viewMatrix, const SkRect& rect,
47 SkScalar strokeWidth, bool miterStroke) {
48 SkRect devRect;
49 viewMatrix.mapRect(&devRect, rect);
50
51 SkVector devStrokeSize;
52 if (strokeWidth > 0) {
53 devStrokeSize.set(strokeWidth, strokeWidth);
54 viewMatrix.mapVectors(&devStrokeSize, 1);
55 devStrokeSize.setAbs(devStrokeSize);
56 } else {
57 devStrokeSize.set(SK_Scalar1, SK_Scalar1);
58 }
59
60 const SkScalar dx = devStrokeSize.fX;
61 const SkScalar dy = devStrokeSize.fY;
62 const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf);
63 const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf);
64
65 *devOutside = devRect;
66 *devOutsideAssist = devRect;
67 *devInside = devRect;
68
69 devOutside->outset(rx, ry);
70 devInside->inset(rx, ry);
71
72 // If we have a degenerate stroking rect(ie the stroke is larger than inner rect) then we
73 // make a degenerate inside rect to avoid double hitting. We will also jam all of the points
74 // together when we render these rects.
75 SkScalar spare;
76 {
77 SkScalar w = devRect.width() - dx;
78 SkScalar h = devRect.height() - dy;
79 spare = SkTMin(w, h);
80 }
81
82 *isDegenerate = spare <= 0;
83 if (*isDegenerate) {
84 devInside->fLeft = devInside->fRight = devRect.centerX();
85 devInside->fTop = devInside->fBottom = devRect.centerY();
86 }
87
88 // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
89 // to draw the outside of the octagon. Because there are 8 vertices on the outer
90 // edge, while vertex number of inner edge is 4, the same as miter-stroke.
91 if (!miterStroke) {
92 devOutside->inset(0, ry);
93 devOutsideAssist->outset(0, ry);
94 }
95}
96
bungeman06ca8ec2016-06-09 08:01:03 -070097static sk_sp<GrGeometryProcessor> create_stroke_rect_gp(bool tweakAlphaForCoverage,
joshualitt9ff64252015-08-10 09:03:51 -070098 const SkMatrix& viewMatrix,
99 bool usesLocalCoords,
100 bool coverageIgnored) {
101 using namespace GrDefaultGeoProcFactory;
102
103 Color color(Color::kAttribute_Type);
104 Coverage::Type coverageType;
105 // TODO remove coverage if coverage is ignored
106 /*if (coverageIgnored) {
107 coverageType = Coverage::kNone_Type;
108 } else*/ if (tweakAlphaForCoverage) {
109 coverageType = Coverage::kSolid_Type;
110 } else {
111 coverageType = Coverage::kAttribute_Type;
112 }
113 Coverage coverage(coverageType);
114 LocalCoords localCoords(usesLocalCoords ? LocalCoords::kUsePosition_Type :
115 LocalCoords::kUnused_Type);
bungeman06ca8ec2016-06-09 08:01:03 -0700116 return MakeForDeviceSpace(color, coverage, localCoords, viewMatrix);
joshualitt9ff64252015-08-10 09:03:51 -0700117}
118
joshualitt3566d442015-09-18 07:12:55 -0700119class AAStrokeRectBatch : public GrVertexBatch {
120public:
121 DEFINE_BATCH_CLASS_ID
122
bsalomon8b7a9e12016-07-06 13:06:22 -0700123 AAStrokeRectBatch(GrColor color, const SkMatrix& viewMatrix,
124 const SkRect& devOutside, const SkRect& devInside)
125 : INHERITED(ClassID())
126 , fViewMatrix(viewMatrix) {
127 SkASSERT(!devOutside.isEmpty())
128 SkASSERT(!devInside.isEmpty())
joshualitt3566d442015-09-18 07:12:55 -0700129
bsalomon8b7a9e12016-07-06 13:06:22 -0700130 fGeoData.emplace_back(Geometry{color, devOutside, devOutside, devInside, false});
131 fBounds = devOutside;
132 fMiterStroke = true;
133 }
134
135
136 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
137 const SkStrokeRec& stroke) {
138 bool isMiter;
139 if (!allowed_stroke(stroke, &isMiter)) {
140 return nullptr;
141 }
142
143 AAStrokeRectBatch* batch = new AAStrokeRectBatch();
144 batch->fMiterStroke = isMiter;
145 Geometry& geo = batch->fGeoData.push_back();
146 compute_rects(&geo.fDevOutside, &geo.fDevOutsideAssist, &geo.fDevInside, &geo.fDegenerate,
147 viewMatrix, rect, stroke.getWidth(), isMiter);
148 geo.fColor = color;
149 batch->fBounds = geo.fDevOutside;
150 batch->fBounds.join(geo.fDevOutsideAssist);
151 batch->fViewMatrix = viewMatrix;
152 return batch;
joshualitt3566d442015-09-18 07:12:55 -0700153 }
154
155 const char* name() const override { return "AAStrokeRect"; }
156
halcanary9d524f22016-03-29 09:03:52 -0700157 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800158 GrInitInvariantOutput* coverage,
159 GrBatchToXPOverrides* overrides) const override {
joshualitt3566d442015-09-18 07:12:55 -0700160 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800161 color->setKnownFourComponents(fGeoData[0].fColor);
162 coverage->setUnknownSingleComponent();
joshualitt3566d442015-09-18 07:12:55 -0700163 }
164
joshualittaa37a962015-09-18 13:03:25 -0700165private:
bsalomon8b7a9e12016-07-06 13:06:22 -0700166 AAStrokeRectBatch() : INHERITED(ClassID()) {}
joshualittaa37a962015-09-18 13:03:25 -0700167
joshualitt144c3c82015-11-30 12:30:13 -0800168 void onPrepareDraws(Target*) const override;
ethannicholasff210322015-11-24 12:10:10 -0800169 void initBatchTracker(const GrXPOverridesForBatch&) override;
joshualittaa37a962015-09-18 13:03:25 -0700170
joshualitt3566d442015-09-18 07:12:55 -0700171 static const int kMiterIndexCnt = 3 * 24;
172 static const int kMiterVertexCnt = 16;
173 static const int kNumMiterRectsInIndexBuffer = 256;
174
175 static const int kBevelIndexCnt = 48 + 36 + 24;
176 static const int kBevelVertexCnt = 24;
177 static const int kNumBevelRectsInIndexBuffer = 256;
178
cdalton397536c2016-03-25 12:15:03 -0700179 static const GrBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider, bool miterStroke);
joshualitt3566d442015-09-18 07:12:55 -0700180
181 GrColor color() const { return fBatch.fColor; }
182 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
183 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
184 bool colorIgnored() const { return fBatch.fColorIgnored; }
joshualitt3566d442015-09-18 07:12:55 -0700185 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
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
189 bool onCombineIfPossible(GrBatch* t, const GrCaps&) override;
190
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
204 struct BatchTracker {
joshualitt3566d442015-09-18 07:12:55 -0700205 GrColor fColor;
206 bool fUsesLocalCoords;
207 bool fColorIgnored;
208 bool fCoverageIgnored;
joshualitt3566d442015-09-18 07:12:55 -0700209 bool fCanTweakAlphaForCoverage;
210 };
211
bsalomon8b7a9e12016-07-06 13:06:22 -0700212 // TODO support AA rotated stroke rects by copying around view matrices
213 struct Geometry {
214 GrColor fColor;
215 SkRect fDevOutside;
216 SkRect fDevOutsideAssist;
217 SkRect fDevInside;
218 bool fDegenerate;
219 };
220
joshualitt3566d442015-09-18 07:12:55 -0700221 BatchTracker fBatch;
222 SkSTArray<1, Geometry, true> fGeoData;
joshualittaa37a962015-09-18 13:03:25 -0700223 SkMatrix fViewMatrix;
224 bool fMiterStroke;
joshualitt3566d442015-09-18 07:12:55 -0700225
226 typedef GrVertexBatch INHERITED;
227};
228
ethannicholasff210322015-11-24 12:10:10 -0800229void AAStrokeRectBatch::initBatchTracker(const GrXPOverridesForBatch& overrides) {
joshualitt9ff64252015-08-10 09:03:51 -0700230 // Handle any color overrides
ethannicholasff210322015-11-24 12:10:10 -0800231 if (!overrides.readsColor()) {
joshualitt9ff64252015-08-10 09:03:51 -0700232 fGeoData[0].fColor = GrColor_ILLEGAL;
233 }
ethannicholasff210322015-11-24 12:10:10 -0800234 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt9ff64252015-08-10 09:03:51 -0700235
236 // setup batch properties
ethannicholasff210322015-11-24 12:10:10 -0800237 fBatch.fColorIgnored = !overrides.readsColor();
joshualitt9ff64252015-08-10 09:03:51 -0700238 fBatch.fColor = fGeoData[0].fColor;
ethannicholasff210322015-11-24 12:10:10 -0800239 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
240 fBatch.fCoverageIgnored = !overrides.readsCoverage();
241 fBatch.fCanTweakAlphaForCoverage = overrides.canTweakAlphaForCoverage();
joshualitt9ff64252015-08-10 09:03:51 -0700242}
243
joshualitt144c3c82015-11-30 12:30:13 -0800244void AAStrokeRectBatch::onPrepareDraws(Target* target) const {
joshualitt9ff64252015-08-10 09:03:51 -0700245 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
246
bungeman06ca8ec2016-06-09 08:01:03 -0700247 sk_sp<GrGeometryProcessor> gp(create_stroke_rect_gp(canTweakAlphaForCoverage,
248 this->viewMatrix(),
249 this->usesLocalCoords(),
250 this->coverageIgnored()));
joshualitt9ff64252015-08-10 09:03:51 -0700251 if (!gp) {
252 SkDebugf("Couldn't create GrGeometryProcessor\n");
253 return;
254 }
255
joshualitt9ff64252015-08-10 09:03:51 -0700256 size_t vertexStride = gp->getVertexStride();
257
258 SkASSERT(canTweakAlphaForCoverage ?
259 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
260 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
261 int innerVertexNum = 4;
262 int outerVertexNum = this->miterStroke() ? 4 : 8;
263 int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
264 int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
265 int instanceCount = fGeoData.count();
266
cdalton397536c2016-03-25 12:15:03 -0700267 const SkAutoTUnref<const GrBuffer> indexBuffer(
bsalomon75398562015-08-17 12:55:38 -0700268 GetIndexBuffer(target->resourceProvider(), this->miterStroke()));
joshualitt9ff64252015-08-10 09:03:51 -0700269 InstancedHelper helper;
bsalomon75398562015-08-17 12:55:38 -0700270 void* vertices = helper.init(target, kTriangles_GrPrimitiveType, vertexStride,
egdaniel0e1853c2016-03-17 11:35:45 -0700271 indexBuffer, verticesPerInstance, indicesPerInstance,
joshualitt9ff64252015-08-10 09:03:51 -0700272 instanceCount);
273 if (!vertices || !indexBuffer) {
274 SkDebugf("Could not allocate vertices\n");
275 return;
276 }
277
278 for (int i = 0; i < instanceCount; i++) {
279 const Geometry& args = fGeoData[i];
280 this->generateAAStrokeRectGeometry(vertices,
281 i * verticesPerInstance * vertexStride,
282 vertexStride,
283 outerVertexNum,
284 innerVertexNum,
285 args.fColor,
286 args.fDevOutside,
287 args.fDevOutsideAssist,
288 args.fDevInside,
joshualittaa37a962015-09-18 13:03:25 -0700289 fMiterStroke,
joshualitt11edad92015-09-22 10:32:28 -0700290 args.fDegenerate,
joshualitt9ff64252015-08-10 09:03:51 -0700291 canTweakAlphaForCoverage);
292 }
bungeman06ca8ec2016-06-09 08:01:03 -0700293 helper.recordDraw(target, gp.get());
joshualitt9ff64252015-08-10 09:03:51 -0700294}
295
cdalton397536c2016-03-25 12:15:03 -0700296const GrBuffer* AAStrokeRectBatch::GetIndexBuffer(GrResourceProvider* resourceProvider,
297 bool miterStroke) {
joshualitt9ff64252015-08-10 09:03:51 -0700298
299 if (miterStroke) {
300 static const uint16_t gMiterIndices[] = {
301 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
302 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
303 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
304 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
305
306 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
307 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
308 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
309 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
310
311 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
312 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
313 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
314 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
315 };
316 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
317 GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
318 return resourceProvider->findOrCreateInstancedIndexBuffer(gMiterIndices,
319 kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
320 gMiterIndexBufferKey);
321 } else {
322 /**
323 * As in miter-stroke, index = a + b, and a is the current index, b is the shift
324 * from the first index. The index layout:
325 * outer AA line: 0~3, 4~7
326 * outer edge: 8~11, 12~15
327 * inner edge: 16~19
328 * inner AA line: 20~23
329 * Following comes a bevel-stroke rect and its indices:
330 *
331 * 4 7
332 * *********************************
333 * * ______________________________ *
334 * * / 12 15 \ *
335 * * / \ *
336 * 0 * |8 16_____________________19 11 | * 3
337 * * | | | | *
338 * * | | **************** | | *
339 * * | | * 20 23 * | | *
340 * * | | * * | | *
341 * * | | * 21 22 * | | *
342 * * | | **************** | | *
343 * * | |____________________| | *
344 * 1 * |9 17 18 10| * 2
345 * * \ / *
346 * * \13 __________________________14/ *
347 * * *
348 * **********************************
349 * 5 6
350 */
351 static const uint16_t gBevelIndices[] = {
352 // Draw outer AA, from outer AA line to outer edge, shift is 0.
353 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
354 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
355 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
356 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
357 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
358 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
359 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
360 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
361
362 // Draw the stroke, from outer edge to inner edge, shift is 8.
363 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
364 1 + 8, 5 + 8, 9 + 8,
365 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
366 6 + 8, 2 + 8, 10 + 8,
367 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
368 3 + 8, 7 + 8, 11 + 8,
369 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
370 4 + 8, 0 + 8, 8 + 8,
371
372 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
373 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
374 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
375 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
376 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
377 };
378 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
379
380 GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
381 return resourceProvider->findOrCreateInstancedIndexBuffer(gBevelIndices,
382 kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
383 gBevelIndexBufferKey);
384 }
385}
386
joshualitt3566d442015-09-18 07:12:55 -0700387bool AAStrokeRectBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) {
388 AAStrokeRectBatch* that = t->cast<AAStrokeRectBatch>();
bsalomonabd30f52015-08-13 13:34:48 -0700389
390 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
391 that->bounds(), caps)) {
joshualitt9ff64252015-08-10 09:03:51 -0700392 return false;
393 }
394
joshualitt9ff64252015-08-10 09:03:51 -0700395 // TODO batch across miterstroke changes
396 if (this->miterStroke() != that->miterStroke()) {
397 return false;
398 }
399
400 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
401 // local coords then we won't be able to batch. We could actually upload the viewmatrix
402 // using vertex attributes in these cases, but haven't investigated that
403 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
404 return false;
405 }
406
407 // In the event of two batches, one who can tweak, one who cannot, we just fall back to
408 // not tweaking
409 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
410 fBatch.fCanTweakAlphaForCoverage = false;
411 }
412
413 if (this->color() != that->color()) {
414 fBatch.fColor = GrColor_ILLEGAL;
415 }
bsalomon8b7a9e12016-07-06 13:06:22 -0700416 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
joshualitt9ff64252015-08-10 09:03:51 -0700417 this->joinBounds(that->bounds());
418 return true;
419}
420
joshualitt11edad92015-09-22 10:32:28 -0700421static void setup_scale(int* scale, SkScalar inset) {
422 if (inset < SK_ScalarHalf) {
423 *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
424 SkASSERT(*scale >= 0 && *scale <= 255);
425 } else {
426 *scale = 0xff;
427 }
428}
429
joshualitt3566d442015-09-18 07:12:55 -0700430void AAStrokeRectBatch::generateAAStrokeRectGeometry(void* vertices,
431 size_t offset,
432 size_t vertexStride,
433 int outerVertexNum,
434 int innerVertexNum,
435 GrColor color,
436 const SkRect& devOutside,
437 const SkRect& devOutsideAssist,
438 const SkRect& devInside,
439 bool miterStroke,
joshualitt11edad92015-09-22 10:32:28 -0700440 bool degenerate,
joshualitt3566d442015-09-18 07:12:55 -0700441 bool tweakAlphaForCoverage) const {
joshualitt9ff64252015-08-10 09:03:51 -0700442 intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
443
444 // We create vertices for four nested rectangles. There are two ramps from 0 to full
445 // coverage, one on the exterior of the stroke and the other on the interior.
446 // The following pointers refer to the four rects, from outermost to innermost.
447 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
448 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
449 SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
450 SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(verts +
451 (2 * outerVertexNum + innerVertexNum) *
452 vertexStride);
453
454#ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
455 // TODO: this only really works if the X & Y margins are the same all around
456 // the rect (or if they are all >= 1.0).
joshualitt11edad92015-09-22 10:32:28 -0700457 SkScalar inset;
458 if (!degenerate) {
459 inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
460 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
461 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
462 if (miterStroke) {
463 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
464 } else {
465 inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom -
466 devInside.fBottom);
467 }
468 SkASSERT(inset >= 0);
joshualitt9ff64252015-08-10 09:03:51 -0700469 } else {
joshualitt11edad92015-09-22 10:32:28 -0700470 // TODO use real devRect here
471 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
472 inset = SK_ScalarHalf * SkMinScalar(inset, SkTMax(devOutside.height(),
473 devOutsideAssist.height()));
joshualitt9ff64252015-08-10 09:03:51 -0700474 }
joshualitt9ff64252015-08-10 09:03:51 -0700475#else
joshualitt11edad92015-09-22 10:32:28 -0700476 SkScalar inset;
477 if (!degenerate) {
478 inset = SK_ScalarHalf;
479 } else {
480 // TODO use real devRect here
481 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
482 inset = SK_ScalarHalf * SkMinScalar(inset, SkTMax(devOutside.height(),
483 devOutsideAssist.height()));
484 }
joshualitt9ff64252015-08-10 09:03:51 -0700485#endif
486
487 if (miterStroke) {
488 // outermost
489 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
490 // inner two
491 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
joshualitt11edad92015-09-22 10:32:28 -0700492 if (!degenerate) {
493 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
494 // innermost
495 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
496 } else {
497 // When the interior rect has become degenerate we smoosh to a single point
498 SkASSERT(devInside.fLeft == devInside.fRight &&
499 devInside.fTop == devInside.fBottom);
500 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop,
501 devInside.fRight, devInside.fBottom, vertexStride);
502 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop,
503 devInside.fRight, devInside.fBottom, vertexStride);
504 }
joshualitt9ff64252015-08-10 09:03:51 -0700505 } else {
506 SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
507 SkPoint* fan1AssistPos = reinterpret_cast<SkPoint*>(verts +
508 (outerVertexNum + 4) *
509 vertexStride);
510 // outermost
511 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
512 set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
513 -SK_ScalarHalf);
514 // outer one of the inner two
515 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
516 set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset);
joshualitt11edad92015-09-22 10:32:28 -0700517 if (!degenerate) {
518 // inner one of the inner two
519 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
520 // innermost
521 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
522 } else {
523 // When the interior rect has become degenerate we smoosh to a single point
524 SkASSERT(devInside.fLeft == devInside.fRight &&
525 devInside.fTop == devInside.fBottom);
526 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop,
527 devInside.fRight, devInside.fBottom, vertexStride);
528 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop,
529 devInside.fRight, devInside.fBottom, vertexStride);
530 }
joshualitt9ff64252015-08-10 09:03:51 -0700531 }
532
533 // Make verts point to vertex color and then set all the color and coverage vertex attrs
534 // values. The outermost rect has 0 coverage
535 verts += sizeof(SkPoint);
536 for (int i = 0; i < outerVertexNum; ++i) {
537 if (tweakAlphaForCoverage) {
538 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
539 } else {
540 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
541 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
542 }
543 }
544
545 // scale is the coverage for the the inner two rects.
546 int scale;
joshualitt11edad92015-09-22 10:32:28 -0700547 setup_scale(&scale, inset);
joshualitt9ff64252015-08-10 09:03:51 -0700548
549 float innerCoverage = GrNormalizeByteToFloat(scale);
550 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
551
552 verts += outerVertexNum * vertexStride;
553 for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
554 if (tweakAlphaForCoverage) {
555 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
556 } else {
557 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
joshualitt11edad92015-09-22 10:32:28 -0700558 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
joshualitt9ff64252015-08-10 09:03:51 -0700559 }
560 }
561
joshualitt11edad92015-09-22 10:32:28 -0700562 // The innermost rect has 0 coverage, unless we are degenerate, in which case we must apply the
563 // scaled coverage
joshualitt9ff64252015-08-10 09:03:51 -0700564 verts += (outerVertexNum + innerVertexNum) * vertexStride;
joshualitt11edad92015-09-22 10:32:28 -0700565 if (!degenerate) {
566 innerCoverage = 0;
567 scaledColor = 0;
568 }
569
joshualitt9ff64252015-08-10 09:03:51 -0700570 for (int i = 0; i < innerVertexNum; ++i) {
571 if (tweakAlphaForCoverage) {
joshualitt11edad92015-09-22 10:32:28 -0700572 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
joshualitt9ff64252015-08-10 09:03:51 -0700573 } else {
574 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
joshualitt11edad92015-09-22 10:32:28 -0700575 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
joshualitt9ff64252015-08-10 09:03:51 -0700576 }
577 }
578}
579
joshualitt3566d442015-09-18 07:12:55 -0700580namespace GrAAStrokeRectBatch {
581
bsalomon40ef4852016-05-02 13:22:13 -0700582GrDrawBatch* CreateFillBetweenRects(GrColor color,
583 const SkMatrix& viewMatrix,
584 const SkRect& devOutside,
585 const SkRect& devInside) {
bsalomon8b7a9e12016-07-06 13:06:22 -0700586 return new AAStrokeRectBatch(color, viewMatrix, devOutside, devInside);
joshualittaa37a962015-09-18 13:03:25 -0700587}
588
joshualitt10cae832015-09-22 12:50:33 -0700589GrDrawBatch* Create(GrColor color,
590 const SkMatrix& viewMatrix,
591 const SkRect& rect,
592 const SkStrokeRec& stroke) {
bsalomon8b7a9e12016-07-06 13:06:22 -0700593 return AAStrokeRectBatch::Create(color, viewMatrix, rect, stroke);
joshualitt10cae832015-09-22 12:50:33 -0700594}
595
bsalomon8b7a9e12016-07-06 13:06:22 -0700596}
joshualitt3566d442015-09-18 07:12:55 -0700597
joshualitt9ff64252015-08-10 09:03:51 -0700598///////////////////////////////////////////////////////////////////////////////////////////////////
599
600#ifdef GR_TEST_UTILS
601
602#include "GrBatchTest.h"
603
bsalomonabd30f52015-08-13 13:34:48 -0700604DRAW_BATCH_TEST_DEFINE(AAStrokeRectBatch) {
joshualitt9ff64252015-08-10 09:03:51 -0700605 bool miterStroke = random->nextBool();
606
bsalomon40ef4852016-05-02 13:22:13 -0700607 // Create either a empty rect or a non-empty rect.
608 SkRect rect = random->nextBool() ? SkRect::MakeXYWH(10, 10, 50, 40) :
609 SkRect::MakeXYWH(6, 7, 0, 0);
610 SkScalar minDim = SkMinScalar(rect.width(), rect.height());
611 SkScalar strokeWidth = random->nextUScalar1() * minDim;
joshualitt9ff64252015-08-10 09:03:51 -0700612
joshualitt3566d442015-09-18 07:12:55 -0700613 GrColor color = GrRandomColor(random);
joshualitt9ff64252015-08-10 09:03:51 -0700614
bsalomon40ef4852016-05-02 13:22:13 -0700615 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
616 rec.setStrokeStyle(strokeWidth);
617 rec.setStrokeParams(SkPaint::kButt_Cap,
618 miterStroke ? SkPaint::kMiter_Join : SkPaint::kBevel_Join,
619 1.f);
620 SkMatrix matrix = GrTest::TestMatrixRectStaysRect(random);
621 return GrAAStrokeRectBatch::Create(color, matrix, rect, rec);
joshualitt9ff64252015-08-10 09:03:51 -0700622}
623
624#endif