blob: cc5440871fbead19c04b231d5a29168dd07890c8 [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
24static const GrGeometryProcessor* create_stroke_rect_gp(bool tweakAlphaForCoverage,
25 const SkMatrix& viewMatrix,
26 bool usesLocalCoords,
27 bool coverageIgnored) {
28 using namespace GrDefaultGeoProcFactory;
29
30 Color color(Color::kAttribute_Type);
31 Coverage::Type coverageType;
32 // TODO remove coverage if coverage is ignored
33 /*if (coverageIgnored) {
34 coverageType = Coverage::kNone_Type;
35 } else*/ if (tweakAlphaForCoverage) {
36 coverageType = Coverage::kSolid_Type;
37 } else {
38 coverageType = Coverage::kAttribute_Type;
39 }
40 Coverage coverage(coverageType);
41 LocalCoords localCoords(usesLocalCoords ? LocalCoords::kUsePosition_Type :
42 LocalCoords::kUnused_Type);
43 return CreateForDeviceSpace(color, coverage, localCoords, viewMatrix);
44}
45
joshualitt3566d442015-09-18 07:12:55 -070046class AAStrokeRectBatch : public GrVertexBatch {
47public:
48 DEFINE_BATCH_CLASS_ID
49
50 // TODO support AA rotated stroke rects by copying around view matrices
51 struct Geometry {
52 SkRect fDevOutside;
53 SkRect fDevOutsideAssist;
54 SkRect fDevInside;
55 GrColor fColor;
joshualitt11edad92015-09-22 10:32:28 -070056 bool fDegenerate;
joshualitt3566d442015-09-18 07:12:55 -070057 };
58
joshualittaa37a962015-09-18 13:03:25 -070059 static AAStrokeRectBatch* Create(const SkMatrix& viewMatrix, bool miterStroke) {
60 return new AAStrokeRectBatch(viewMatrix, miterStroke);
joshualitt3566d442015-09-18 07:12:55 -070061 }
62
63 const char* name() const override { return "AAStrokeRect"; }
64
halcanary9d524f22016-03-29 09:03:52 -070065 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -080066 GrInitInvariantOutput* coverage,
67 GrBatchToXPOverrides* overrides) const override {
joshualitt3566d442015-09-18 07:12:55 -070068 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -080069 color->setKnownFourComponents(fGeoData[0].fColor);
70 coverage->setUnknownSingleComponent();
joshualitt3566d442015-09-18 07:12:55 -070071 }
72
73 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
74
joshualittaa37a962015-09-18 13:03:25 -070075 bool canAppend(const SkMatrix& viewMatrix, bool miterStroke) {
76 return fViewMatrix.cheapEqualTo(viewMatrix) && fMiterStroke == miterStroke;
77 }
joshualitt3566d442015-09-18 07:12:55 -070078
joshualittaa37a962015-09-18 13:03:25 -070079 void append(GrColor color, const SkRect& devOutside, const SkRect& devOutsideAssist,
joshualitt11edad92015-09-22 10:32:28 -070080 const SkRect& devInside, bool degenerate) {
joshualitt3566d442015-09-18 07:12:55 -070081 Geometry& geometry = fGeoData.push_back();
82 geometry.fColor = color;
83 geometry.fDevOutside = devOutside;
84 geometry.fDevOutsideAssist = devOutsideAssist;
85 geometry.fDevInside = devInside;
joshualitt11edad92015-09-22 10:32:28 -070086 geometry.fDegenerate = degenerate;
joshualitt3566d442015-09-18 07:12:55 -070087 }
88
joshualittaa37a962015-09-18 13:03:25 -070089 void appendAndUpdateBounds(GrColor color, const SkRect& devOutside,
joshualitt11edad92015-09-22 10:32:28 -070090 const SkRect& devOutsideAssist, const SkRect& devInside,
91 bool degenerate) {
92 this->append(color, devOutside, devOutsideAssist, devInside, degenerate);
joshualittaa37a962015-09-18 13:03:25 -070093
94 SkRect bounds;
95 this->updateBounds(&bounds, fGeoData.back());
96 this->joinBounds(bounds);
97 }
98
99 void init() { this->updateBounds(&fBounds, fGeoData[0]); }
100
101private:
102 void updateBounds(SkRect* bounds, const Geometry& geo) {
103 // If we have miterstroke then we inset devOutside and outset devOutsideAssist, so we need
104 // the join for proper bounds
105 *bounds = geo.fDevOutside;
106 bounds->join(geo.fDevOutsideAssist);
107 }
108
joshualitt144c3c82015-11-30 12:30:13 -0800109 void onPrepareDraws(Target*) const override;
ethannicholasff210322015-11-24 12:10:10 -0800110 void initBatchTracker(const GrXPOverridesForBatch&) override;
joshualittaa37a962015-09-18 13:03:25 -0700111
112 AAStrokeRectBatch(const SkMatrix& viewMatrix,bool miterStroke)
113 : INHERITED(ClassID()) {
114 fViewMatrix = viewMatrix;
115 fMiterStroke = miterStroke;
116 }
joshualitt3566d442015-09-18 07:12:55 -0700117
118 static const int kMiterIndexCnt = 3 * 24;
119 static const int kMiterVertexCnt = 16;
120 static const int kNumMiterRectsInIndexBuffer = 256;
121
122 static const int kBevelIndexCnt = 48 + 36 + 24;
123 static const int kBevelVertexCnt = 24;
124 static const int kNumBevelRectsInIndexBuffer = 256;
125
cdalton397536c2016-03-25 12:15:03 -0700126 static const GrBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider, bool miterStroke);
joshualitt3566d442015-09-18 07:12:55 -0700127
128 GrColor color() const { return fBatch.fColor; }
129 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
130 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
131 bool colorIgnored() const { return fBatch.fColorIgnored; }
joshualitt3566d442015-09-18 07:12:55 -0700132 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
joshualittaa37a962015-09-18 13:03:25 -0700133 const Geometry& geometry() const { return fGeoData[0]; }
134 const SkMatrix& viewMatrix() const { return fViewMatrix; }
135 bool miterStroke() const { return fMiterStroke; }
joshualitt3566d442015-09-18 07:12:55 -0700136
137 bool onCombineIfPossible(GrBatch* t, const GrCaps&) override;
138
139 void generateAAStrokeRectGeometry(void* vertices,
140 size_t offset,
141 size_t vertexStride,
142 int outerVertexNum,
143 int innerVertexNum,
144 GrColor color,
145 const SkRect& devOutside,
146 const SkRect& devOutsideAssist,
147 const SkRect& devInside,
148 bool miterStroke,
joshualitt11edad92015-09-22 10:32:28 -0700149 bool degenerate,
joshualitt3566d442015-09-18 07:12:55 -0700150 bool tweakAlphaForCoverage) const;
151
152 struct BatchTracker {
joshualitt3566d442015-09-18 07:12:55 -0700153 GrColor fColor;
154 bool fUsesLocalCoords;
155 bool fColorIgnored;
156 bool fCoverageIgnored;
joshualitt3566d442015-09-18 07:12:55 -0700157 bool fCanTweakAlphaForCoverage;
158 };
159
160 BatchTracker fBatch;
161 SkSTArray<1, Geometry, true> fGeoData;
joshualittaa37a962015-09-18 13:03:25 -0700162 SkMatrix fViewMatrix;
163 bool fMiterStroke;
joshualitt3566d442015-09-18 07:12:55 -0700164
165 typedef GrVertexBatch INHERITED;
166};
167
ethannicholasff210322015-11-24 12:10:10 -0800168void AAStrokeRectBatch::initBatchTracker(const GrXPOverridesForBatch& overrides) {
joshualitt9ff64252015-08-10 09:03:51 -0700169 // Handle any color overrides
ethannicholasff210322015-11-24 12:10:10 -0800170 if (!overrides.readsColor()) {
joshualitt9ff64252015-08-10 09:03:51 -0700171 fGeoData[0].fColor = GrColor_ILLEGAL;
172 }
ethannicholasff210322015-11-24 12:10:10 -0800173 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt9ff64252015-08-10 09:03:51 -0700174
175 // setup batch properties
ethannicholasff210322015-11-24 12:10:10 -0800176 fBatch.fColorIgnored = !overrides.readsColor();
joshualitt9ff64252015-08-10 09:03:51 -0700177 fBatch.fColor = fGeoData[0].fColor;
ethannicholasff210322015-11-24 12:10:10 -0800178 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
179 fBatch.fCoverageIgnored = !overrides.readsCoverage();
180 fBatch.fCanTweakAlphaForCoverage = overrides.canTweakAlphaForCoverage();
joshualitt9ff64252015-08-10 09:03:51 -0700181}
182
joshualitt144c3c82015-11-30 12:30:13 -0800183void AAStrokeRectBatch::onPrepareDraws(Target* target) const {
joshualitt9ff64252015-08-10 09:03:51 -0700184 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
185
186 SkAutoTUnref<const GrGeometryProcessor> gp(create_stroke_rect_gp(canTweakAlphaForCoverage,
187 this->viewMatrix(),
188 this->usesLocalCoords(),
189 this->coverageIgnored()));
190 if (!gp) {
191 SkDebugf("Couldn't create GrGeometryProcessor\n");
192 return;
193 }
194
joshualitt9ff64252015-08-10 09:03:51 -0700195 size_t vertexStride = gp->getVertexStride();
196
197 SkASSERT(canTweakAlphaForCoverage ?
198 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
199 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
200 int innerVertexNum = 4;
201 int outerVertexNum = this->miterStroke() ? 4 : 8;
202 int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
203 int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
204 int instanceCount = fGeoData.count();
205
cdalton397536c2016-03-25 12:15:03 -0700206 const SkAutoTUnref<const GrBuffer> indexBuffer(
bsalomon75398562015-08-17 12:55:38 -0700207 GetIndexBuffer(target->resourceProvider(), this->miterStroke()));
joshualitt9ff64252015-08-10 09:03:51 -0700208 InstancedHelper helper;
bsalomon75398562015-08-17 12:55:38 -0700209 void* vertices = helper.init(target, kTriangles_GrPrimitiveType, vertexStride,
egdaniel0e1853c2016-03-17 11:35:45 -0700210 indexBuffer, verticesPerInstance, indicesPerInstance,
joshualitt9ff64252015-08-10 09:03:51 -0700211 instanceCount);
212 if (!vertices || !indexBuffer) {
213 SkDebugf("Could not allocate vertices\n");
214 return;
215 }
216
217 for (int i = 0; i < instanceCount; i++) {
218 const Geometry& args = fGeoData[i];
219 this->generateAAStrokeRectGeometry(vertices,
220 i * verticesPerInstance * vertexStride,
221 vertexStride,
222 outerVertexNum,
223 innerVertexNum,
224 args.fColor,
225 args.fDevOutside,
226 args.fDevOutsideAssist,
227 args.fDevInside,
joshualittaa37a962015-09-18 13:03:25 -0700228 fMiterStroke,
joshualitt11edad92015-09-22 10:32:28 -0700229 args.fDegenerate,
joshualitt9ff64252015-08-10 09:03:51 -0700230 canTweakAlphaForCoverage);
231 }
bsalomon342bfc22016-04-01 06:06:20 -0700232 helper.recordDraw(target, gp);
joshualitt9ff64252015-08-10 09:03:51 -0700233}
234
cdalton397536c2016-03-25 12:15:03 -0700235const GrBuffer* AAStrokeRectBatch::GetIndexBuffer(GrResourceProvider* resourceProvider,
236 bool miterStroke) {
joshualitt9ff64252015-08-10 09:03:51 -0700237
238 if (miterStroke) {
239 static const uint16_t gMiterIndices[] = {
240 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
241 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
242 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
243 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
244
245 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
246 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
247 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
248 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
249
250 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
251 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
252 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
253 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
254 };
255 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
256 GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
257 return resourceProvider->findOrCreateInstancedIndexBuffer(gMiterIndices,
258 kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
259 gMiterIndexBufferKey);
260 } else {
261 /**
262 * As in miter-stroke, index = a + b, and a is the current index, b is the shift
263 * from the first index. The index layout:
264 * outer AA line: 0~3, 4~7
265 * outer edge: 8~11, 12~15
266 * inner edge: 16~19
267 * inner AA line: 20~23
268 * Following comes a bevel-stroke rect and its indices:
269 *
270 * 4 7
271 * *********************************
272 * * ______________________________ *
273 * * / 12 15 \ *
274 * * / \ *
275 * 0 * |8 16_____________________19 11 | * 3
276 * * | | | | *
277 * * | | **************** | | *
278 * * | | * 20 23 * | | *
279 * * | | * * | | *
280 * * | | * 21 22 * | | *
281 * * | | **************** | | *
282 * * | |____________________| | *
283 * 1 * |9 17 18 10| * 2
284 * * \ / *
285 * * \13 __________________________14/ *
286 * * *
287 * **********************************
288 * 5 6
289 */
290 static const uint16_t gBevelIndices[] = {
291 // Draw outer AA, from outer AA line to outer edge, shift is 0.
292 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
293 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
294 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
295 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
296 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
297 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
298 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
299 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
300
301 // Draw the stroke, from outer edge to inner edge, shift is 8.
302 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
303 1 + 8, 5 + 8, 9 + 8,
304 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
305 6 + 8, 2 + 8, 10 + 8,
306 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
307 3 + 8, 7 + 8, 11 + 8,
308 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
309 4 + 8, 0 + 8, 8 + 8,
310
311 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
312 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
313 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
314 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
315 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
316 };
317 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
318
319 GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
320 return resourceProvider->findOrCreateInstancedIndexBuffer(gBevelIndices,
321 kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
322 gBevelIndexBufferKey);
323 }
324}
325
joshualitt3566d442015-09-18 07:12:55 -0700326bool AAStrokeRectBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) {
327 AAStrokeRectBatch* that = t->cast<AAStrokeRectBatch>();
bsalomonabd30f52015-08-13 13:34:48 -0700328
329 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
330 that->bounds(), caps)) {
joshualitt9ff64252015-08-10 09:03:51 -0700331 return false;
332 }
333
joshualitt9ff64252015-08-10 09:03:51 -0700334 // TODO batch across miterstroke changes
335 if (this->miterStroke() != that->miterStroke()) {
336 return false;
337 }
338
339 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
340 // local coords then we won't be able to batch. We could actually upload the viewmatrix
341 // using vertex attributes in these cases, but haven't investigated that
342 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
343 return false;
344 }
345
346 // In the event of two batches, one who can tweak, one who cannot, we just fall back to
347 // not tweaking
348 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
349 fBatch.fCanTweakAlphaForCoverage = false;
350 }
351
352 if (this->color() != that->color()) {
353 fBatch.fColor = GrColor_ILLEGAL;
354 }
355 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
356 this->joinBounds(that->bounds());
357 return true;
358}
359
joshualitt11edad92015-09-22 10:32:28 -0700360static void setup_scale(int* scale, SkScalar inset) {
361 if (inset < SK_ScalarHalf) {
362 *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
363 SkASSERT(*scale >= 0 && *scale <= 255);
364 } else {
365 *scale = 0xff;
366 }
367}
368
joshualitt3566d442015-09-18 07:12:55 -0700369void AAStrokeRectBatch::generateAAStrokeRectGeometry(void* vertices,
370 size_t offset,
371 size_t vertexStride,
372 int outerVertexNum,
373 int innerVertexNum,
374 GrColor color,
375 const SkRect& devOutside,
376 const SkRect& devOutsideAssist,
377 const SkRect& devInside,
378 bool miterStroke,
joshualitt11edad92015-09-22 10:32:28 -0700379 bool degenerate,
joshualitt3566d442015-09-18 07:12:55 -0700380 bool tweakAlphaForCoverage) const {
joshualitt9ff64252015-08-10 09:03:51 -0700381 intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
382
383 // We create vertices for four nested rectangles. There are two ramps from 0 to full
384 // coverage, one on the exterior of the stroke and the other on the interior.
385 // The following pointers refer to the four rects, from outermost to innermost.
386 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
387 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
388 SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
389 SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(verts +
390 (2 * outerVertexNum + innerVertexNum) *
391 vertexStride);
392
393#ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
394 // TODO: this only really works if the X & Y margins are the same all around
395 // the rect (or if they are all >= 1.0).
joshualitt11edad92015-09-22 10:32:28 -0700396 SkScalar inset;
397 if (!degenerate) {
398 inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
399 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
400 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
401 if (miterStroke) {
402 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
403 } else {
404 inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom -
405 devInside.fBottom);
406 }
407 SkASSERT(inset >= 0);
joshualitt9ff64252015-08-10 09:03:51 -0700408 } else {
joshualitt11edad92015-09-22 10:32:28 -0700409 // TODO use real devRect here
410 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
411 inset = SK_ScalarHalf * SkMinScalar(inset, SkTMax(devOutside.height(),
412 devOutsideAssist.height()));
joshualitt9ff64252015-08-10 09:03:51 -0700413 }
joshualitt9ff64252015-08-10 09:03:51 -0700414#else
joshualitt11edad92015-09-22 10:32:28 -0700415 SkScalar inset;
416 if (!degenerate) {
417 inset = SK_ScalarHalf;
418 } else {
419 // TODO use real devRect here
420 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
421 inset = SK_ScalarHalf * SkMinScalar(inset, SkTMax(devOutside.height(),
422 devOutsideAssist.height()));
423 }
joshualitt9ff64252015-08-10 09:03:51 -0700424#endif
425
426 if (miterStroke) {
427 // outermost
428 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
429 // inner two
430 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
joshualitt11edad92015-09-22 10:32:28 -0700431 if (!degenerate) {
432 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
433 // innermost
434 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
435 } else {
436 // When the interior rect has become degenerate we smoosh to a single point
437 SkASSERT(devInside.fLeft == devInside.fRight &&
438 devInside.fTop == devInside.fBottom);
439 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop,
440 devInside.fRight, devInside.fBottom, vertexStride);
441 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop,
442 devInside.fRight, devInside.fBottom, vertexStride);
443 }
joshualitt9ff64252015-08-10 09:03:51 -0700444 } else {
445 SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
446 SkPoint* fan1AssistPos = reinterpret_cast<SkPoint*>(verts +
447 (outerVertexNum + 4) *
448 vertexStride);
449 // outermost
450 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
451 set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
452 -SK_ScalarHalf);
453 // outer one of the inner two
454 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
455 set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset);
joshualitt11edad92015-09-22 10:32:28 -0700456 if (!degenerate) {
457 // inner one of the inner two
458 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
459 // innermost
460 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
461 } else {
462 // When the interior rect has become degenerate we smoosh to a single point
463 SkASSERT(devInside.fLeft == devInside.fRight &&
464 devInside.fTop == devInside.fBottom);
465 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop,
466 devInside.fRight, devInside.fBottom, vertexStride);
467 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop,
468 devInside.fRight, devInside.fBottom, vertexStride);
469 }
joshualitt9ff64252015-08-10 09:03:51 -0700470 }
471
472 // Make verts point to vertex color and then set all the color and coverage vertex attrs
473 // values. The outermost rect has 0 coverage
474 verts += sizeof(SkPoint);
475 for (int i = 0; i < outerVertexNum; ++i) {
476 if (tweakAlphaForCoverage) {
477 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
478 } else {
479 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
480 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
481 }
482 }
483
484 // scale is the coverage for the the inner two rects.
485 int scale;
joshualitt11edad92015-09-22 10:32:28 -0700486 setup_scale(&scale, inset);
joshualitt9ff64252015-08-10 09:03:51 -0700487
488 float innerCoverage = GrNormalizeByteToFloat(scale);
489 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
490
491 verts += outerVertexNum * vertexStride;
492 for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
493 if (tweakAlphaForCoverage) {
494 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
495 } else {
496 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
joshualitt11edad92015-09-22 10:32:28 -0700497 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
joshualitt9ff64252015-08-10 09:03:51 -0700498 }
499 }
500
joshualitt11edad92015-09-22 10:32:28 -0700501 // The innermost rect has 0 coverage, unless we are degenerate, in which case we must apply the
502 // scaled coverage
joshualitt9ff64252015-08-10 09:03:51 -0700503 verts += (outerVertexNum + innerVertexNum) * vertexStride;
joshualitt11edad92015-09-22 10:32:28 -0700504 if (!degenerate) {
505 innerCoverage = 0;
506 scaledColor = 0;
507 }
508
joshualitt9ff64252015-08-10 09:03:51 -0700509 for (int i = 0; i < innerVertexNum; ++i) {
510 if (tweakAlphaForCoverage) {
joshualitt11edad92015-09-22 10:32:28 -0700511 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
joshualitt9ff64252015-08-10 09:03:51 -0700512 } else {
513 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
joshualitt11edad92015-09-22 10:32:28 -0700514 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
joshualitt9ff64252015-08-10 09:03:51 -0700515 }
516 }
517}
518
joshualitt10cae832015-09-22 12:50:33 -0700519inline static bool is_miter(const SkStrokeRec& stroke) {
520 // For hairlines, make bevel and round joins appear the same as mitered ones.
521 // small miter limit means right angles show bevel...
522 if ((stroke.getWidth() > 0) && (stroke.getJoin() != SkPaint::kMiter_Join ||
523 stroke.getMiter() < SK_ScalarSqrt2)) {
524 return false;
525 }
526 return true;
527}
528
529static void compute_rects(SkRect* devOutside, SkRect* devOutsideAssist, SkRect* devInside,
530 bool* isDegenerate, const SkMatrix& viewMatrix, const SkRect& rect,
531 SkScalar strokeWidth, bool miterStroke) {
532 SkRect devRect;
533 viewMatrix.mapRect(&devRect, rect);
534
535 SkVector devStrokeSize;
536 if (strokeWidth > 0) {
537 devStrokeSize.set(strokeWidth, strokeWidth);
538 viewMatrix.mapVectors(&devStrokeSize, 1);
539 devStrokeSize.setAbs(devStrokeSize);
540 } else {
541 devStrokeSize.set(SK_Scalar1, SK_Scalar1);
542 }
543
544 const SkScalar dx = devStrokeSize.fX;
545 const SkScalar dy = devStrokeSize.fY;
546 const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf);
547 const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf);
548
549 *devOutside = devRect;
550 *devOutsideAssist = devRect;
551 *devInside = devRect;
552
553 devOutside->outset(rx, ry);
554 devInside->inset(rx, ry);
555
556 // If we have a degenerate stroking rect(ie the stroke is larger than inner rect) then we
557 // make a degenerate inside rect to avoid double hitting. We will also jam all of the points
558 // together when we render these rects.
559 SkScalar spare;
560 {
561 SkScalar w = devRect.width() - dx;
562 SkScalar h = devRect.height() - dy;
563 spare = SkTMin(w, h);
564 }
565
566 *isDegenerate = spare <= 0;
567 if (*isDegenerate) {
568 devInside->fLeft = devInside->fRight = devRect.centerX();
569 devInside->fTop = devInside->fBottom = devRect.centerY();
570 }
571
572 // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
573 // to draw the outside of the octagon. Because there are 8 vertices on the outer
574 // edge, while vertex number of inner edge is 4, the same as miter-stroke.
575 if (!miterStroke) {
576 devOutside->inset(0, ry);
577 devOutsideAssist->outset(0, ry);
578 }
579}
580
joshualitt3566d442015-09-18 07:12:55 -0700581namespace GrAAStrokeRectBatch {
582
bsalomon40ef4852016-05-02 13:22:13 -0700583GrDrawBatch* CreateFillBetweenRects(GrColor color,
584 const SkMatrix& viewMatrix,
585 const SkRect& devOutside,
586 const SkRect& devInside) {
587 SkASSERT(!devOutside.isEmpty())
588 SkASSERT(!devInside.isEmpty())
589 AAStrokeRectBatch* batch = AAStrokeRectBatch::Create(viewMatrix, true);
590 batch->append(color, devOutside, devOutside, devInside, false);
joshualittaa37a962015-09-18 13:03:25 -0700591 batch->init();
592 return batch;
593}
594
joshualitt10cae832015-09-22 12:50:33 -0700595GrDrawBatch* Create(GrColor color,
596 const SkMatrix& viewMatrix,
597 const SkRect& rect,
598 const SkStrokeRec& stroke) {
599 bool isMiterStroke = is_miter(stroke);
600 AAStrokeRectBatch* batch = AAStrokeRectBatch::Create(viewMatrix, isMiterStroke);
601
602 SkRect devOutside, devOutsideAssist, devInside;
603 bool isDegenerate;
604 compute_rects(&devOutside, &devOutsideAssist, &devInside, &isDegenerate, viewMatrix,
605 rect, stroke.getWidth(), isMiterStroke);
606
607 batch->append(color, devOutside, devOutsideAssist, devInside, isDegenerate);
608 batch->init();
609 return batch;
610}
611
joshualittaa37a962015-09-18 13:03:25 -0700612bool Append(GrBatch* origBatch,
613 GrColor color,
614 const SkMatrix& viewMatrix,
joshualitt10cae832015-09-22 12:50:33 -0700615 const SkRect& rect,
616 const SkStrokeRec& stroke) {
joshualittaa37a962015-09-18 13:03:25 -0700617 AAStrokeRectBatch* batch = origBatch->cast<AAStrokeRectBatch>();
618
619 // we can't batch across vm changes
joshualitt10cae832015-09-22 12:50:33 -0700620 bool isMiterStroke = is_miter(stroke);
621 if (!batch->canAppend(viewMatrix, isMiterStroke)) {
joshualittaa37a962015-09-18 13:03:25 -0700622 return false;
623 }
624
joshualitt10cae832015-09-22 12:50:33 -0700625 SkRect devOutside, devOutsideAssist, devInside;
626 bool isDegenerate;
627 compute_rects(&devOutside, &devOutsideAssist, &devInside, &isDegenerate, viewMatrix,
628 rect, stroke.getWidth(), isMiterStroke);
629
630 batch->appendAndUpdateBounds(color, devOutside, devOutsideAssist, devInside, isDegenerate);
joshualittaa37a962015-09-18 13:03:25 -0700631 return true;
joshualitt3566d442015-09-18 07:12:55 -0700632}
633
634};
635
joshualitt9ff64252015-08-10 09:03:51 -0700636///////////////////////////////////////////////////////////////////////////////////////////////////
637
638#ifdef GR_TEST_UTILS
639
640#include "GrBatchTest.h"
641
bsalomonabd30f52015-08-13 13:34:48 -0700642DRAW_BATCH_TEST_DEFINE(AAStrokeRectBatch) {
joshualitt9ff64252015-08-10 09:03:51 -0700643 bool miterStroke = random->nextBool();
644
bsalomon40ef4852016-05-02 13:22:13 -0700645 // Create either a empty rect or a non-empty rect.
646 SkRect rect = random->nextBool() ? SkRect::MakeXYWH(10, 10, 50, 40) :
647 SkRect::MakeXYWH(6, 7, 0, 0);
648 SkScalar minDim = SkMinScalar(rect.width(), rect.height());
649 SkScalar strokeWidth = random->nextUScalar1() * minDim;
joshualitt9ff64252015-08-10 09:03:51 -0700650
joshualitt3566d442015-09-18 07:12:55 -0700651 GrColor color = GrRandomColor(random);
joshualitt9ff64252015-08-10 09:03:51 -0700652
bsalomon40ef4852016-05-02 13:22:13 -0700653 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
654 rec.setStrokeStyle(strokeWidth);
655 rec.setStrokeParams(SkPaint::kButt_Cap,
656 miterStroke ? SkPaint::kMiter_Join : SkPaint::kBevel_Join,
657 1.f);
658 SkMatrix matrix = GrTest::TestMatrixRectStaysRect(random);
659 return GrAAStrokeRectBatch::Create(color, matrix, rect, rec);
joshualitt9ff64252015-08-10 09:03:51 -0700660}
661
662#endif