2 * Copyright 2012 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "GrAARectRenderer.h"
10 #include "GrBatchTarget.h"
11 #include "GrBufferAllocPool.h"
12 #include "GrDefaultGeoProcFactory.h"
13 #include "GrGeometryProcessor.h"
15 #include "GrInvariantOutput.h"
16 #include "SkColorPriv.h"
17 #include "gl/GrGLProcessor.h"
18 #include "gl/GrGLGeometryProcessor.h"
19 #include "gl/builders/GrGLProgramBuilder.h"
21 ///////////////////////////////////////////////////////////////////////////////
23 static void set_inset_fan(SkPoint* pts, size_t stride,
24 const SkRect& r, SkScalar dx, SkScalar dy) {
25 pts->setRectFan(r.fLeft + dx, r.fTop + dy,
26 r.fRight - dx, r.fBottom - dy, stride);
29 static const uint16_t gFillAARectIdx[] = {
37 static const int kIndicesPerAAFillRect = SK_ARRAY_COUNT(gFillAARectIdx);
38 static const int kVertsPerAAFillRect = 8;
39 static const int kNumAAFillRectsInIndexBuffer = 256;
41 static const GrGeometryProcessor* create_fill_rect_gp(bool tweakAlphaForCoverage,
42 const SkMatrix& localMatrix) {
43 uint32_t flags = GrDefaultGeoProcFactory::kColor_GPType;
44 const GrGeometryProcessor* gp;
45 if (tweakAlphaForCoverage) {
46 gp = GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, SkMatrix::I(), localMatrix,
49 flags |= GrDefaultGeoProcFactory::kCoverage_GPType;
50 gp = GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, SkMatrix::I(), localMatrix,
56 class AAFillRectBatch : public GrBatch {
65 static GrBatch* Create(const Geometry& geometry, const GrIndexBuffer* indexBuffer) {
66 return SkNEW_ARGS(AAFillRectBatch, (geometry, indexBuffer));
69 const char* name() const SK_OVERRIDE { return "AAFillRectBatch"; }
71 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
72 // When this is called on a batch, there is only one geometry bundle
73 out->setKnownFourComponents(fGeoData[0].fColor);
76 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
77 out->setUnknownSingleComponent();
80 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
81 // Handle any color overrides
82 if (init.fColorIgnored) {
83 fGeoData[0].fColor = GrColor_ILLEGAL;
84 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
85 fGeoData[0].fColor = init.fOverrideColor;
88 // setup batch properties
89 fBatch.fColorIgnored = init.fColorIgnored;
90 fBatch.fColor = fGeoData[0].fColor;
91 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
92 fBatch.fCoverageIgnored = init.fCoverageIgnored;
93 fBatch.fCanTweakAlphaForCoverage = init.fCanTweakAlphaForCoverage;
96 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
97 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
100 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
101 SkDebugf("Cannot invert\n");
105 SkAutoTUnref<const GrGeometryProcessor> gp(create_fill_rect_gp(canTweakAlphaForCoverage,
108 batchTarget->initDraw(gp, pipeline);
110 // TODO this is hacky, but the only way we have to initialize the GP is to use the
111 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
112 // everywhere we can remove this nastiness
114 init.fColorIgnored = fBatch.fColorIgnored;
115 init.fOverrideColor = GrColor_ILLEGAL;
116 init.fCoverageIgnored = fBatch.fCoverageIgnored;
117 init.fUsesLocalCoords = this->usesLocalCoords();
118 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
120 size_t vertexStride = gp->getVertexStride();
122 SkASSERT(canTweakAlphaForCoverage ?
123 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
124 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
126 int instanceCount = fGeoData.count();
127 int vertexCount = kVertsPerAAFillRect * instanceCount;
129 const GrVertexBuffer* vertexBuffer;
132 void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
138 SkDebugf("Could not allocate vertices\n");
142 for (int i = 0; i < instanceCount; i++) {
143 const Geometry& args = fGeoData[i];
144 this->generateAAFillRectGeometry(vertices,
145 i * kVertsPerAAFillRect * vertexStride,
151 canTweakAlphaForCoverage);
154 GrDrawTarget::DrawInfo drawInfo;
155 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
156 drawInfo.setStartVertex(0);
157 drawInfo.setStartIndex(0);
158 drawInfo.setVerticesPerInstance(kVertsPerAAFillRect);
159 drawInfo.setIndicesPerInstance(kIndicesPerAAFillRect);
160 drawInfo.adjustStartVertex(firstVertex);
161 drawInfo.setVertexBuffer(vertexBuffer);
162 drawInfo.setIndexBuffer(fIndexBuffer);
164 int maxInstancesPerDraw = kNumAAFillRectsInIndexBuffer;
166 while (instanceCount) {
167 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
168 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
169 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
171 batchTarget->draw(drawInfo);
173 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
174 instanceCount -= drawInfo.instanceCount();
178 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
181 AAFillRectBatch(const Geometry& geometry, const GrIndexBuffer* indexBuffer)
182 : fIndexBuffer(indexBuffer) {
183 this->initClassID<AAFillRectBatch>();
184 fGeoData.push_back(geometry);
187 GrColor color() const { return fBatch.fColor; }
188 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
189 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
190 bool colorIgnored() const { return fBatch.fColorIgnored; }
191 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
193 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
194 AAFillRectBatch* that = t->cast<AAFillRectBatch>();
196 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
197 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
198 // local coords then we won't be able to batch. We could actually upload the viewmatrix
199 // using vertex attributes in these cases, but haven't investigated that
200 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
204 if (this->color() != that->color()) {
205 fBatch.fColor = GrColor_ILLEGAL;
208 // In the event of two batches, one who can tweak, one who cannot, we just fall back to
210 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
211 fBatch.fCanTweakAlphaForCoverage = false;
214 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
218 void generateAAFillRectGeometry(void* vertices,
222 const SkMatrix& viewMatrix,
224 const SkRect& devRect,
225 bool tweakAlphaForCoverage) const {
226 intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
228 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
229 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
231 SkScalar inset = SkMinScalar(devRect.width(), SK_Scalar1);
232 inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height());
234 if (viewMatrix.rectStaysRect()) {
235 set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf);
236 set_inset_fan(fan1Pos, vertexStride, devRect, inset, inset);
238 // compute transformed (1, 0) and (0, 1) vectors
240 { viewMatrix[SkMatrix::kMScaleX], viewMatrix[SkMatrix::kMSkewY] },
241 { viewMatrix[SkMatrix::kMSkewX], viewMatrix[SkMatrix::kMScaleY] }
245 vec[0].scale(SK_ScalarHalf);
247 vec[1].scale(SK_ScalarHalf);
249 // create the rotated rect
250 fan0Pos->setRectFan(rect.fLeft, rect.fTop,
251 rect.fRight, rect.fBottom, vertexStride);
252 viewMatrix.mapPointsWithStride(fan0Pos, vertexStride, 4);
254 // Now create the inset points and then outset the original
258 *((SkPoint*)((intptr_t)fan1Pos + 0 * vertexStride)) =
259 *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) + vec[0] + vec[1];
260 *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) -= vec[0] + vec[1];
262 *((SkPoint*)((intptr_t)fan1Pos + 1 * vertexStride)) =
263 *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) + vec[0] - vec[1];
264 *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) -= vec[0] - vec[1];
266 *((SkPoint*)((intptr_t)fan1Pos + 2 * vertexStride)) =
267 *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) - vec[0] - vec[1];
268 *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) += vec[0] + vec[1];
270 *((SkPoint*)((intptr_t)fan1Pos + 3 * vertexStride)) =
271 *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) - vec[0] + vec[1];
272 *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) += vec[0] - vec[1];
275 // Make verts point to vertex color and then set all the color and coverage vertex attrs
277 verts += sizeof(SkPoint);
278 for (int i = 0; i < 4; ++i) {
279 if (tweakAlphaForCoverage) {
280 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
282 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
283 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
288 if (inset < SK_ScalarHalf) {
289 scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
290 SkASSERT(scale >= 0 && scale <= 255);
295 verts += 4 * vertexStride;
297 float innerCoverage = GrNormalizeByteToFloat(scale);
298 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
300 for (int i = 0; i < 4; ++i) {
301 if (tweakAlphaForCoverage) {
302 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
304 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
305 *reinterpret_cast<float*>(verts + i * vertexStride +
306 sizeof(GrColor)) = innerCoverage;
311 struct BatchTracker {
313 bool fUsesLocalCoords;
315 bool fCoverageIgnored;
316 bool fCanTweakAlphaForCoverage;
320 const GrIndexBuffer* fIndexBuffer;
321 SkSTArray<1, Geometry, true> fGeoData;
325 // Should the coverage be multiplied into the color attrib or use a separate attrib.
326 enum CoverageAttribType {
327 kUseColor_CoverageAttribType,
328 kUseCoverage_CoverageAttribType,
332 void GrAARectRenderer::reset() {
333 SkSafeSetNull(fAAFillRectIndexBuffer);
334 SkSafeSetNull(fAAMiterStrokeRectIndexBuffer);
335 SkSafeSetNull(fAABevelStrokeRectIndexBuffer);
338 static const uint16_t gMiterStrokeAARectIdx[] = {
339 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
340 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
341 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
342 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
344 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
345 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
346 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
347 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
349 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
350 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
351 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
352 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
355 static const int kIndicesPerMiterStrokeRect = SK_ARRAY_COUNT(gMiterStrokeAARectIdx);
356 static const int kVertsPerMiterStrokeRect = 16;
357 static const int kNumMiterStrokeRectsInIndexBuffer = 256;
360 * As in miter-stroke, index = a + b, and a is the current index, b is the shift
361 * from the first index. The index layout:
362 * outer AA line: 0~3, 4~7
363 * outer edge: 8~11, 12~15
365 * inner AA line: 20~23
366 * Following comes a bevel-stroke rect and its indices:
369 * *********************************
370 * * ______________________________ *
373 * 0 * |8 16_____________________19 11 | * 3
375 * * | | **************** | | *
376 * * | | * 20 23 * | | *
378 * * | | * 21 22 * | | *
379 * * | | **************** | | *
380 * * | |____________________| | *
381 * 1 * |9 17 18 10| * 2
383 * * \13 __________________________14/ *
385 * **********************************
388 static const uint16_t gBevelStrokeAARectIdx[] = {
389 // Draw outer AA, from outer AA line to outer edge, shift is 0.
390 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
391 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
392 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
393 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
394 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
395 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
396 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
397 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
399 // Draw the stroke, from outer edge to inner edge, shift is 8.
400 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
402 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
403 6 + 8, 2 + 8, 10 + 8,
404 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
405 3 + 8, 7 + 8, 11 + 8,
406 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
409 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
410 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
411 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
412 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
413 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
416 static const int kIndicesPerBevelStrokeRect = SK_ARRAY_COUNT(gBevelStrokeAARectIdx);
417 static const int kVertsPerBevelStrokeRect = 24;
418 static const int kNumBevelStrokeRectsInIndexBuffer = 256;
420 static int aa_stroke_rect_index_count(bool miterStroke) {
421 return miterStroke ? SK_ARRAY_COUNT(gMiterStrokeAARectIdx) :
422 SK_ARRAY_COUNT(gBevelStrokeAARectIdx);
425 GrIndexBuffer* GrAARectRenderer::aaStrokeRectIndexBuffer(bool miterStroke) {
427 if (NULL == fAAMiterStrokeRectIndexBuffer) {
428 fAAMiterStrokeRectIndexBuffer =
429 fGpu->createInstancedIndexBuffer(gMiterStrokeAARectIdx,
430 kIndicesPerMiterStrokeRect,
431 kNumMiterStrokeRectsInIndexBuffer,
432 kVertsPerMiterStrokeRect);
434 return fAAMiterStrokeRectIndexBuffer;
436 if (NULL == fAABevelStrokeRectIndexBuffer) {
437 fAABevelStrokeRectIndexBuffer =
438 fGpu->createInstancedIndexBuffer(gBevelStrokeAARectIdx,
439 kIndicesPerBevelStrokeRect,
440 kNumBevelStrokeRectsInIndexBuffer,
441 kVertsPerBevelStrokeRect);
443 return fAABevelStrokeRectIndexBuffer;
447 void GrAARectRenderer::geometryFillAARect(GrDrawTarget* target,
448 GrPipelineBuilder* pipelineBuilder,
450 const SkMatrix& viewMatrix,
452 const SkRect& devRect) {
453 if (!fAAFillRectIndexBuffer) {
454 fAAFillRectIndexBuffer = fGpu->createInstancedIndexBuffer(gFillAARectIdx,
455 kIndicesPerAAFillRect,
456 kNumAAFillRectsInIndexBuffer,
457 kVertsPerAAFillRect);
460 if (!fAAFillRectIndexBuffer) {
461 SkDebugf("Unable to create index buffer\n");
465 AAFillRectBatch::Geometry geometry;
466 geometry.fRect = rect;
467 geometry.fViewMatrix = viewMatrix;
468 geometry.fDevRect = devRect;
469 geometry.fColor = color;
471 SkAutoTUnref<GrBatch> batch(AAFillRectBatch::Create(geometry, fAAFillRectIndexBuffer));
472 target->drawBatch(pipelineBuilder, batch, &devRect);
475 void GrAARectRenderer::strokeAARect(GrDrawTarget* target,
476 GrPipelineBuilder* pipelineBuilder,
478 const SkMatrix& viewMatrix,
480 const SkRect& devRect,
481 const SkStrokeRec& stroke) {
482 SkVector devStrokeSize;
483 SkScalar width = stroke.getWidth();
485 devStrokeSize.set(width, width);
486 viewMatrix.mapVectors(&devStrokeSize, 1);
487 devStrokeSize.setAbs(devStrokeSize);
489 devStrokeSize.set(SK_Scalar1, SK_Scalar1);
492 const SkScalar dx = devStrokeSize.fX;
493 const SkScalar dy = devStrokeSize.fY;
494 const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf);
495 const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf);
497 // Temporarily #if'ed out. We don't want to pass in the devRect but
498 // right now it is computed in GrContext::apply_aa_to_rect and we don't
499 // want to throw away the work
502 combinedMatrix.mapRect(&devRect, rect);
507 SkScalar w = devRect.width() - dx;
508 SkScalar h = devRect.height() - dy;
509 spare = SkTMin(w, h);
512 SkRect devOutside(devRect);
513 devOutside.outset(rx, ry);
515 bool miterStroke = true;
516 // For hairlines, make bevel and round joins appear the same as mitered ones.
517 // small miter limit means right angles show bevel...
518 if ((width > 0) && (stroke.getJoin() != SkPaint::kMiter_Join ||
519 stroke.getMiter() < SK_ScalarSqrt2)) {
523 if (spare <= 0 && miterStroke) {
524 this->fillAARect(target, pipelineBuilder, color, viewMatrix, devOutside, devOutside);
528 SkRect devInside(devRect);
529 devInside.inset(rx, ry);
531 SkRect devOutsideAssist(devRect);
533 // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
534 // to draw the outer of the rect. Because there are 8 vertices on the outer
535 // edge, while vertex number of inner edge is 4, the same as miter-stroke.
537 devOutside.inset(0, ry);
538 devOutsideAssist.outset(0, ry);
541 this->geometryStrokeAARect(target, pipelineBuilder, color, viewMatrix, devOutside,
542 devOutsideAssist, devInside, miterStroke);
545 class AAStrokeRectBatch : public GrBatch {
547 // TODO support AA rotated stroke rects by copying around view matrices
551 SkRect fDevOutsideAssist;
556 static GrBatch* Create(const Geometry& geometry, const SkMatrix& viewMatrix,
557 const GrIndexBuffer* indexBuffer) {
558 return SkNEW_ARGS(AAStrokeRectBatch, (geometry, viewMatrix, indexBuffer));
561 const char* name() const SK_OVERRIDE { return "AAStrokeRect"; }
563 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
564 // When this is called on a batch, there is only one geometry bundle
565 out->setKnownFourComponents(fGeoData[0].fColor);
568 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
569 out->setUnknownSingleComponent();
572 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
573 // Handle any color overrides
574 if (init.fColorIgnored) {
575 fGeoData[0].fColor = GrColor_ILLEGAL;
576 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
577 fGeoData[0].fColor = init.fOverrideColor;
580 // setup batch properties
581 fBatch.fColorIgnored = init.fColorIgnored;
582 fBatch.fColor = fGeoData[0].fColor;
583 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
584 fBatch.fCoverageIgnored = init.fCoverageIgnored;
585 fBatch.fMiterStroke = fGeoData[0].fMiterStroke;
586 fBatch.fCanTweakAlphaForCoverage = init.fCanTweakAlphaForCoverage;
589 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
590 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
592 // Local matrix is ignored if we don't have local coords. If we have localcoords we only
593 // batch with identical view matrices
594 SkMatrix localMatrix;
595 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
596 SkDebugf("Cannot invert\n");
600 SkAutoTUnref<const GrGeometryProcessor> gp(create_fill_rect_gp(canTweakAlphaForCoverage,
603 batchTarget->initDraw(gp, pipeline);
605 // TODO this is hacky, but the only way we have to initialize the GP is to use the
606 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
607 // everywhere we can remove this nastiness
609 init.fColorIgnored = fBatch.fColorIgnored;
610 init.fOverrideColor = GrColor_ILLEGAL;
611 init.fCoverageIgnored = fBatch.fCoverageIgnored;
612 init.fUsesLocalCoords = this->usesLocalCoords();
613 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
615 size_t vertexStride = gp->getVertexStride();
617 SkASSERT(canTweakAlphaForCoverage ?
618 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
619 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
621 int innerVertexNum = 4;
622 int outerVertexNum = this->miterStroke() ? 4 : 8;
623 int totalVertexNum = (outerVertexNum + innerVertexNum) * 2;
625 int instanceCount = fGeoData.count();
626 int vertexCount = totalVertexNum * instanceCount;
628 const GrVertexBuffer* vertexBuffer;
631 void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
637 SkDebugf("Could not allocate vertices\n");
641 for (int i = 0; i < instanceCount; i++) {
642 const Geometry& args = fGeoData[i];
643 this->generateAAStrokeRectGeometry(vertices,
644 i * totalVertexNum * vertexStride,
650 args.fDevOutsideAssist,
653 canTweakAlphaForCoverage);
656 GrDrawTarget::DrawInfo drawInfo;
657 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
658 drawInfo.setStartVertex(0);
659 drawInfo.setStartIndex(0);
660 drawInfo.setVerticesPerInstance(totalVertexNum);
661 drawInfo.setIndicesPerInstance(aa_stroke_rect_index_count(this->miterStroke()));
662 drawInfo.adjustStartVertex(firstVertex);
663 drawInfo.setVertexBuffer(vertexBuffer);
664 drawInfo.setIndexBuffer(fIndexBuffer);
666 int maxInstancesPerDraw = kNumBevelStrokeRectsInIndexBuffer;
668 while (instanceCount) {
669 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
670 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
671 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
673 batchTarget->draw(drawInfo);
675 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
676 instanceCount -= drawInfo.instanceCount();
680 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
683 AAStrokeRectBatch(const Geometry& geometry, const SkMatrix& viewMatrix,
684 const GrIndexBuffer* indexBuffer)
685 : fIndexBuffer(indexBuffer) {
686 this->initClassID<AAStrokeRectBatch>();
687 fBatch.fViewMatrix = viewMatrix;
688 fGeoData.push_back(geometry);
691 GrColor color() const { return fBatch.fColor; }
692 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
693 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
694 bool colorIgnored() const { return fBatch.fColorIgnored; }
695 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
696 bool miterStroke() const { return fBatch.fMiterStroke; }
698 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
699 AAStrokeRectBatch* that = t->cast<AAStrokeRectBatch>();
701 // TODO batch across miterstroke changes
702 if (this->miterStroke() != that->miterStroke()) {
706 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
707 // local coords then we won't be able to batch. We could actually upload the viewmatrix
708 // using vertex attributes in these cases, but haven't investigated that
709 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
713 // In the event of two batches, one who can tweak, one who cannot, we just fall back to
715 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
716 fBatch.fCanTweakAlphaForCoverage = false;
719 if (this->color() != that->color()) {
720 fBatch.fColor = GrColor_ILLEGAL;
722 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
726 void generateAAStrokeRectGeometry(void* vertices,
732 const SkRect& devOutside,
733 const SkRect& devOutsideAssist,
734 const SkRect& devInside,
736 bool tweakAlphaForCoverage) const {
737 intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
739 // We create vertices for four nested rectangles. There are two ramps from 0 to full
740 // coverage, one on the exterior of the stroke and the other on the interior.
741 // The following pointers refer to the four rects, from outermost to innermost.
742 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
743 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
744 SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
745 SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(verts +
746 (2 * outerVertexNum + innerVertexNum) *
749 #ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
750 // TODO: this only really works if the X & Y margins are the same all around
751 // the rect (or if they are all >= 1.0).
752 SkScalar inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
753 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
754 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
756 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
758 inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom -
761 SkASSERT(inset >= 0);
763 SkScalar inset = SK_ScalarHalf;
768 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
770 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
771 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
773 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
775 SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
776 SkPoint* fan1AssistPos = reinterpret_cast<SkPoint*>(verts +
777 (outerVertexNum + 4) *
780 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
781 set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
783 // outer one of the inner two
784 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
785 set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset);
786 // inner one of the inner two
787 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
789 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
792 // Make verts point to vertex color and then set all the color and coverage vertex attrs
793 // values. The outermost rect has 0 coverage
794 verts += sizeof(SkPoint);
795 for (int i = 0; i < outerVertexNum; ++i) {
796 if (tweakAlphaForCoverage) {
797 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
799 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
800 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
804 // scale is the coverage for the the inner two rects.
806 if (inset < SK_ScalarHalf) {
807 scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
808 SkASSERT(scale >= 0 && scale <= 255);
813 float innerCoverage = GrNormalizeByteToFloat(scale);
814 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
816 verts += outerVertexNum * vertexStride;
817 for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
818 if (tweakAlphaForCoverage) {
819 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
821 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
822 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
827 // The innermost rect has 0 coverage
828 verts += (outerVertexNum + innerVertexNum) * vertexStride;
829 for (int i = 0; i < innerVertexNum; ++i) {
830 if (tweakAlphaForCoverage) {
831 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
833 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
834 *reinterpret_cast<GrColor*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
839 struct BatchTracker {
840 SkMatrix fViewMatrix;
842 bool fUsesLocalCoords;
844 bool fCoverageIgnored;
846 bool fCanTweakAlphaForCoverage;
850 const GrIndexBuffer* fIndexBuffer;
851 SkSTArray<1, Geometry, true> fGeoData;
855 void GrAARectRenderer::geometryStrokeAARect(GrDrawTarget* target,
856 GrPipelineBuilder* pipelineBuilder,
858 const SkMatrix& viewMatrix,
859 const SkRect& devOutside,
860 const SkRect& devOutsideAssist,
861 const SkRect& devInside,
863 GrIndexBuffer* indexBuffer = this->aaStrokeRectIndexBuffer(miterStroke);
865 SkDebugf("Failed to create index buffer!\n");
869 AAStrokeRectBatch::Geometry geometry;
870 geometry.fColor = color;
871 geometry.fDevOutside = devOutside;
872 geometry.fDevOutsideAssist = devOutsideAssist;
873 geometry.fDevInside = devInside;
874 geometry.fMiterStroke = miterStroke;
876 SkAutoTUnref<GrBatch> batch(AAStrokeRectBatch::Create(geometry, viewMatrix, indexBuffer));
877 target->drawBatch(pipelineBuilder, batch);
880 void GrAARectRenderer::fillAANestedRects(GrDrawTarget* target,
881 GrPipelineBuilder* pipelineBuilder,
883 const SkMatrix& viewMatrix,
884 const SkRect rects[2]) {
885 SkASSERT(viewMatrix.rectStaysRect());
886 SkASSERT(!rects[1].isEmpty());
888 SkRect devOutside, devOutsideAssist, devInside;
889 viewMatrix.mapRect(&devOutside, rects[0]);
890 // can't call mapRect for devInside since it calls sort
891 viewMatrix.mapPoints((SkPoint*)&devInside, (const SkPoint*)&rects[1], 2);
893 if (devInside.isEmpty()) {
894 this->fillAARect(target, pipelineBuilder, color, viewMatrix, devOutside, devOutside);
898 this->geometryStrokeAARect(target, pipelineBuilder, color, viewMatrix, devOutside,
899 devOutsideAssist, devInside, true);