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 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 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 57 static GrBatch* Create(const Geometry& geometry) { 58 return SkNEW_ARGS(AAFillRectBatch, (geometry)); 59 } 60 61 const char* name() const override { return "AAFillRectBatch"; } 62 63 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 68 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override { 69 out->setUnknownSingleComponent(); 70 } 71 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 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 144 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } 145 146 private: 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 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 174 GrColor color() const { return fBatch.fColor; } 175 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } 176 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; } 177 bool colorIgnored() const { return fBatch.fColorIgnored; } 178 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } 179 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 206 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 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 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 412 static GrBatch* Create(const Geometry& geometry, const SkMatrix& viewMatrix) { 413 return SkNEW_ARGS(AAStrokeRectBatch, (geometry, viewMatrix)); 414 } 415 416 const char* name() const override { return "AAStrokeRect"; } 417 418 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 423 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override { 424 out->setUnknownSingleComponent(); 425 } 426 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 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 509 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } 510 511 private: 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 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 623 GrColor color() const { return fBatch.fColor; } 624 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } 625 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; } 626 bool colorIgnored() const { return fBatch.fColorIgnored; } 627 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; } 628 bool miterStroke() const { return fBatch.fMiterStroke; } 629 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 659 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 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 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 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 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