1 /* 2 * Copyright 2015 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "GrAAStrokeRectBatch.h" 9 10 #include "GrBatchFlushState.h" 11 #include "GrDefaultGeoProcFactory.h" 12 #include "GrResourceKey.h" 13 #include "GrResourceProvider.h" 14 15 GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey); 16 GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey); 17 18 static void set_inset_fan(SkPoint* pts, size_t stride, 19 const SkRect& r, SkScalar dx, SkScalar dy) { 20 pts->setRectFan(r.fLeft + dx, r.fTop + dy, 21 r.fRight - dx, r.fBottom - dy, stride); 22 } 23 24 static const GrGeometryProcessor* create_stroke_rect_gp(bool tweakAlphaForCoverage, 25 const SkMatrix& viewMatrix, 26 bool usesLocalCoords, 27 bool coverageIgnored) { 28 using namespace GrDefaultGeoProcFactory; 29 30 Color color(Color::kAttribute_Type); 31 Coverage::Type coverageType; 32 // TODO remove coverage if coverage is ignored 33 /*if (coverageIgnored) { 34 coverageType = Coverage::kNone_Type; 35 } else*/ if (tweakAlphaForCoverage) { 36 coverageType = Coverage::kSolid_Type; 37 } else { 38 coverageType = Coverage::kAttribute_Type; 39 } 40 Coverage coverage(coverageType); 41 LocalCoords localCoords(usesLocalCoords ? LocalCoords::kUsePosition_Type : 42 LocalCoords::kUnused_Type); 43 return CreateForDeviceSpace(color, coverage, localCoords, viewMatrix); 44 } 45 46 class AAStrokeRectBatch : public GrVertexBatch { 47 public: 48 DEFINE_BATCH_CLASS_ID 49 50 // TODO support AA rotated stroke rects by copying around view matrices 51 struct Geometry { 52 SkRect fDevOutside; 53 SkRect fDevOutsideAssist; 54 SkRect fDevInside; 55 GrColor fColor; 56 bool fDegenerate; 57 }; 58 59 static AAStrokeRectBatch* Create(const SkMatrix& viewMatrix, bool miterStroke) { 60 return new AAStrokeRectBatch(viewMatrix, miterStroke); 61 } 62 63 const char* name() const override { return "AAStrokeRect"; } 64 65 void computePipelineOptimizations(GrInitInvariantOutput* color, 66 GrInitInvariantOutput* coverage, 67 GrBatchToXPOverrides* overrides) const override { 68 // When this is called on a batch, there is only one geometry bundle 69 color->setKnownFourComponents(fGeoData[0].fColor); 70 coverage->setUnknownSingleComponent(); 71 } 72 73 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } 74 75 bool canAppend(const SkMatrix& viewMatrix, bool miterStroke) { 76 return fViewMatrix.cheapEqualTo(viewMatrix) && fMiterStroke == miterStroke; 77 } 78 79 void append(GrColor color, const SkRect& devOutside, const SkRect& devOutsideAssist, 80 const SkRect& devInside, bool degenerate) { 81 Geometry& geometry = fGeoData.push_back(); 82 geometry.fColor = color; 83 geometry.fDevOutside = devOutside; 84 geometry.fDevOutsideAssist = devOutsideAssist; 85 geometry.fDevInside = devInside; 86 geometry.fDegenerate = degenerate; 87 } 88 89 void appendAndUpdateBounds(GrColor color, const SkRect& devOutside, 90 const SkRect& devOutsideAssist, const SkRect& devInside, 91 bool degenerate) { 92 this->append(color, devOutside, devOutsideAssist, devInside, degenerate); 93 94 SkRect bounds; 95 this->updateBounds(&bounds, fGeoData.back()); 96 this->joinBounds(bounds); 97 } 98 99 void init() { this->updateBounds(&fBounds, fGeoData[0]); } 100 101 private: 102 void updateBounds(SkRect* bounds, const Geometry& geo) { 103 // If we have miterstroke then we inset devOutside and outset devOutsideAssist, so we need 104 // the join for proper bounds 105 *bounds = geo.fDevOutside; 106 bounds->join(geo.fDevOutsideAssist); 107 } 108 109 void onPrepareDraws(Target*) const override; 110 void initBatchTracker(const GrXPOverridesForBatch&) override; 111 112 AAStrokeRectBatch(const SkMatrix& viewMatrix,bool miterStroke) 113 : INHERITED(ClassID()) { 114 fViewMatrix = viewMatrix; 115 fMiterStroke = miterStroke; 116 } 117 118 static const int kMiterIndexCnt = 3 * 24; 119 static const int kMiterVertexCnt = 16; 120 static const int kNumMiterRectsInIndexBuffer = 256; 121 122 static const int kBevelIndexCnt = 48 + 36 + 24; 123 static const int kBevelVertexCnt = 24; 124 static const int kNumBevelRectsInIndexBuffer = 256; 125 126 static const GrIndexBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider, 127 bool miterStroke); 128 129 GrColor color() const { return fBatch.fColor; } 130 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } 131 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; } 132 bool colorIgnored() const { return fBatch.fColorIgnored; } 133 bool coverageIgnored() const { return fBatch.fCoverageIgnored; } 134 const Geometry& geometry() const { return fGeoData[0]; } 135 const SkMatrix& viewMatrix() const { return fViewMatrix; } 136 bool miterStroke() const { return fMiterStroke; } 137 138 bool onCombineIfPossible(GrBatch* t, const GrCaps&) override; 139 140 void generateAAStrokeRectGeometry(void* vertices, 141 size_t offset, 142 size_t vertexStride, 143 int outerVertexNum, 144 int innerVertexNum, 145 GrColor color, 146 const SkRect& devOutside, 147 const SkRect& devOutsideAssist, 148 const SkRect& devInside, 149 bool miterStroke, 150 bool degenerate, 151 bool tweakAlphaForCoverage) const; 152 153 struct BatchTracker { 154 GrColor fColor; 155 bool fUsesLocalCoords; 156 bool fColorIgnored; 157 bool fCoverageIgnored; 158 bool fCanTweakAlphaForCoverage; 159 }; 160 161 BatchTracker fBatch; 162 SkSTArray<1, Geometry, true> fGeoData; 163 SkMatrix fViewMatrix; 164 bool fMiterStroke; 165 166 typedef GrVertexBatch INHERITED; 167 }; 168 169 void AAStrokeRectBatch::initBatchTracker(const GrXPOverridesForBatch& overrides) { 170 // Handle any color overrides 171 if (!overrides.readsColor()) { 172 fGeoData[0].fColor = GrColor_ILLEGAL; 173 } 174 overrides.getOverrideColorIfSet(&fGeoData[0].fColor); 175 176 // setup batch properties 177 fBatch.fColorIgnored = !overrides.readsColor(); 178 fBatch.fColor = fGeoData[0].fColor; 179 fBatch.fUsesLocalCoords = overrides.readsLocalCoords(); 180 fBatch.fCoverageIgnored = !overrides.readsCoverage(); 181 fBatch.fCanTweakAlphaForCoverage = overrides.canTweakAlphaForCoverage(); 182 } 183 184 void AAStrokeRectBatch::onPrepareDraws(Target* target) const { 185 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage(); 186 187 SkAutoTUnref<const GrGeometryProcessor> gp(create_stroke_rect_gp(canTweakAlphaForCoverage, 188 this->viewMatrix(), 189 this->usesLocalCoords(), 190 this->coverageIgnored())); 191 if (!gp) { 192 SkDebugf("Couldn't create GrGeometryProcessor\n"); 193 return; 194 } 195 196 target->initDraw(gp, this->pipeline()); 197 198 size_t vertexStride = gp->getVertexStride(); 199 200 SkASSERT(canTweakAlphaForCoverage ? 201 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) : 202 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr)); 203 int innerVertexNum = 4; 204 int outerVertexNum = this->miterStroke() ? 4 : 8; 205 int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2; 206 int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt; 207 int instanceCount = fGeoData.count(); 208 209 const SkAutoTUnref<const GrIndexBuffer> indexBuffer( 210 GetIndexBuffer(target->resourceProvider(), this->miterStroke())); 211 InstancedHelper helper; 212 void* vertices = helper.init(target, kTriangles_GrPrimitiveType, vertexStride, 213 indexBuffer, verticesPerInstance, indicesPerInstance, 214 instanceCount); 215 if (!vertices || !indexBuffer) { 216 SkDebugf("Could not allocate vertices\n"); 217 return; 218 } 219 220 for (int i = 0; i < instanceCount; i++) { 221 const Geometry& args = fGeoData[i]; 222 this->generateAAStrokeRectGeometry(vertices, 223 i * verticesPerInstance * vertexStride, 224 vertexStride, 225 outerVertexNum, 226 innerVertexNum, 227 args.fColor, 228 args.fDevOutside, 229 args.fDevOutsideAssist, 230 args.fDevInside, 231 fMiterStroke, 232 args.fDegenerate, 233 canTweakAlphaForCoverage); 234 } 235 helper.recordDraw(target); 236 } 237 238 const GrIndexBuffer* AAStrokeRectBatch::GetIndexBuffer(GrResourceProvider* resourceProvider, 239 bool miterStroke) { 240 241 if (miterStroke) { 242 static const uint16_t gMiterIndices[] = { 243 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0, 244 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0, 245 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0, 246 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0, 247 248 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4, 249 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4, 250 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4, 251 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4, 252 253 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8, 254 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8, 255 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8, 256 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8, 257 }; 258 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt); 259 GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey); 260 return resourceProvider->findOrCreateInstancedIndexBuffer(gMiterIndices, 261 kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt, 262 gMiterIndexBufferKey); 263 } else { 264 /** 265 * As in miter-stroke, index = a + b, and a is the current index, b is the shift 266 * from the first index. The index layout: 267 * outer AA line: 0~3, 4~7 268 * outer edge: 8~11, 12~15 269 * inner edge: 16~19 270 * inner AA line: 20~23 271 * Following comes a bevel-stroke rect and its indices: 272 * 273 * 4 7 274 * ********************************* 275 * * ______________________________ * 276 * * / 12 15 \ * 277 * * / \ * 278 * 0 * |8 16_____________________19 11 | * 3 279 * * | | | | * 280 * * | | **************** | | * 281 * * | | * 20 23 * | | * 282 * * | | * * | | * 283 * * | | * 21 22 * | | * 284 * * | | **************** | | * 285 * * | |____________________| | * 286 * 1 * |9 17 18 10| * 2 287 * * \ / * 288 * * \13 __________________________14/ * 289 * * * 290 * ********************************** 291 * 5 6 292 */ 293 static const uint16_t gBevelIndices[] = { 294 // Draw outer AA, from outer AA line to outer edge, shift is 0. 295 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0, 296 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0, 297 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0, 298 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0, 299 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0, 300 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0, 301 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0, 302 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0, 303 304 // Draw the stroke, from outer edge to inner edge, shift is 8. 305 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8, 306 1 + 8, 5 + 8, 9 + 8, 307 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8, 308 6 + 8, 2 + 8, 10 + 8, 309 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8, 310 3 + 8, 7 + 8, 11 + 8, 311 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8, 312 4 + 8, 0 + 8, 8 + 8, 313 314 // Draw the inner AA, from inner edge to inner AA line, shift is 16. 315 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16, 316 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16, 317 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16, 318 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16, 319 }; 320 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt); 321 322 GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey); 323 return resourceProvider->findOrCreateInstancedIndexBuffer(gBevelIndices, 324 kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt, 325 gBevelIndexBufferKey); 326 } 327 } 328 329 bool AAStrokeRectBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) { 330 AAStrokeRectBatch* that = t->cast<AAStrokeRectBatch>(); 331 332 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), 333 that->bounds(), caps)) { 334 return false; 335 } 336 337 // TODO batch across miterstroke changes 338 if (this->miterStroke() != that->miterStroke()) { 339 return false; 340 } 341 342 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses 343 // local coords then we won't be able to batch. We could actually upload the viewmatrix 344 // using vertex attributes in these cases, but haven't investigated that 345 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 346 return false; 347 } 348 349 // In the event of two batches, one who can tweak, one who cannot, we just fall back to 350 // not tweaking 351 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) { 352 fBatch.fCanTweakAlphaForCoverage = false; 353 } 354 355 if (this->color() != that->color()) { 356 fBatch.fColor = GrColor_ILLEGAL; 357 } 358 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); 359 this->joinBounds(that->bounds()); 360 return true; 361 } 362 363 static void setup_scale(int* scale, SkScalar inset) { 364 if (inset < SK_ScalarHalf) { 365 *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf)); 366 SkASSERT(*scale >= 0 && *scale <= 255); 367 } else { 368 *scale = 0xff; 369 } 370 } 371 372 void AAStrokeRectBatch::generateAAStrokeRectGeometry(void* vertices, 373 size_t offset, 374 size_t vertexStride, 375 int outerVertexNum, 376 int innerVertexNum, 377 GrColor color, 378 const SkRect& devOutside, 379 const SkRect& devOutsideAssist, 380 const SkRect& devInside, 381 bool miterStroke, 382 bool degenerate, 383 bool tweakAlphaForCoverage) const { 384 intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset; 385 386 // We create vertices for four nested rectangles. There are two ramps from 0 to full 387 // coverage, one on the exterior of the stroke and the other on the interior. 388 // The following pointers refer to the four rects, from outermost to innermost. 389 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts); 390 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride); 391 SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride); 392 SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(verts + 393 (2 * outerVertexNum + innerVertexNum) * 394 vertexStride); 395 396 #ifndef SK_IGNORE_THIN_STROKED_RECT_FIX 397 // TODO: this only really works if the X & Y margins are the same all around 398 // the rect (or if they are all >= 1.0). 399 SkScalar inset; 400 if (!degenerate) { 401 inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight); 402 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft); 403 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop); 404 if (miterStroke) { 405 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom); 406 } else { 407 inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom - 408 devInside.fBottom); 409 } 410 SkASSERT(inset >= 0); 411 } else { 412 // TODO use real devRect here 413 inset = SkMinScalar(devOutside.width(), SK_Scalar1); 414 inset = SK_ScalarHalf * SkMinScalar(inset, SkTMax(devOutside.height(), 415 devOutsideAssist.height())); 416 } 417 #else 418 SkScalar inset; 419 if (!degenerate) { 420 inset = SK_ScalarHalf; 421 } else { 422 // TODO use real devRect here 423 inset = SkMinScalar(devOutside.width(), SK_Scalar1); 424 inset = SK_ScalarHalf * SkMinScalar(inset, SkTMax(devOutside.height(), 425 devOutsideAssist.height())); 426 } 427 #endif 428 429 if (miterStroke) { 430 // outermost 431 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf); 432 // inner two 433 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset); 434 if (!degenerate) { 435 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset); 436 // innermost 437 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf); 438 } else { 439 // When the interior rect has become degenerate we smoosh to a single point 440 SkASSERT(devInside.fLeft == devInside.fRight && 441 devInside.fTop == devInside.fBottom); 442 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop, 443 devInside.fRight, devInside.fBottom, vertexStride); 444 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop, 445 devInside.fRight, devInside.fBottom, vertexStride); 446 } 447 } else { 448 SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride); 449 SkPoint* fan1AssistPos = reinterpret_cast<SkPoint*>(verts + 450 (outerVertexNum + 4) * 451 vertexStride); 452 // outermost 453 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf); 454 set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf, 455 -SK_ScalarHalf); 456 // outer one of the inner two 457 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset); 458 set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset); 459 if (!degenerate) { 460 // inner one of the inner two 461 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset); 462 // innermost 463 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf); 464 } else { 465 // When the interior rect has become degenerate we smoosh to a single point 466 SkASSERT(devInside.fLeft == devInside.fRight && 467 devInside.fTop == devInside.fBottom); 468 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop, 469 devInside.fRight, devInside.fBottom, vertexStride); 470 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop, 471 devInside.fRight, devInside.fBottom, vertexStride); 472 } 473 } 474 475 // Make verts point to vertex color and then set all the color and coverage vertex attrs 476 // values. The outermost rect has 0 coverage 477 verts += sizeof(SkPoint); 478 for (int i = 0; i < outerVertexNum; ++i) { 479 if (tweakAlphaForCoverage) { 480 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0; 481 } else { 482 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; 483 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0; 484 } 485 } 486 487 // scale is the coverage for the the inner two rects. 488 int scale; 489 setup_scale(&scale, inset); 490 491 float innerCoverage = GrNormalizeByteToFloat(scale); 492 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale); 493 494 verts += outerVertexNum * vertexStride; 495 for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) { 496 if (tweakAlphaForCoverage) { 497 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor; 498 } else { 499 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; 500 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage; 501 } 502 } 503 504 // The innermost rect has 0 coverage, unless we are degenerate, in which case we must apply the 505 // scaled coverage 506 verts += (outerVertexNum + innerVertexNum) * vertexStride; 507 if (!degenerate) { 508 innerCoverage = 0; 509 scaledColor = 0; 510 } 511 512 for (int i = 0; i < innerVertexNum; ++i) { 513 if (tweakAlphaForCoverage) { 514 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor; 515 } else { 516 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; 517 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage; 518 } 519 } 520 } 521 522 inline static bool is_miter(const SkStrokeRec& stroke) { 523 // For hairlines, make bevel and round joins appear the same as mitered ones. 524 // small miter limit means right angles show bevel... 525 if ((stroke.getWidth() > 0) && (stroke.getJoin() != SkPaint::kMiter_Join || 526 stroke.getMiter() < SK_ScalarSqrt2)) { 527 return false; 528 } 529 return true; 530 } 531 532 static void compute_rects(SkRect* devOutside, SkRect* devOutsideAssist, SkRect* devInside, 533 bool* isDegenerate, const SkMatrix& viewMatrix, const SkRect& rect, 534 SkScalar strokeWidth, bool miterStroke) { 535 SkRect devRect; 536 viewMatrix.mapRect(&devRect, rect); 537 538 SkVector devStrokeSize; 539 if (strokeWidth > 0) { 540 devStrokeSize.set(strokeWidth, strokeWidth); 541 viewMatrix.mapVectors(&devStrokeSize, 1); 542 devStrokeSize.setAbs(devStrokeSize); 543 } else { 544 devStrokeSize.set(SK_Scalar1, SK_Scalar1); 545 } 546 547 const SkScalar dx = devStrokeSize.fX; 548 const SkScalar dy = devStrokeSize.fY; 549 const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf); 550 const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf); 551 552 *devOutside = devRect; 553 *devOutsideAssist = devRect; 554 *devInside = devRect; 555 556 devOutside->outset(rx, ry); 557 devInside->inset(rx, ry); 558 559 // If we have a degenerate stroking rect(ie the stroke is larger than inner rect) then we 560 // make a degenerate inside rect to avoid double hitting. We will also jam all of the points 561 // together when we render these rects. 562 SkScalar spare; 563 { 564 SkScalar w = devRect.width() - dx; 565 SkScalar h = devRect.height() - dy; 566 spare = SkTMin(w, h); 567 } 568 569 *isDegenerate = spare <= 0; 570 if (*isDegenerate) { 571 devInside->fLeft = devInside->fRight = devRect.centerX(); 572 devInside->fTop = devInside->fBottom = devRect.centerY(); 573 } 574 575 // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist) 576 // to draw the outside of the octagon. Because there are 8 vertices on the outer 577 // edge, while vertex number of inner edge is 4, the same as miter-stroke. 578 if (!miterStroke) { 579 devOutside->inset(0, ry); 580 devOutsideAssist->outset(0, ry); 581 } 582 } 583 584 namespace GrAAStrokeRectBatch { 585 586 GrDrawBatch* Create(GrColor color, 587 const SkMatrix& viewMatrix, 588 const SkRect& devOutside, 589 const SkRect& devOutsideAssist, 590 const SkRect& devInside, 591 bool miterStroke, 592 bool degenerate) { 593 AAStrokeRectBatch* batch = AAStrokeRectBatch::Create(viewMatrix, miterStroke); 594 batch->append(color, devOutside, devOutsideAssist, devInside, degenerate); 595 batch->init(); 596 return batch; 597 } 598 599 GrDrawBatch* Create(GrColor color, 600 const SkMatrix& viewMatrix, 601 const SkRect& rect, 602 const SkStrokeRec& stroke) { 603 bool isMiterStroke = is_miter(stroke); 604 AAStrokeRectBatch* batch = AAStrokeRectBatch::Create(viewMatrix, isMiterStroke); 605 606 SkRect devOutside, devOutsideAssist, devInside; 607 bool isDegenerate; 608 compute_rects(&devOutside, &devOutsideAssist, &devInside, &isDegenerate, viewMatrix, 609 rect, stroke.getWidth(), isMiterStroke); 610 611 batch->append(color, devOutside, devOutsideAssist, devInside, isDegenerate); 612 batch->init(); 613 return batch; 614 } 615 616 bool Append(GrBatch* origBatch, 617 GrColor color, 618 const SkMatrix& viewMatrix, 619 const SkRect& rect, 620 const SkStrokeRec& stroke) { 621 AAStrokeRectBatch* batch = origBatch->cast<AAStrokeRectBatch>(); 622 623 // we can't batch across vm changes 624 bool isMiterStroke = is_miter(stroke); 625 if (!batch->canAppend(viewMatrix, isMiterStroke)) { 626 return false; 627 } 628 629 SkRect devOutside, devOutsideAssist, devInside; 630 bool isDegenerate; 631 compute_rects(&devOutside, &devOutsideAssist, &devInside, &isDegenerate, viewMatrix, 632 rect, stroke.getWidth(), isMiterStroke); 633 634 batch->appendAndUpdateBounds(color, devOutside, devOutsideAssist, devInside, isDegenerate); 635 return true; 636 } 637 638 }; 639 640 /////////////////////////////////////////////////////////////////////////////////////////////////// 641 642 #ifdef GR_TEST_UTILS 643 644 #include "GrBatchTest.h" 645 646 DRAW_BATCH_TEST_DEFINE(AAStrokeRectBatch) { 647 bool miterStroke = random->nextBool(); 648 649 // Create mock stroke rect 650 SkRect outside = GrTest::TestRect(random); 651 SkScalar minDim = SkMinScalar(outside.width(), outside.height()); 652 SkScalar strokeWidth = minDim * 0.1f; 653 SkRect outsideAssist = outside; 654 outsideAssist.outset(strokeWidth, strokeWidth); 655 SkRect inside = outside; 656 inside.inset(strokeWidth, strokeWidth); 657 658 GrColor color = GrRandomColor(random); 659 660 return GrAAStrokeRectBatch::Create(color, GrTest::TestMatrix(random), outside, outsideAssist, 661 inside, miterStroke, inside.isFinite() && inside.isEmpty()); 662 } 663 664 #endif 665