blob: 7f87ad6222d79371c99f96c4a52863da96a94a2f [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) {
caryclarkd6562002016-07-27 12:02:07 -0700127 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});
bsalomon88cf17d2016-07-08 06:40:56 -0700131 this->setBounds(devOutside, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon8b7a9e12016-07-06 13:06:22 -0700132 fMiterStroke = true;
133 }
134
bsalomon8b7a9e12016-07-06 13:06:22 -0700135 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
136 const SkStrokeRec& stroke) {
137 bool isMiter;
138 if (!allowed_stroke(stroke, &isMiter)) {
139 return nullptr;
140 }
141
142 AAStrokeRectBatch* batch = new AAStrokeRectBatch();
143 batch->fMiterStroke = isMiter;
144 Geometry& geo = batch->fGeoData.push_back();
145 compute_rects(&geo.fDevOutside, &geo.fDevOutsideAssist, &geo.fDevInside, &geo.fDegenerate,
146 viewMatrix, rect, stroke.getWidth(), isMiter);
147 geo.fColor = color;
bsalomon88cf17d2016-07-08 06:40:56 -0700148 batch->setBounds(geo.fDevOutside, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon8b7a9e12016-07-06 13:06:22 -0700149 batch->fViewMatrix = viewMatrix;
150 return batch;
joshualitt3566d442015-09-18 07:12:55 -0700151 }
152
153 const char* name() const override { return "AAStrokeRect"; }
154
Brian Salomon7c3e7182016-12-01 09:35:30 -0500155 SkString dumpInfo() const override {
156 SkString string;
157 for (const auto& geo : fGeoData) {
158 string.appendf("Color: 0x%08x, ORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
159 "AssistORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
160 "IRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], Degen: %d",
161 geo.fColor,
162 geo.fDevOutside.fLeft, geo.fDevOutside.fTop,
163 geo.fDevOutside.fRight, geo.fDevOutside.fBottom,
164 geo.fDevOutsideAssist.fLeft, geo.fDevOutsideAssist.fTop,
165 geo.fDevOutsideAssist.fRight, geo.fDevOutsideAssist.fBottom,
166 geo.fDevInside.fLeft, geo.fDevInside.fTop,
167 geo.fDevInside.fRight, geo.fDevInside.fBottom,
168 geo.fDegenerate);
169 }
170 string.append(DumpPipelineInfo(*this->pipeline()));
171 string.append(INHERITED::dumpInfo());
172 return string;
173 }
174
halcanary9d524f22016-03-29 09:03:52 -0700175 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800176 GrInitInvariantOutput* coverage,
177 GrBatchToXPOverrides* overrides) const override {
joshualitt3566d442015-09-18 07:12:55 -0700178 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800179 color->setKnownFourComponents(fGeoData[0].fColor);
180 coverage->setUnknownSingleComponent();
joshualitt3566d442015-09-18 07:12:55 -0700181 }
182
joshualittaa37a962015-09-18 13:03:25 -0700183private:
bsalomon8b7a9e12016-07-06 13:06:22 -0700184 AAStrokeRectBatch() : INHERITED(ClassID()) {}
joshualittaa37a962015-09-18 13:03:25 -0700185
joshualitt144c3c82015-11-30 12:30:13 -0800186 void onPrepareDraws(Target*) const override;
ethannicholasff210322015-11-24 12:10:10 -0800187 void initBatchTracker(const GrXPOverridesForBatch&) override;
joshualittaa37a962015-09-18 13:03:25 -0700188
joshualitt3566d442015-09-18 07:12:55 -0700189 static const int kMiterIndexCnt = 3 * 24;
190 static const int kMiterVertexCnt = 16;
191 static const int kNumMiterRectsInIndexBuffer = 256;
192
193 static const int kBevelIndexCnt = 48 + 36 + 24;
194 static const int kBevelVertexCnt = 24;
195 static const int kNumBevelRectsInIndexBuffer = 256;
196
cdalton397536c2016-03-25 12:15:03 -0700197 static const GrBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider, bool miterStroke);
joshualitt3566d442015-09-18 07:12:55 -0700198
199 GrColor color() const { return fBatch.fColor; }
200 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
201 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
202 bool colorIgnored() const { return fBatch.fColorIgnored; }
joshualitt3566d442015-09-18 07:12:55 -0700203 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
joshualittaa37a962015-09-18 13:03:25 -0700204 const SkMatrix& viewMatrix() const { return fViewMatrix; }
205 bool miterStroke() const { return fMiterStroke; }
joshualitt3566d442015-09-18 07:12:55 -0700206
207 bool onCombineIfPossible(GrBatch* t, const GrCaps&) override;
208
209 void generateAAStrokeRectGeometry(void* vertices,
210 size_t offset,
211 size_t vertexStride,
212 int outerVertexNum,
213 int innerVertexNum,
214 GrColor color,
215 const SkRect& devOutside,
216 const SkRect& devOutsideAssist,
217 const SkRect& devInside,
218 bool miterStroke,
joshualitt11edad92015-09-22 10:32:28 -0700219 bool degenerate,
joshualitt3566d442015-09-18 07:12:55 -0700220 bool tweakAlphaForCoverage) const;
221
222 struct BatchTracker {
joshualitt3566d442015-09-18 07:12:55 -0700223 GrColor fColor;
224 bool fUsesLocalCoords;
225 bool fColorIgnored;
226 bool fCoverageIgnored;
joshualitt3566d442015-09-18 07:12:55 -0700227 bool fCanTweakAlphaForCoverage;
228 };
229
bsalomon8b7a9e12016-07-06 13:06:22 -0700230 // TODO support AA rotated stroke rects by copying around view matrices
231 struct Geometry {
232 GrColor fColor;
233 SkRect fDevOutside;
234 SkRect fDevOutsideAssist;
235 SkRect fDevInside;
236 bool fDegenerate;
237 };
238
joshualitt3566d442015-09-18 07:12:55 -0700239 BatchTracker fBatch;
240 SkSTArray<1, Geometry, true> fGeoData;
joshualittaa37a962015-09-18 13:03:25 -0700241 SkMatrix fViewMatrix;
242 bool fMiterStroke;
joshualitt3566d442015-09-18 07:12:55 -0700243
244 typedef GrVertexBatch INHERITED;
245};
246
ethannicholasff210322015-11-24 12:10:10 -0800247void AAStrokeRectBatch::initBatchTracker(const GrXPOverridesForBatch& overrides) {
joshualitt9ff64252015-08-10 09:03:51 -0700248 // Handle any color overrides
ethannicholasff210322015-11-24 12:10:10 -0800249 if (!overrides.readsColor()) {
joshualitt9ff64252015-08-10 09:03:51 -0700250 fGeoData[0].fColor = GrColor_ILLEGAL;
251 }
ethannicholasff210322015-11-24 12:10:10 -0800252 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt9ff64252015-08-10 09:03:51 -0700253
254 // setup batch properties
ethannicholasff210322015-11-24 12:10:10 -0800255 fBatch.fColorIgnored = !overrides.readsColor();
joshualitt9ff64252015-08-10 09:03:51 -0700256 fBatch.fColor = fGeoData[0].fColor;
ethannicholasff210322015-11-24 12:10:10 -0800257 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
258 fBatch.fCoverageIgnored = !overrides.readsCoverage();
259 fBatch.fCanTweakAlphaForCoverage = overrides.canTweakAlphaForCoverage();
joshualitt9ff64252015-08-10 09:03:51 -0700260}
261
joshualitt144c3c82015-11-30 12:30:13 -0800262void AAStrokeRectBatch::onPrepareDraws(Target* target) const {
joshualitt9ff64252015-08-10 09:03:51 -0700263 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
264
bungeman06ca8ec2016-06-09 08:01:03 -0700265 sk_sp<GrGeometryProcessor> gp(create_stroke_rect_gp(canTweakAlphaForCoverage,
266 this->viewMatrix(),
267 this->usesLocalCoords(),
268 this->coverageIgnored()));
joshualitt9ff64252015-08-10 09:03:51 -0700269 if (!gp) {
270 SkDebugf("Couldn't create GrGeometryProcessor\n");
271 return;
272 }
273
joshualitt9ff64252015-08-10 09:03:51 -0700274 size_t vertexStride = gp->getVertexStride();
275
276 SkASSERT(canTweakAlphaForCoverage ?
277 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
278 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
279 int innerVertexNum = 4;
280 int outerVertexNum = this->miterStroke() ? 4 : 8;
281 int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
282 int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
283 int instanceCount = fGeoData.count();
284
Hal Canary144caf52016-11-07 17:57:18 -0500285 const sk_sp<const GrBuffer> indexBuffer(
bsalomon75398562015-08-17 12:55:38 -0700286 GetIndexBuffer(target->resourceProvider(), this->miterStroke()));
joshualitt9ff64252015-08-10 09:03:51 -0700287 InstancedHelper helper;
bsalomon75398562015-08-17 12:55:38 -0700288 void* vertices = helper.init(target, kTriangles_GrPrimitiveType, vertexStride,
Hal Canary144caf52016-11-07 17:57:18 -0500289 indexBuffer.get(), verticesPerInstance, indicesPerInstance,
joshualitt9ff64252015-08-10 09:03:51 -0700290 instanceCount);
291 if (!vertices || !indexBuffer) {
292 SkDebugf("Could not allocate vertices\n");
293 return;
294 }
295
296 for (int i = 0; i < instanceCount; i++) {
297 const Geometry& args = fGeoData[i];
298 this->generateAAStrokeRectGeometry(vertices,
299 i * verticesPerInstance * vertexStride,
300 vertexStride,
301 outerVertexNum,
302 innerVertexNum,
303 args.fColor,
304 args.fDevOutside,
305 args.fDevOutsideAssist,
306 args.fDevInside,
joshualittaa37a962015-09-18 13:03:25 -0700307 fMiterStroke,
joshualitt11edad92015-09-22 10:32:28 -0700308 args.fDegenerate,
joshualitt9ff64252015-08-10 09:03:51 -0700309 canTweakAlphaForCoverage);
310 }
bungeman06ca8ec2016-06-09 08:01:03 -0700311 helper.recordDraw(target, gp.get());
joshualitt9ff64252015-08-10 09:03:51 -0700312}
313
cdalton397536c2016-03-25 12:15:03 -0700314const GrBuffer* AAStrokeRectBatch::GetIndexBuffer(GrResourceProvider* resourceProvider,
315 bool miterStroke) {
joshualitt9ff64252015-08-10 09:03:51 -0700316
317 if (miterStroke) {
318 static const uint16_t gMiterIndices[] = {
319 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
320 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
321 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
322 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
323
324 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
325 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
326 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
327 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
328
329 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
330 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
331 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
332 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
333 };
334 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
335 GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
336 return resourceProvider->findOrCreateInstancedIndexBuffer(gMiterIndices,
337 kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
338 gMiterIndexBufferKey);
339 } else {
340 /**
341 * As in miter-stroke, index = a + b, and a is the current index, b is the shift
342 * from the first index. The index layout:
343 * outer AA line: 0~3, 4~7
344 * outer edge: 8~11, 12~15
345 * inner edge: 16~19
346 * inner AA line: 20~23
347 * Following comes a bevel-stroke rect and its indices:
348 *
349 * 4 7
350 * *********************************
351 * * ______________________________ *
352 * * / 12 15 \ *
353 * * / \ *
354 * 0 * |8 16_____________________19 11 | * 3
355 * * | | | | *
356 * * | | **************** | | *
357 * * | | * 20 23 * | | *
358 * * | | * * | | *
359 * * | | * 21 22 * | | *
360 * * | | **************** | | *
361 * * | |____________________| | *
362 * 1 * |9 17 18 10| * 2
363 * * \ / *
364 * * \13 __________________________14/ *
365 * * *
366 * **********************************
367 * 5 6
368 */
369 static const uint16_t gBevelIndices[] = {
370 // Draw outer AA, from outer AA line to outer edge, shift is 0.
371 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
372 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
373 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
374 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
375 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
376 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
377 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
378 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
379
380 // Draw the stroke, from outer edge to inner edge, shift is 8.
381 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
382 1 + 8, 5 + 8, 9 + 8,
383 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
384 6 + 8, 2 + 8, 10 + 8,
385 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
386 3 + 8, 7 + 8, 11 + 8,
387 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
388 4 + 8, 0 + 8, 8 + 8,
389
390 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
391 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
392 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
393 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
394 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
395 };
396 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
397
398 GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
399 return resourceProvider->findOrCreateInstancedIndexBuffer(gBevelIndices,
400 kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
401 gBevelIndexBufferKey);
402 }
403}
404
joshualitt3566d442015-09-18 07:12:55 -0700405bool AAStrokeRectBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) {
406 AAStrokeRectBatch* that = t->cast<AAStrokeRectBatch>();
bsalomonabd30f52015-08-13 13:34:48 -0700407
408 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
409 that->bounds(), caps)) {
joshualitt9ff64252015-08-10 09:03:51 -0700410 return false;
411 }
412
joshualitt9ff64252015-08-10 09:03:51 -0700413 // TODO batch across miterstroke changes
414 if (this->miterStroke() != that->miterStroke()) {
415 return false;
416 }
417
418 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
419 // local coords then we won't be able to batch. We could actually upload the viewmatrix
420 // using vertex attributes in these cases, but haven't investigated that
421 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
422 return false;
423 }
424
425 // In the event of two batches, one who can tweak, one who cannot, we just fall back to
426 // not tweaking
427 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
428 fBatch.fCanTweakAlphaForCoverage = false;
429 }
430
431 if (this->color() != that->color()) {
432 fBatch.fColor = GrColor_ILLEGAL;
433 }
bsalomon8b7a9e12016-07-06 13:06:22 -0700434 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700435 this->joinBounds(*that);
joshualitt9ff64252015-08-10 09:03:51 -0700436 return true;
437}
438
joshualitt11edad92015-09-22 10:32:28 -0700439static void setup_scale(int* scale, SkScalar inset) {
440 if (inset < SK_ScalarHalf) {
441 *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
442 SkASSERT(*scale >= 0 && *scale <= 255);
443 } else {
444 *scale = 0xff;
445 }
446}
447
joshualitt3566d442015-09-18 07:12:55 -0700448void AAStrokeRectBatch::generateAAStrokeRectGeometry(void* vertices,
449 size_t offset,
450 size_t vertexStride,
451 int outerVertexNum,
452 int innerVertexNum,
453 GrColor color,
454 const SkRect& devOutside,
455 const SkRect& devOutsideAssist,
456 const SkRect& devInside,
457 bool miterStroke,
joshualitt11edad92015-09-22 10:32:28 -0700458 bool degenerate,
joshualitt3566d442015-09-18 07:12:55 -0700459 bool tweakAlphaForCoverage) const {
joshualitt9ff64252015-08-10 09:03:51 -0700460 intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
461
462 // We create vertices for four nested rectangles. There are two ramps from 0 to full
463 // coverage, one on the exterior of the stroke and the other on the interior.
464 // The following pointers refer to the four rects, from outermost to innermost.
465 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
466 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
467 SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
468 SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(verts +
469 (2 * outerVertexNum + innerVertexNum) *
470 vertexStride);
471
472#ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
473 // TODO: this only really works if the X & Y margins are the same all around
474 // the rect (or if they are all >= 1.0).
joshualitt11edad92015-09-22 10:32:28 -0700475 SkScalar inset;
476 if (!degenerate) {
477 inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
478 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
479 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
480 if (miterStroke) {
481 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
482 } else {
483 inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom -
484 devInside.fBottom);
485 }
486 SkASSERT(inset >= 0);
joshualitt9ff64252015-08-10 09:03:51 -0700487 } else {
joshualitt11edad92015-09-22 10:32:28 -0700488 // TODO use real devRect here
489 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
490 inset = SK_ScalarHalf * SkMinScalar(inset, SkTMax(devOutside.height(),
491 devOutsideAssist.height()));
joshualitt9ff64252015-08-10 09:03:51 -0700492 }
joshualitt9ff64252015-08-10 09:03:51 -0700493#else
joshualitt11edad92015-09-22 10:32:28 -0700494 SkScalar inset;
495 if (!degenerate) {
496 inset = SK_ScalarHalf;
497 } else {
498 // TODO use real devRect here
499 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
500 inset = SK_ScalarHalf * SkMinScalar(inset, SkTMax(devOutside.height(),
501 devOutsideAssist.height()));
502 }
joshualitt9ff64252015-08-10 09:03:51 -0700503#endif
504
505 if (miterStroke) {
506 // outermost
507 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
508 // inner two
509 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
joshualitt11edad92015-09-22 10:32:28 -0700510 if (!degenerate) {
511 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
512 // innermost
513 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
514 } else {
515 // When the interior rect has become degenerate we smoosh to a single point
516 SkASSERT(devInside.fLeft == devInside.fRight &&
517 devInside.fTop == devInside.fBottom);
518 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop,
519 devInside.fRight, devInside.fBottom, vertexStride);
520 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop,
521 devInside.fRight, devInside.fBottom, vertexStride);
522 }
joshualitt9ff64252015-08-10 09:03:51 -0700523 } else {
524 SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
525 SkPoint* fan1AssistPos = reinterpret_cast<SkPoint*>(verts +
526 (outerVertexNum + 4) *
527 vertexStride);
528 // outermost
529 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
530 set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
531 -SK_ScalarHalf);
532 // outer one of the inner two
533 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
534 set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset);
joshualitt11edad92015-09-22 10:32:28 -0700535 if (!degenerate) {
536 // inner one of the inner two
537 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
538 // innermost
539 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
540 } else {
541 // When the interior rect has become degenerate we smoosh to a single point
542 SkASSERT(devInside.fLeft == devInside.fRight &&
543 devInside.fTop == devInside.fBottom);
544 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop,
545 devInside.fRight, devInside.fBottom, vertexStride);
546 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop,
547 devInside.fRight, devInside.fBottom, vertexStride);
548 }
joshualitt9ff64252015-08-10 09:03:51 -0700549 }
550
551 // Make verts point to vertex color and then set all the color and coverage vertex attrs
552 // values. The outermost rect has 0 coverage
553 verts += sizeof(SkPoint);
554 for (int i = 0; i < outerVertexNum; ++i) {
555 if (tweakAlphaForCoverage) {
556 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
557 } else {
558 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
559 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
560 }
561 }
562
563 // scale is the coverage for the the inner two rects.
564 int scale;
joshualitt11edad92015-09-22 10:32:28 -0700565 setup_scale(&scale, inset);
joshualitt9ff64252015-08-10 09:03:51 -0700566
567 float innerCoverage = GrNormalizeByteToFloat(scale);
568 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
569
570 verts += outerVertexNum * vertexStride;
571 for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
572 if (tweakAlphaForCoverage) {
573 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
574 } else {
575 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
joshualitt11edad92015-09-22 10:32:28 -0700576 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
joshualitt9ff64252015-08-10 09:03:51 -0700577 }
578 }
579
joshualitt11edad92015-09-22 10:32:28 -0700580 // The innermost rect has 0 coverage, unless we are degenerate, in which case we must apply the
581 // scaled coverage
joshualitt9ff64252015-08-10 09:03:51 -0700582 verts += (outerVertexNum + innerVertexNum) * vertexStride;
joshualitt11edad92015-09-22 10:32:28 -0700583 if (!degenerate) {
584 innerCoverage = 0;
585 scaledColor = 0;
586 }
587
joshualitt9ff64252015-08-10 09:03:51 -0700588 for (int i = 0; i < innerVertexNum; ++i) {
589 if (tweakAlphaForCoverage) {
joshualitt11edad92015-09-22 10:32:28 -0700590 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
joshualitt9ff64252015-08-10 09:03:51 -0700591 } else {
592 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
joshualitt11edad92015-09-22 10:32:28 -0700593 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
joshualitt9ff64252015-08-10 09:03:51 -0700594 }
595 }
596}
597
joshualitt3566d442015-09-18 07:12:55 -0700598namespace GrAAStrokeRectBatch {
599
bsalomon40ef4852016-05-02 13:22:13 -0700600GrDrawBatch* CreateFillBetweenRects(GrColor color,
601 const SkMatrix& viewMatrix,
602 const SkRect& devOutside,
603 const SkRect& devInside) {
bsalomon8b7a9e12016-07-06 13:06:22 -0700604 return new AAStrokeRectBatch(color, viewMatrix, devOutside, devInside);
joshualittaa37a962015-09-18 13:03:25 -0700605}
606
joshualitt10cae832015-09-22 12:50:33 -0700607GrDrawBatch* Create(GrColor color,
608 const SkMatrix& viewMatrix,
609 const SkRect& rect,
610 const SkStrokeRec& stroke) {
bsalomon8b7a9e12016-07-06 13:06:22 -0700611 return AAStrokeRectBatch::Create(color, viewMatrix, rect, stroke);
joshualitt10cae832015-09-22 12:50:33 -0700612}
613
bsalomon8b7a9e12016-07-06 13:06:22 -0700614}
joshualitt3566d442015-09-18 07:12:55 -0700615
joshualitt9ff64252015-08-10 09:03:51 -0700616///////////////////////////////////////////////////////////////////////////////////////////////////
617
618#ifdef GR_TEST_UTILS
619
620#include "GrBatchTest.h"
621
bsalomonabd30f52015-08-13 13:34:48 -0700622DRAW_BATCH_TEST_DEFINE(AAStrokeRectBatch) {
joshualitt9ff64252015-08-10 09:03:51 -0700623 bool miterStroke = random->nextBool();
624
bsalomon40ef4852016-05-02 13:22:13 -0700625 // Create either a empty rect or a non-empty rect.
626 SkRect rect = random->nextBool() ? SkRect::MakeXYWH(10, 10, 50, 40) :
627 SkRect::MakeXYWH(6, 7, 0, 0);
628 SkScalar minDim = SkMinScalar(rect.width(), rect.height());
629 SkScalar strokeWidth = random->nextUScalar1() * minDim;
joshualitt9ff64252015-08-10 09:03:51 -0700630
joshualitt3566d442015-09-18 07:12:55 -0700631 GrColor color = GrRandomColor(random);
joshualitt9ff64252015-08-10 09:03:51 -0700632
bsalomon40ef4852016-05-02 13:22:13 -0700633 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
634 rec.setStrokeStyle(strokeWidth);
635 rec.setStrokeParams(SkPaint::kButt_Cap,
636 miterStroke ? SkPaint::kMiter_Join : SkPaint::kBevel_Join,
637 1.f);
638 SkMatrix matrix = GrTest::TestMatrixRectStaysRect(random);
639 return GrAAStrokeRectBatch::Create(color, matrix, rect, rec);
joshualitt9ff64252015-08-10 09:03:51 -0700640}
641
642#endif