1 /*
2 * Copyright 2012 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 "GrAARectRenderer.h"
9 #include "GrBatch.h"
10 #include "GrBatchTarget.h"
11 #include "GrBatchTest.h"
12 #include "GrContext.h"
13 #include "GrDefaultGeoProcFactory.h"
14 #include "GrGeometryProcessor.h"
15 #include "GrInvariantOutput.h"
16 #include "GrResourceKey.h"
17 #include "GrResourceProvider.h"
18 #include "GrTestUtils.h"
19 #include "GrVertexBuffer.h"
20 #include "SkColorPriv.h"
21 #include "gl/GrGLProcessor.h"
22 #include "gl/GrGLGeometryProcessor.h"
23 #include "gl/builders/GrGLProgramBuilder.h"
24
25 ///////////////////////////////////////////////////////////////////////////////
26
set_inset_fan(SkPoint * pts,size_t stride,const SkRect & r,SkScalar dx,SkScalar dy)27 static void set_inset_fan(SkPoint* pts, size_t stride,
28 const SkRect& r, SkScalar dx, SkScalar dy) {
29 pts->setRectFan(r.fLeft + dx, r.fTop + dy,
30 r.fRight - dx, r.fBottom - dy, stride);
31 }
32
create_fill_rect_gp(bool tweakAlphaForCoverage,const SkMatrix & localMatrix)33 static const GrGeometryProcessor* create_fill_rect_gp(bool tweakAlphaForCoverage,
34 const SkMatrix& localMatrix) {
35 uint32_t flags = GrDefaultGeoProcFactory::kColor_GPType;
36 const GrGeometryProcessor* gp;
37 if (tweakAlphaForCoverage) {
38 gp = GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, SkMatrix::I(), localMatrix);
39 } else {
40 flags |= GrDefaultGeoProcFactory::kCoverage_GPType;
41 gp = GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, SkMatrix::I(), localMatrix);
42 }
43 return gp;
44 }
45
46 GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
47
48 class AAFillRectBatch : public GrBatch {
49 public:
50 struct Geometry {
51 GrColor fColor;
52 SkMatrix fViewMatrix;
53 SkRect fRect;
54 SkRect fDevRect;
55 };
56
Create(const Geometry & geometry)57 static GrBatch* Create(const Geometry& geometry) {
58 return SkNEW_ARGS(AAFillRectBatch, (geometry));
59 }
60
name() const61 const char* name() const override { return "AAFillRectBatch"; }
62
getInvariantOutputColor(GrInitInvariantOutput * out) const63 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
64 // When this is called on a batch, there is only one geometry bundle
65 out->setKnownFourComponents(fGeoData[0].fColor);
66 }
67
getInvariantOutputCoverage(GrInitInvariantOutput * out) const68 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
69 out->setUnknownSingleComponent();
70 }
71
initBatchTracker(const GrPipelineInfo & init)72 void initBatchTracker(const GrPipelineInfo& init) override {
73 // Handle any color overrides
74 if (init.fColorIgnored) {
75 fGeoData[0].fColor = GrColor_ILLEGAL;
76 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
77 fGeoData[0].fColor = init.fOverrideColor;
78 }
79
80 // setup batch properties
81 fBatch.fColorIgnored = init.fColorIgnored;
82 fBatch.fColor = fGeoData[0].fColor;
83 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
84 fBatch.fCoverageIgnored = init.fCoverageIgnored;
85 fBatch.fCanTweakAlphaForCoverage = init.fCanTweakAlphaForCoverage;
86 }
87
generateGeometry(GrBatchTarget * batchTarget,const GrPipeline * pipeline)88 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
89 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
90
91 SkMatrix localMatrix;
92 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
93 SkDebugf("Cannot invert\n");
94 return;
95 }
96
97 SkAutoTUnref<const GrGeometryProcessor> gp(create_fill_rect_gp(canTweakAlphaForCoverage,
98 localMatrix));
99
100 batchTarget->initDraw(gp, pipeline);
101
102 // TODO this is hacky, but the only way we have to initialize the GP is to use the
103 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
104 // everywhere we can remove this nastiness
105 GrPipelineInfo init;
106 init.fColorIgnored = fBatch.fColorIgnored;
107 init.fOverrideColor = GrColor_ILLEGAL;
108 init.fCoverageIgnored = fBatch.fCoverageIgnored;
109 init.fUsesLocalCoords = this->usesLocalCoords();
110 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
111
112 size_t vertexStride = gp->getVertexStride();
113 SkASSERT(canTweakAlphaForCoverage ?
114 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
115 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
116 int instanceCount = fGeoData.count();
117
118 SkAutoTUnref<const GrIndexBuffer> indexBuffer(this->getIndexBuffer(
119 batchTarget->resourceProvider()));
120 InstancedHelper helper;
121 void* vertices = helper.init(batchTarget, kTriangles_GrPrimitiveType, vertexStride,
122 indexBuffer, kVertsPerAAFillRect, kIndicesPerAAFillRect,
123 instanceCount);
124 if (!vertices || !indexBuffer) {
125 SkDebugf("Could not allocate vertices\n");
126 return;
127 }
128
129 for (int i = 0; i < instanceCount; i++) {
130 const Geometry& args = fGeoData[i];
131 this->generateAAFillRectGeometry(vertices,
132 i * kVertsPerAAFillRect * vertexStride,
133 vertexStride,
134 args.fColor,
135 args.fViewMatrix,
136 args.fRect,
137 args.fDevRect,
138 canTweakAlphaForCoverage);
139 }
140
141 helper.issueDraw(batchTarget);
142 }
143
geoData()144 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
145
146 private:
AAFillRectBatch(const Geometry & geometry)147 AAFillRectBatch(const Geometry& geometry) {
148 this->initClassID<AAFillRectBatch>();
149 fGeoData.push_back(geometry);
150
151 this->setBounds(geometry.fDevRect);
152 }
153
154 static const int kNumAAFillRectsInIndexBuffer = 256;
155 static const int kVertsPerAAFillRect = 8;
156 static const int kIndicesPerAAFillRect = 30;
157
getIndexBuffer(GrResourceProvider * resourceProvider)158 const GrIndexBuffer* getIndexBuffer(GrResourceProvider* resourceProvider) {
159 GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
160
161 static const uint16_t gFillAARectIdx[] = {
162 0, 1, 5, 5, 4, 0,
163 1, 2, 6, 6, 5, 1,
164 2, 3, 7, 7, 6, 2,
165 3, 0, 4, 4, 7, 3,
166 4, 5, 6, 6, 7, 4,
167 };
168 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
169 return resourceProvider->refOrCreateInstancedIndexBuffer(gFillAARectIdx,
170 kIndicesPerAAFillRect, kNumAAFillRectsInIndexBuffer, kVertsPerAAFillRect,
171 gAAFillRectIndexBufferKey);
172 }
173
color() const174 GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const175 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
canTweakAlphaForCoverage() const176 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
colorIgnored() const177 bool colorIgnored() const { return fBatch.fColorIgnored; }
viewMatrix() const178 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
179
onCombineIfPossible(GrBatch * t)180 bool onCombineIfPossible(GrBatch* t) override {
181 AAFillRectBatch* that = t->cast<AAFillRectBatch>();
182
183 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
184 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
185 // local coords then we won't be able to batch. We could actually upload the viewmatrix
186 // using vertex attributes in these cases, but haven't investigated that
187 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
188 return false;
189 }
190
191 if (this->color() != that->color()) {
192 fBatch.fColor = GrColor_ILLEGAL;
193 }
194
195 // In the event of two batches, one who can tweak, one who cannot, we just fall back to
196 // not tweaking
197 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
198 fBatch.fCanTweakAlphaForCoverage = false;
199 }
200
201 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
202 this->joinBounds(that->bounds());
203 return true;
204 }
205
generateAAFillRectGeometry(void * vertices,size_t offset,size_t vertexStride,GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect & devRect,bool tweakAlphaForCoverage) const206 void generateAAFillRectGeometry(void* vertices,
207 size_t offset,
208 size_t vertexStride,
209 GrColor color,
210 const SkMatrix& viewMatrix,
211 const SkRect& rect,
212 const SkRect& devRect,
213 bool tweakAlphaForCoverage) const {
214 intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
215
216 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
217 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
218
219 SkScalar inset = SkMinScalar(devRect.width(), SK_Scalar1);
220 inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height());
221
222 if (viewMatrix.rectStaysRect()) {
223 set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf);
224 set_inset_fan(fan1Pos, vertexStride, devRect, inset, inset);
225 } else {
226 // compute transformed (1, 0) and (0, 1) vectors
227 SkVector vec[2] = {
228 { viewMatrix[SkMatrix::kMScaleX], viewMatrix[SkMatrix::kMSkewY] },
229 { viewMatrix[SkMatrix::kMSkewX], viewMatrix[SkMatrix::kMScaleY] }
230 };
231
232 vec[0].normalize();
233 vec[0].scale(SK_ScalarHalf);
234 vec[1].normalize();
235 vec[1].scale(SK_ScalarHalf);
236
237 // create the rotated rect
238 fan0Pos->setRectFan(rect.fLeft, rect.fTop,
239 rect.fRight, rect.fBottom, vertexStride);
240 viewMatrix.mapPointsWithStride(fan0Pos, vertexStride, 4);
241
242 // Now create the inset points and then outset the original
243 // rotated points
244
245 // TL
246 *((SkPoint*)((intptr_t)fan1Pos + 0 * vertexStride)) =
247 *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) + vec[0] + vec[1];
248 *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) -= vec[0] + vec[1];
249 // BL
250 *((SkPoint*)((intptr_t)fan1Pos + 1 * vertexStride)) =
251 *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) + vec[0] - vec[1];
252 *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) -= vec[0] - vec[1];
253 // BR
254 *((SkPoint*)((intptr_t)fan1Pos + 2 * vertexStride)) =
255 *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) - vec[0] - vec[1];
256 *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) += vec[0] + vec[1];
257 // TR
258 *((SkPoint*)((intptr_t)fan1Pos + 3 * vertexStride)) =
259 *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) - vec[0] + vec[1];
260 *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) += vec[0] - vec[1];
261 }
262
263 // Make verts point to vertex color and then set all the color and coverage vertex attrs
264 // values.
265 verts += sizeof(SkPoint);
266 for (int i = 0; i < 4; ++i) {
267 if (tweakAlphaForCoverage) {
268 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
269 } else {
270 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
271 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
272 }
273 }
274
275 int scale;
276 if (inset < SK_ScalarHalf) {
277 scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
278 SkASSERT(scale >= 0 && scale <= 255);
279 } else {
280 scale = 0xff;
281 }
282
283 verts += 4 * vertexStride;
284
285 float innerCoverage = GrNormalizeByteToFloat(scale);
286 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
287
288 for (int i = 0; i < 4; ++i) {
289 if (tweakAlphaForCoverage) {
290 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
291 } else {
292 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
293 *reinterpret_cast<float*>(verts + i * vertexStride +
294 sizeof(GrColor)) = innerCoverage;
295 }
296 }
297 }
298
299 struct BatchTracker {
300 GrColor fColor;
301 bool fUsesLocalCoords;
302 bool fColorIgnored;
303 bool fCoverageIgnored;
304 bool fCanTweakAlphaForCoverage;
305 };
306
307 BatchTracker fBatch;
308 SkSTArray<1, Geometry, true> fGeoData;
309 };
310
311 namespace {
312 // Should the coverage be multiplied into the color attrib or use a separate attrib.
313 enum CoverageAttribType {
314 kUseColor_CoverageAttribType,
315 kUseCoverage_CoverageAttribType,
316 };
317 }
318
geometryFillAARect(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect & devRect)319 void GrAARectRenderer::geometryFillAARect(GrDrawTarget* target,
320 GrPipelineBuilder* pipelineBuilder,
321 GrColor color,
322 const SkMatrix& viewMatrix,
323 const SkRect& rect,
324 const SkRect& devRect) {
325 AAFillRectBatch::Geometry geometry;
326 geometry.fRect = rect;
327 geometry.fViewMatrix = viewMatrix;
328 geometry.fDevRect = devRect;
329 geometry.fColor = color;
330
331
332 SkAutoTUnref<GrBatch> batch(AAFillRectBatch::Create(geometry));
333 target->drawBatch(pipelineBuilder, batch);
334 }
335
strokeAARect(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect & devRect,const SkStrokeRec & stroke)336 void GrAARectRenderer::strokeAARect(GrDrawTarget* target,
337 GrPipelineBuilder* pipelineBuilder,
338 GrColor color,
339 const SkMatrix& viewMatrix,
340 const SkRect& rect,
341 const SkRect& devRect,
342 const SkStrokeRec& stroke) {
343 SkVector devStrokeSize;
344 SkScalar width = stroke.getWidth();
345 if (width > 0) {
346 devStrokeSize.set(width, width);
347 viewMatrix.mapVectors(&devStrokeSize, 1);
348 devStrokeSize.setAbs(devStrokeSize);
349 } else {
350 devStrokeSize.set(SK_Scalar1, SK_Scalar1);
351 }
352
353 const SkScalar dx = devStrokeSize.fX;
354 const SkScalar dy = devStrokeSize.fY;
355 const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf);
356 const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf);
357
358 SkScalar spare;
359 {
360 SkScalar w = devRect.width() - dx;
361 SkScalar h = devRect.height() - dy;
362 spare = SkTMin(w, h);
363 }
364
365 SkRect devOutside(devRect);
366 devOutside.outset(rx, ry);
367
368 bool miterStroke = true;
369 // For hairlines, make bevel and round joins appear the same as mitered ones.
370 // small miter limit means right angles show bevel...
371 if ((width > 0) && (stroke.getJoin() != SkPaint::kMiter_Join ||
372 stroke.getMiter() < SK_ScalarSqrt2)) {
373 miterStroke = false;
374 }
375
376 if (spare <= 0 && miterStroke) {
377 this->fillAARect(target, pipelineBuilder, color, viewMatrix, devOutside, devOutside);
378 return;
379 }
380
381 SkRect devInside(devRect);
382 devInside.inset(rx, ry);
383
384 SkRect devOutsideAssist(devRect);
385
386 // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
387 // to draw the outer of the rect. Because there are 8 vertices on the outer
388 // edge, while vertex number of inner edge is 4, the same as miter-stroke.
389 if (!miterStroke) {
390 devOutside.inset(0, ry);
391 devOutsideAssist.outset(0, ry);
392 }
393
394 this->geometryStrokeAARect(target, pipelineBuilder, color, viewMatrix, devOutside,
395 devOutsideAssist, devInside, miterStroke);
396 }
397
398 GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
399 GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
400
401 class AAStrokeRectBatch : public GrBatch {
402 public:
403 // TODO support AA rotated stroke rects by copying around view matrices
404 struct Geometry {
405 GrColor fColor;
406 SkRect fDevOutside;
407 SkRect fDevOutsideAssist;
408 SkRect fDevInside;
409 bool fMiterStroke;
410 };
411
Create(const Geometry & geometry,const SkMatrix & viewMatrix)412 static GrBatch* Create(const Geometry& geometry, const SkMatrix& viewMatrix) {
413 return SkNEW_ARGS(AAStrokeRectBatch, (geometry, viewMatrix));
414 }
415
name() const416 const char* name() const override { return "AAStrokeRect"; }
417
getInvariantOutputColor(GrInitInvariantOutput * out) const418 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
419 // When this is called on a batch, there is only one geometry bundle
420 out->setKnownFourComponents(fGeoData[0].fColor);
421 }
422
getInvariantOutputCoverage(GrInitInvariantOutput * out) const423 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
424 out->setUnknownSingleComponent();
425 }
426
initBatchTracker(const GrPipelineInfo & init)427 void initBatchTracker(const GrPipelineInfo& init) override {
428 // Handle any color overrides
429 if (init.fColorIgnored) {
430 fGeoData[0].fColor = GrColor_ILLEGAL;
431 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
432 fGeoData[0].fColor = init.fOverrideColor;
433 }
434
435 // setup batch properties
436 fBatch.fColorIgnored = init.fColorIgnored;
437 fBatch.fColor = fGeoData[0].fColor;
438 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
439 fBatch.fCoverageIgnored = init.fCoverageIgnored;
440 fBatch.fMiterStroke = fGeoData[0].fMiterStroke;
441 fBatch.fCanTweakAlphaForCoverage = init.fCanTweakAlphaForCoverage;
442 }
443
generateGeometry(GrBatchTarget * batchTarget,const GrPipeline * pipeline)444 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
445 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
446
447 // Local matrix is ignored if we don't have local coords. If we have localcoords we only
448 // batch with identical view matrices
449 SkMatrix localMatrix;
450 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
451 SkDebugf("Cannot invert\n");
452 return;
453 }
454
455 SkAutoTUnref<const GrGeometryProcessor> gp(create_fill_rect_gp(canTweakAlphaForCoverage,
456 localMatrix));
457
458 batchTarget->initDraw(gp, pipeline);
459
460 // TODO this is hacky, but the only way we have to initialize the GP is to use the
461 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
462 // everywhere we can remove this nastiness
463 GrPipelineInfo init;
464 init.fColorIgnored = fBatch.fColorIgnored;
465 init.fOverrideColor = GrColor_ILLEGAL;
466 init.fCoverageIgnored = fBatch.fCoverageIgnored;
467 init.fUsesLocalCoords = this->usesLocalCoords();
468 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
469
470 size_t vertexStride = gp->getVertexStride();
471
472 SkASSERT(canTweakAlphaForCoverage ?
473 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
474 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
475 int innerVertexNum = 4;
476 int outerVertexNum = this->miterStroke() ? 4 : 8;
477 int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
478 int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
479 int instanceCount = fGeoData.count();
480
481 const SkAutoTUnref<const GrIndexBuffer> indexBuffer(
482 GetIndexBuffer(batchTarget->resourceProvider(), this->miterStroke()));
483 InstancedHelper helper;
484 void* vertices = helper.init(batchTarget, kTriangles_GrPrimitiveType, vertexStride,
485 indexBuffer, verticesPerInstance, indicesPerInstance,
486 instanceCount);
487 if (!vertices || !indexBuffer) {
488 SkDebugf("Could not allocate vertices\n");
489 return;
490 }
491
492 for (int i = 0; i < instanceCount; i++) {
493 const Geometry& args = fGeoData[i];
494 this->generateAAStrokeRectGeometry(vertices,
495 i * verticesPerInstance * vertexStride,
496 vertexStride,
497 outerVertexNum,
498 innerVertexNum,
499 args.fColor,
500 args.fDevOutside,
501 args.fDevOutsideAssist,
502 args.fDevInside,
503 args.fMiterStroke,
504 canTweakAlphaForCoverage);
505 }
506 helper.issueDraw(batchTarget);
507 }
508
geoData()509 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
510
511 private:
AAStrokeRectBatch(const Geometry & geometry,const SkMatrix & viewMatrix)512 AAStrokeRectBatch(const Geometry& geometry, const SkMatrix& viewMatrix) {
513 this->initClassID<AAStrokeRectBatch>();
514 fBatch.fViewMatrix = viewMatrix;
515 fGeoData.push_back(geometry);
516
517 // If we have miterstroke then we inset devOutside and outset devOutsideAssist, so we need
518 // the join for proper bounds
519 fBounds = geometry.fDevOutside;
520 fBounds.join(geometry.fDevOutsideAssist);
521 }
522
523
524 static const int kMiterIndexCnt = 3 * 24;
525 static const int kMiterVertexCnt = 16;
526 static const int kNumMiterRectsInIndexBuffer = 256;
527
528 static const int kBevelIndexCnt = 48 + 36 + 24;
529 static const int kBevelVertexCnt = 24;
530 static const int kNumBevelRectsInIndexBuffer = 256;
531
GetIndexBuffer(GrResourceProvider * resourceProvider,bool miterStroke)532 static const GrIndexBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider,
533 bool miterStroke) {
534
535 if (miterStroke) {
536 static const uint16_t gMiterIndices[] = {
537 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
538 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
539 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
540 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
541
542 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
543 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
544 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
545 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
546
547 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
548 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
549 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
550 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
551 };
552 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
553 GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
554 return resourceProvider->refOrCreateInstancedIndexBuffer(gMiterIndices,
555 kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
556 gMiterIndexBufferKey);
557 } else {
558 /**
559 * As in miter-stroke, index = a + b, and a is the current index, b is the shift
560 * from the first index. The index layout:
561 * outer AA line: 0~3, 4~7
562 * outer edge: 8~11, 12~15
563 * inner edge: 16~19
564 * inner AA line: 20~23
565 * Following comes a bevel-stroke rect and its indices:
566 *
567 * 4 7
568 * *********************************
569 * * ______________________________ *
570 * * / 12 15 \ *
571 * * / \ *
572 * 0 * |8 16_____________________19 11 | * 3
573 * * | | | | *
574 * * | | **************** | | *
575 * * | | * 20 23 * | | *
576 * * | | * * | | *
577 * * | | * 21 22 * | | *
578 * * | | **************** | | *
579 * * | |____________________| | *
580 * 1 * |9 17 18 10| * 2
581 * * \ / *
582 * * \13 __________________________14/ *
583 * * *
584 * **********************************
585 * 5 6
586 */
587 static const uint16_t gBevelIndices[] = {
588 // Draw outer AA, from outer AA line to outer edge, shift is 0.
589 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
590 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
591 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
592 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
593 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
594 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
595 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
596 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
597
598 // Draw the stroke, from outer edge to inner edge, shift is 8.
599 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
600 1 + 8, 5 + 8, 9 + 8,
601 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
602 6 + 8, 2 + 8, 10 + 8,
603 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
604 3 + 8, 7 + 8, 11 + 8,
605 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
606 4 + 8, 0 + 8, 8 + 8,
607
608 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
609 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
610 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
611 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
612 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
613 };
614 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
615
616 GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
617 return resourceProvider->refOrCreateInstancedIndexBuffer(gBevelIndices,
618 kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
619 gBevelIndexBufferKey);
620 }
621 }
622
color() const623 GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const624 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
canTweakAlphaForCoverage() const625 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
colorIgnored() const626 bool colorIgnored() const { return fBatch.fColorIgnored; }
viewMatrix() const627 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
miterStroke() const628 bool miterStroke() const { return fBatch.fMiterStroke; }
629
onCombineIfPossible(GrBatch * t)630 bool onCombineIfPossible(GrBatch* t) override {
631 AAStrokeRectBatch* that = t->cast<AAStrokeRectBatch>();
632
633 // TODO batch across miterstroke changes
634 if (this->miterStroke() != that->miterStroke()) {
635 return false;
636 }
637
638 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
639 // local coords then we won't be able to batch. We could actually upload the viewmatrix
640 // using vertex attributes in these cases, but haven't investigated that
641 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
642 return false;
643 }
644
645 // In the event of two batches, one who can tweak, one who cannot, we just fall back to
646 // not tweaking
647 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
648 fBatch.fCanTweakAlphaForCoverage = false;
649 }
650
651 if (this->color() != that->color()) {
652 fBatch.fColor = GrColor_ILLEGAL;
653 }
654 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
655 this->joinBounds(that->bounds());
656 return true;
657 }
658
generateAAStrokeRectGeometry(void * vertices,size_t offset,size_t vertexStride,int outerVertexNum,int innerVertexNum,GrColor color,const SkRect & devOutside,const SkRect & devOutsideAssist,const SkRect & devInside,bool miterStroke,bool tweakAlphaForCoverage) const659 void generateAAStrokeRectGeometry(void* vertices,
660 size_t offset,
661 size_t vertexStride,
662 int outerVertexNum,
663 int innerVertexNum,
664 GrColor color,
665 const SkRect& devOutside,
666 const SkRect& devOutsideAssist,
667 const SkRect& devInside,
668 bool miterStroke,
669 bool tweakAlphaForCoverage) const {
670 intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
671
672 // We create vertices for four nested rectangles. There are two ramps from 0 to full
673 // coverage, one on the exterior of the stroke and the other on the interior.
674 // The following pointers refer to the four rects, from outermost to innermost.
675 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
676 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
677 SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
678 SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(verts +
679 (2 * outerVertexNum + innerVertexNum) *
680 vertexStride);
681
682 #ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
683 // TODO: this only really works if the X & Y margins are the same all around
684 // the rect (or if they are all >= 1.0).
685 SkScalar inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
686 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
687 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
688 if (miterStroke) {
689 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
690 } else {
691 inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom -
692 devInside.fBottom);
693 }
694 SkASSERT(inset >= 0);
695 #else
696 SkScalar inset = SK_ScalarHalf;
697 #endif
698
699 if (miterStroke) {
700 // outermost
701 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
702 // inner two
703 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
704 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
705 // innermost
706 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
707 } else {
708 SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
709 SkPoint* fan1AssistPos = reinterpret_cast<SkPoint*>(verts +
710 (outerVertexNum + 4) *
711 vertexStride);
712 // outermost
713 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
714 set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
715 -SK_ScalarHalf);
716 // outer one of the inner two
717 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
718 set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset);
719 // inner one of the inner two
720 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
721 // innermost
722 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
723 }
724
725 // Make verts point to vertex color and then set all the color and coverage vertex attrs
726 // values. The outermost rect has 0 coverage
727 verts += sizeof(SkPoint);
728 for (int i = 0; i < outerVertexNum; ++i) {
729 if (tweakAlphaForCoverage) {
730 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
731 } else {
732 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
733 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
734 }
735 }
736
737 // scale is the coverage for the the inner two rects.
738 int scale;
739 if (inset < SK_ScalarHalf) {
740 scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
741 SkASSERT(scale >= 0 && scale <= 255);
742 } else {
743 scale = 0xff;
744 }
745
746 float innerCoverage = GrNormalizeByteToFloat(scale);
747 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
748
749 verts += outerVertexNum * vertexStride;
750 for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
751 if (tweakAlphaForCoverage) {
752 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
753 } else {
754 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
755 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
756 innerCoverage;
757 }
758 }
759
760 // The innermost rect has 0 coverage
761 verts += (outerVertexNum + innerVertexNum) * vertexStride;
762 for (int i = 0; i < innerVertexNum; ++i) {
763 if (tweakAlphaForCoverage) {
764 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
765 } else {
766 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
767 *reinterpret_cast<GrColor*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
768 }
769 }
770 }
771
772 struct BatchTracker {
773 SkMatrix fViewMatrix;
774 GrColor fColor;
775 bool fUsesLocalCoords;
776 bool fColorIgnored;
777 bool fCoverageIgnored;
778 bool fMiterStroke;
779 bool fCanTweakAlphaForCoverage;
780 };
781
782 BatchTracker fBatch;
783 SkSTArray<1, Geometry, true> fGeoData;
784 };
785
geometryStrokeAARect(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,const SkRect & devOutside,const SkRect & devOutsideAssist,const SkRect & devInside,bool miterStroke)786 void GrAARectRenderer::geometryStrokeAARect(GrDrawTarget* target,
787 GrPipelineBuilder* pipelineBuilder,
788 GrColor color,
789 const SkMatrix& viewMatrix,
790 const SkRect& devOutside,
791 const SkRect& devOutsideAssist,
792 const SkRect& devInside,
793 bool miterStroke) {
794 AAStrokeRectBatch::Geometry geometry;
795 geometry.fColor = color;
796 geometry.fDevOutside = devOutside;
797 geometry.fDevOutsideAssist = devOutsideAssist;
798 geometry.fDevInside = devInside;
799 geometry.fMiterStroke = miterStroke;
800
801 SkAutoTUnref<GrBatch> batch(AAStrokeRectBatch::Create(geometry, viewMatrix));
802 target->drawBatch(pipelineBuilder, batch);
803 }
804
fillAANestedRects(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,const SkRect rects[2])805 void GrAARectRenderer::fillAANestedRects(GrDrawTarget* target,
806 GrPipelineBuilder* pipelineBuilder,
807 GrColor color,
808 const SkMatrix& viewMatrix,
809 const SkRect rects[2]) {
810 SkASSERT(viewMatrix.rectStaysRect());
811 SkASSERT(!rects[1].isEmpty());
812
813 SkRect devOutside, devInside;
814 viewMatrix.mapRect(&devOutside, rects[0]);
815 // can't call mapRect for devInside since it calls sort
816 viewMatrix.mapPoints((SkPoint*)&devInside, (const SkPoint*)&rects[1], 2);
817
818 if (devInside.isEmpty()) {
819 this->fillAARect(target, pipelineBuilder, color, viewMatrix, devOutside, devOutside);
820 return;
821 }
822
823 this->geometryStrokeAARect(target, pipelineBuilder, color, viewMatrix, devOutside,
824 devOutside, devInside, true);
825 }
826
827 ///////////////////////////////////////////////////////////////////////////////////////////////////
828
829 #ifdef GR_TEST_UTILS
830
BATCH_TEST_DEFINE(AAFillRectBatch)831 BATCH_TEST_DEFINE(AAFillRectBatch) {
832 AAFillRectBatch::Geometry geo;
833 geo.fColor = GrRandomColor(random);
834 geo.fViewMatrix = GrTest::TestMatrix(random);
835 geo.fRect = GrTest::TestRect(random);
836 geo.fDevRect = GrTest::TestRect(random);
837 return AAFillRectBatch::Create(geo);
838 }
839
BATCH_TEST_DEFINE(AAStrokeRectBatch)840 BATCH_TEST_DEFINE(AAStrokeRectBatch) {
841 bool miterStroke = random->nextBool();
842
843 // Create mock stroke rect
844 SkRect outside = GrTest::TestRect(random);
845 SkScalar minDim = SkMinScalar(outside.width(), outside.height());
846 SkScalar strokeWidth = minDim * 0.1f;
847 SkRect outsideAssist = outside;
848 outsideAssist.outset(strokeWidth, strokeWidth);
849 SkRect inside = outside;
850 inside.inset(strokeWidth, strokeWidth);
851
852 AAStrokeRectBatch::Geometry geo;
853 geo.fColor = GrRandomColor(random);
854 geo.fDevOutside = outside;
855 geo.fDevOutsideAssist = outsideAssist;
856 geo.fDevInside = inside;
857 geo.fMiterStroke = miterStroke;
858
859 return AAStrokeRectBatch::Create(geo, GrTest::TestMatrix(random));
860 }
861
862 #endif
863