1 /* 2 * Copyright 2016 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 "GrShadowRRectOp.h" 9 #include "GrDrawOpTest.h" 10 #include "GrOpFlushState.h" 11 #include "SkRRect.h" 12 #include "effects/GrShadowGeoProc.h" 13 14 /////////////////////////////////////////////////////////////////////////////// 15 // Circle Data 16 // 17 // We have two possible cases for geometry for a circle: 18 19 // In the case of a normal fill, we draw geometry for the circle as an octagon. 20 static const uint16_t gFillCircleIndices[] = { 21 // enter the octagon 22 // clang-format off 23 0, 1, 8, 1, 2, 8, 24 2, 3, 8, 3, 4, 8, 25 4, 5, 8, 5, 6, 8, 26 6, 7, 8, 7, 0, 8, 27 // clang-format on 28 }; 29 30 // For stroked circles, we use two nested octagons. 31 static const uint16_t gStrokeCircleIndices[] = { 32 // enter the octagon 33 // clang-format off 34 0, 1, 9, 0, 9, 8, 35 1, 2, 10, 1, 10, 9, 36 2, 3, 11, 2, 11, 10, 37 3, 4, 12, 3, 12, 11, 38 4, 5, 13, 4, 13, 12, 39 5, 6, 14, 5, 14, 13, 40 6, 7, 15, 6, 15, 14, 41 7, 0, 8, 7, 8, 15, 42 // clang-format on 43 }; 44 45 static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices); 46 static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices); 47 static const int kVertsPerStrokeCircle = 16; 48 static const int kVertsPerFillCircle = 9; 49 50 static int circle_type_to_vert_count(bool stroked) { 51 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle; 52 } 53 54 static int circle_type_to_index_count(bool stroked) { 55 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle; 56 } 57 58 static const uint16_t* circle_type_to_indices(bool stroked) { 59 return stroked ? gStrokeCircleIndices : gFillCircleIndices; 60 } 61 62 /////////////////////////////////////////////////////////////////////////////// 63 // RoundRect Data 64 // 65 // The geometry for a shadow roundrect is similar to a 9-patch: 66 // ____________ 67 // |_|________|_| 68 // | | | | 69 // | | | | 70 // | | | | 71 // |_|________|_| 72 // |_|________|_| 73 // 74 // However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram 75 // shows the upper part of the upper left corner. The bottom triangle would similarly be split 76 // into two triangles.) 77 // ________ 78 // |\ \ | 79 // | \ \ | 80 // | \\ | 81 // | \| 82 // -------- 83 // 84 // The center of the fan handles the curve of the corner. For roundrects where the stroke width 85 // is greater than the corner radius, the outer triangles blend from the curve to the straight 86 // sides. Otherwise these triangles will be degenerate. 87 // 88 // In the case where the stroke width is greater than the corner radius and the 89 // blur radius (overstroke), we add additional geometry to mark out the rectangle in the center. 90 // This rectangle extends the coverage values of the center edges of the 9-patch. 91 // ____________ 92 // |_|________|_| 93 // | |\ ____ /| | 94 // | | | | | | 95 // | | |____| | | 96 // |_|/______\|_| 97 // |_|________|_| 98 // 99 // For filled rrects we reuse the stroke geometry but add an additional quad to the center. 100 101 static const uint16_t gRRectIndices[] = { 102 // clang-format off 103 // overstroke quads 104 // we place this at the beginning so that we can skip these indices when rendering as filled 105 0, 6, 25, 0, 25, 24, 106 6, 18, 27, 6, 27, 25, 107 18, 12, 26, 18, 26, 27, 108 12, 0, 24, 12, 24, 26, 109 110 // corners 111 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, 112 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7, 113 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13, 114 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23, 115 116 // edges 117 0, 5, 11, 0, 11, 6, 118 6, 7, 19, 6, 19, 18, 119 18, 23, 17, 18, 17, 12, 120 12, 13, 1, 12, 1, 0, 121 122 // fill quad 123 // we place this at the end so that we can skip these indices when rendering as stroked 124 0, 6, 18, 0, 18, 12, 125 // clang-format on 126 }; 127 128 // overstroke count 129 static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6; 130 // simple stroke count skips overstroke indices 131 static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6*4; 132 // fill count adds final quad to stroke count 133 static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6; 134 static const int kVertsPerStrokeRRect = 24; 135 static const int kVertsPerOverstrokeRRect = 28; 136 static const int kVertsPerFillRRect = 24; 137 138 enum RRectType { 139 kFill_RRectType, 140 kStroke_RRectType, 141 kOverstroke_RRectType, 142 }; 143 144 static int rrect_type_to_vert_count(RRectType type) { 145 switch (type) { 146 case kFill_RRectType: 147 return kVertsPerFillRRect; 148 case kStroke_RRectType: 149 return kVertsPerStrokeRRect; 150 case kOverstroke_RRectType: 151 return kVertsPerOverstrokeRRect; 152 } 153 SkFAIL("Invalid type"); 154 return 0; 155 } 156 157 static int rrect_type_to_index_count(RRectType type) { 158 switch (type) { 159 case kFill_RRectType: 160 return kIndicesPerFillRRect; 161 case kStroke_RRectType: 162 return kIndicesPerStrokeRRect; 163 case kOverstroke_RRectType: 164 return kIndicesPerOverstrokeRRect; 165 } 166 SkFAIL("Invalid type"); 167 return 0; 168 } 169 170 static const uint16_t* rrect_type_to_indices(RRectType type) { 171 switch (type) { 172 case kFill_RRectType: 173 case kStroke_RRectType: 174 return gRRectIndices + 6*4; 175 case kOverstroke_RRectType: 176 return gRRectIndices; 177 } 178 SkFAIL("Invalid type"); 179 return nullptr; 180 } 181 182 /////////////////////////////////////////////////////////////////////////////// 183 namespace { 184 185 class ShadowCircularRRectOp final : public GrMeshDrawOp { 186 public: 187 DEFINE_OP_CLASS_ID 188 189 // An insetWidth > 1/2 rect width or height indicates a simple fill. 190 ShadowCircularRRectOp(GrColor color, const SkRect& devRect, 191 float devRadius, bool isCircle, float blurRadius, float insetWidth, 192 float blurClamp) 193 : INHERITED(ClassID()) { 194 SkRect bounds = devRect; 195 SkASSERT(insetWidth > 0); 196 SkScalar innerRadius = 0.0f; 197 SkScalar outerRadius = devRadius; 198 SkScalar umbraInset; 199 200 RRectType type = kFill_RRectType; 201 if (isCircle) { 202 umbraInset = 0; 203 } else if (insetWidth > 0 && insetWidth <= outerRadius) { 204 // If the client has requested a stroke smaller than the outer radius, 205 // we will assume they want no special umbra inset (this is for ambient shadows). 206 umbraInset = outerRadius; 207 } else { 208 umbraInset = SkTMax(outerRadius, blurRadius); 209 } 210 211 // If stroke is greater than width or height, this is still a fill, 212 // otherwise we compute stroke params. 213 if (isCircle) { 214 innerRadius = devRadius - insetWidth; 215 type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType; 216 } else { 217 if (insetWidth <= 0.5f*SkTMin(devRect.width(), devRect.height())) { 218 // We don't worry about a real inner radius, we just need to know if we 219 // need to create overstroke vertices. 220 innerRadius = SkTMax(insetWidth - umbraInset, 0.0f); 221 type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType; 222 } 223 } 224 225 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo); 226 227 fGeoData.emplace_back(Geometry{color, outerRadius, umbraInset, innerRadius, 228 blurRadius, blurClamp, bounds, type, isCircle}); 229 if (isCircle) { 230 fVertCount = circle_type_to_vert_count(kStroke_RRectType == type); 231 fIndexCount = circle_type_to_index_count(kStroke_RRectType == type); 232 } else { 233 fVertCount = rrect_type_to_vert_count(type); 234 fIndexCount = rrect_type_to_index_count(type); 235 } 236 } 237 238 const char* name() const override { return "ShadowCircularRRectOp"; } 239 240 SkString dumpInfo() const override { 241 SkString string; 242 for (int i = 0; i < fGeoData.count(); ++i) { 243 string.appendf( 244 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]," 245 "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n", 246 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop, 247 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom, 248 fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset, 249 fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius); 250 } 251 string.append(INHERITED::dumpInfo()); 252 return string; 253 } 254 255 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } 256 257 RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*) override { 258 return RequiresDstTexture::kNo; 259 } 260 261 private: 262 struct Geometry { 263 GrColor fColor; 264 SkScalar fOuterRadius; 265 SkScalar fUmbraInset; 266 SkScalar fInnerRadius; 267 SkScalar fBlurRadius; 268 SkScalar fClampValue; 269 SkRect fDevBounds; 270 RRectType fType; 271 bool fIsCircle; 272 }; 273 274 struct CircleVertex { 275 SkPoint fPos; 276 GrColor fColor; 277 SkPoint fOffset; 278 SkScalar fDistanceCorrection; 279 SkScalar fClampValue; 280 }; 281 282 void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const { 283 284 GrColor color = args.fColor; 285 SkScalar outerRadius = args.fOuterRadius; 286 SkScalar innerRadius = args.fInnerRadius; 287 SkScalar blurRadius = args.fBlurRadius; 288 SkScalar distanceCorrection = outerRadius / blurRadius; 289 SkScalar clampValue = args.fClampValue; 290 291 const SkRect& bounds = args.fDevBounds; 292 293 // The inner radius in the vertex data must be specified in normalized space. 294 innerRadius = innerRadius / outerRadius; 295 296 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY()); 297 SkScalar halfWidth = 0.5f * bounds.width(); 298 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1 299 300 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth); 301 (*verts)->fColor = color; 302 (*verts)->fOffset = SkPoint::Make(-octOffset, -1); 303 (*verts)->fDistanceCorrection = distanceCorrection; 304 (*verts)->fClampValue = clampValue; 305 (*verts)++; 306 307 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth); 308 (*verts)->fColor = color; 309 (*verts)->fOffset = SkPoint::Make(octOffset, -1); 310 (*verts)->fDistanceCorrection = distanceCorrection; 311 (*verts)->fClampValue = clampValue; 312 (*verts)++; 313 314 (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth); 315 (*verts)->fColor = color; 316 (*verts)->fOffset = SkPoint::Make(1, -octOffset); 317 (*verts)->fDistanceCorrection = distanceCorrection; 318 (*verts)->fClampValue = clampValue; 319 (*verts)++; 320 321 (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth); 322 (*verts)->fColor = color; 323 (*verts)->fOffset = SkPoint::Make(1, octOffset); 324 (*verts)->fDistanceCorrection = distanceCorrection; 325 (*verts)->fClampValue = clampValue; 326 (*verts)++; 327 328 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth); 329 (*verts)->fColor = color; 330 (*verts)->fOffset = SkPoint::Make(octOffset, 1); 331 (*verts)->fDistanceCorrection = distanceCorrection; 332 (*verts)->fClampValue = clampValue; 333 (*verts)++; 334 335 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth); 336 (*verts)->fColor = color; 337 (*verts)->fOffset = SkPoint::Make(-octOffset, 1); 338 (*verts)->fDistanceCorrection = distanceCorrection; 339 (*verts)->fClampValue = clampValue; 340 (*verts)++; 341 342 (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth); 343 (*verts)->fColor = color; 344 (*verts)->fOffset = SkPoint::Make(-1, octOffset); 345 (*verts)->fDistanceCorrection = distanceCorrection; 346 (*verts)->fClampValue = clampValue; 347 (*verts)++; 348 349 (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth); 350 (*verts)->fColor = color; 351 (*verts)->fOffset = SkPoint::Make(-1, -octOffset); 352 (*verts)->fDistanceCorrection = distanceCorrection; 353 (*verts)->fClampValue = clampValue; 354 (*verts)++; 355 356 if (isStroked) { 357 // compute the inner ring 358 359 // cosine and sine of pi/8 360 SkScalar c = 0.923579533f; 361 SkScalar s = 0.382683432f; 362 SkScalar r = args.fInnerRadius; 363 364 (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r); 365 (*verts)->fColor = color; 366 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius); 367 (*verts)->fDistanceCorrection = distanceCorrection; 368 (*verts)->fClampValue = clampValue; 369 (*verts)++; 370 371 (*verts)->fPos = center + SkPoint::Make(s * r, -c * r); 372 (*verts)->fColor = color; 373 (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius); 374 (*verts)->fDistanceCorrection = distanceCorrection; 375 (*verts)->fClampValue = clampValue; 376 (*verts)++; 377 378 (*verts)->fPos = center + SkPoint::Make(c * r, -s * r); 379 (*verts)->fColor = color; 380 (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius); 381 (*verts)->fDistanceCorrection = distanceCorrection; 382 (*verts)->fClampValue = clampValue; 383 (*verts)++; 384 385 (*verts)->fPos = center + SkPoint::Make(c * r, s * r); 386 (*verts)->fColor = color; 387 (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius); 388 (*verts)->fDistanceCorrection = distanceCorrection; 389 (*verts)->fClampValue = clampValue; 390 (*verts)++; 391 392 (*verts)->fPos = center + SkPoint::Make(s * r, c * r); 393 (*verts)->fColor = color; 394 (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius); 395 (*verts)->fDistanceCorrection = distanceCorrection; 396 (*verts)->fClampValue = clampValue; 397 (*verts)++; 398 399 (*verts)->fPos = center + SkPoint::Make(-s * r, c * r); 400 (*verts)->fColor = color; 401 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius); 402 (*verts)->fDistanceCorrection = distanceCorrection; 403 (*verts)->fClampValue = clampValue; 404 (*verts)++; 405 406 (*verts)->fPos = center + SkPoint::Make(-c * r, s * r); 407 (*verts)->fColor = color; 408 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius); 409 (*verts)->fDistanceCorrection = distanceCorrection; 410 (*verts)->fClampValue = clampValue; 411 (*verts)++; 412 413 (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r); 414 (*verts)->fColor = color; 415 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius); 416 (*verts)->fDistanceCorrection = distanceCorrection; 417 (*verts)->fClampValue = clampValue; 418 (*verts)++; 419 } else { 420 // filled 421 (*verts)->fPos = center; 422 (*verts)->fColor = color; 423 (*verts)->fOffset = SkPoint::Make(0, 0); 424 (*verts)->fDistanceCorrection = distanceCorrection; 425 (*verts)->fClampValue = clampValue; 426 (*verts)++; 427 } 428 } 429 430 void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const { 431 GrColor color = args.fColor; 432 SkScalar outerRadius = args.fOuterRadius; 433 434 const SkRect& bounds = args.fDevBounds; 435 436 SkScalar umbraInset = args.fUmbraInset; 437 SkScalar minDim = 0.5f*SkTMin(bounds.width(), bounds.height()); 438 if (umbraInset > minDim) { 439 umbraInset = minDim; 440 } 441 442 SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset, 443 bounds.fLeft + umbraInset, bounds.fRight - umbraInset }; 444 SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius, 445 bounds.fLeft + outerRadius, bounds.fRight - outerRadius }; 446 SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight, 447 bounds.fLeft, bounds.fRight }; 448 SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset, 449 bounds.fBottom - umbraInset, bounds.fBottom - umbraInset }; 450 SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius, 451 bounds.fBottom - outerRadius, bounds.fBottom - outerRadius }; 452 SkScalar yOuter[4] = { bounds.fTop, bounds.fTop, 453 bounds.fBottom, bounds.fBottom }; 454 455 SkScalar blurRadius = args.fBlurRadius; 456 457 // In the case where we have to inset more for the umbra, our two triangles in the 458 // corner get skewed to a diamond rather than a square. To correct for that, 459 // we also skew the vectors we send to the shader that help define the circle. 460 // By doing so, we end up with a quarter circle in the corner rather than the 461 // elliptical curve. 462 SkVector outerVec = SkVector::Make(0.5f*(outerRadius - umbraInset), -umbraInset); 463 outerVec.normalize(); 464 SkVector diagVec = SkVector::Make(outerVec.fX + outerVec.fY, 465 outerVec.fX + outerVec.fY); 466 diagVec *= umbraInset / (2 * umbraInset - outerRadius); 467 SkScalar distanceCorrection = umbraInset / blurRadius; 468 SkScalar clampValue = args.fClampValue; 469 470 // build corner by corner 471 for (int i = 0; i < 4; ++i) { 472 // inner point 473 (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]); 474 (*verts)->fColor = color; 475 (*verts)->fOffset = SkVector::Make(0, 0); 476 (*verts)->fDistanceCorrection = distanceCorrection; 477 (*verts)->fClampValue = clampValue; 478 (*verts)++; 479 480 // outer points 481 (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]); 482 (*verts)->fColor = color; 483 (*verts)->fOffset = SkVector::Make(0, -1); 484 (*verts)->fDistanceCorrection = distanceCorrection; 485 (*verts)->fClampValue = clampValue; 486 (*verts)++; 487 488 (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]); 489 (*verts)->fColor = color; 490 (*verts)->fOffset = outerVec; 491 (*verts)->fDistanceCorrection = distanceCorrection; 492 (*verts)->fClampValue = clampValue; 493 (*verts)++; 494 495 (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]); 496 (*verts)->fColor = color; 497 (*verts)->fOffset = diagVec; 498 (*verts)->fDistanceCorrection = distanceCorrection; 499 (*verts)->fClampValue = clampValue; 500 (*verts)++; 501 502 (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]); 503 (*verts)->fColor = color; 504 (*verts)->fOffset = outerVec; 505 (*verts)->fDistanceCorrection = distanceCorrection; 506 (*verts)->fClampValue = clampValue; 507 (*verts)++; 508 509 (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]); 510 (*verts)->fColor = color; 511 (*verts)->fOffset = SkVector::Make(0, -1); 512 (*verts)->fDistanceCorrection = distanceCorrection; 513 (*verts)->fClampValue = clampValue; 514 (*verts)++; 515 } 516 517 // Add the additional vertices for overstroked rrects. 518 // Effectively this is an additional stroked rrect, with its 519 // parameters equal to those in the center of the 9-patch. This will 520 // give constant values across this inner ring. 521 if (kOverstroke_RRectType == args.fType) { 522 SkASSERT(args.fInnerRadius > 0.0f); 523 524 SkScalar inset = umbraInset + args.fInnerRadius; 525 526 // TL 527 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset); 528 (*verts)->fColor = color; 529 (*verts)->fOffset = SkPoint::Make(0, 0); 530 (*verts)->fDistanceCorrection = distanceCorrection; 531 (*verts)->fClampValue = clampValue; 532 (*verts)++; 533 534 // TR 535 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset); 536 (*verts)->fColor = color; 537 (*verts)->fOffset = SkPoint::Make(0, 0); 538 (*verts)->fDistanceCorrection = distanceCorrection; 539 (*verts)->fClampValue = clampValue; 540 (*verts)++; 541 542 // BL 543 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset); 544 (*verts)->fColor = color; 545 (*verts)->fOffset = SkPoint::Make(0, 0); 546 (*verts)->fDistanceCorrection = distanceCorrection; 547 (*verts)->fClampValue = clampValue; 548 (*verts)++; 549 550 // BR 551 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset); 552 (*verts)->fColor = color; 553 (*verts)->fOffset = SkPoint::Make(0, 0); 554 (*verts)->fDistanceCorrection = distanceCorrection; 555 (*verts)->fClampValue = clampValue; 556 (*verts)++; 557 } 558 559 } 560 561 void onPrepareDraws(Target* target) const override { 562 // Setup geometry processor 563 sk_sp<GrGeometryProcessor> gp = GrRRectShadowGeoProc::Make(); 564 565 int instanceCount = fGeoData.count(); 566 size_t vertexStride = gp->getVertexStride(); 567 SkASSERT(sizeof(CircleVertex) == vertexStride); 568 569 const GrBuffer* vertexBuffer; 570 int firstVertex; 571 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount, 572 &vertexBuffer, &firstVertex); 573 if (!verts) { 574 SkDebugf("Could not allocate vertices\n"); 575 return; 576 } 577 578 const GrBuffer* indexBuffer = nullptr; 579 int firstIndex = 0; 580 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); 581 if (!indices) { 582 SkDebugf("Could not allocate indices\n"); 583 return; 584 } 585 586 int currStartVertex = 0; 587 for (int i = 0; i < instanceCount; i++) { 588 const Geometry& args = fGeoData[i]; 589 590 if (args.fIsCircle) { 591 bool isStroked = SkToBool(kStroke_RRectType == args.fType); 592 this->fillInCircleVerts(args, isStroked, &verts); 593 594 const uint16_t* primIndices = circle_type_to_indices(isStroked); 595 const int primIndexCount = circle_type_to_index_count(isStroked); 596 for (int i = 0; i < primIndexCount; ++i) { 597 *indices++ = primIndices[i] + currStartVertex; 598 } 599 600 currStartVertex += circle_type_to_vert_count(isStroked); 601 602 } else { 603 this->fillInRRectVerts(args, &verts); 604 605 const uint16_t* primIndices = rrect_type_to_indices(args.fType); 606 const int primIndexCount = rrect_type_to_index_count(args.fType); 607 for (int i = 0; i < primIndexCount; ++i) { 608 *indices++ = primIndices[i] + currStartVertex; 609 } 610 611 currStartVertex += rrect_type_to_vert_count(args.fType); 612 } 613 } 614 615 static const uint32_t kPipelineFlags = 0; 616 const GrPipeline* pipeline = 617 target->makePipeline(kPipelineFlags, &GrProcessorSet::EmptySet()); 618 619 GrMesh mesh(GrPrimitiveType::kTriangles); 620 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1); 621 mesh.setVertexData(vertexBuffer, firstVertex); 622 target->draw(gp.get(), pipeline, mesh); 623 } 624 625 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 626 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>(); 627 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); 628 this->joinBounds(*that); 629 fVertCount += that->fVertCount; 630 fIndexCount += that->fIndexCount; 631 return true; 632 } 633 634 SkSTArray<1, Geometry, true> fGeoData; 635 int fVertCount; 636 int fIndexCount; 637 638 typedef GrMeshDrawOp INHERITED; 639 }; 640 641 } // anonymous namespace 642 643 /////////////////////////////////////////////////////////////////////////////// 644 645 namespace GrShadowRRectOp { 646 std::unique_ptr<GrDrawOp> Make(GrColor color, 647 const SkMatrix& viewMatrix, 648 const SkRRect& rrect, 649 SkScalar blurWidth, 650 SkScalar insetWidth, 651 SkScalar blurClamp) { 652 // Shadow rrect ops only handle simple circular rrects. 653 SkASSERT(viewMatrix.isSimilarity() && 654 (rrect.isSimpleCircular() || rrect.isRect() || rrect.isCircle())); 655 656 // Do any matrix crunching before we reset the draw state for device coords. 657 const SkRect& rrectBounds = rrect.getBounds(); 658 SkRect bounds; 659 viewMatrix.mapRect(&bounds, rrectBounds); 660 661 // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic. 662 SkScalar radius = rrect.getSimpleRadii().fX; 663 SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX]; 664 SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor); 665 SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor); 666 667 return std::unique_ptr<GrDrawOp>(new ShadowCircularRRectOp(color, bounds, 668 scaledRadius, 669 rrect.isOval(), 670 blurWidth, 671 scaledInsetWidth, 672 blurClamp)); 673 } 674 } 675 676 /////////////////////////////////////////////////////////////////////////////// 677 678 #if GR_TEST_UTILS 679 680 GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) { 681 // create a similarity matrix 682 SkScalar rotate = random->nextSScalar1() * 360.f; 683 SkScalar translateX = random->nextSScalar1() * 1000.f; 684 SkScalar translateY = random->nextSScalar1() * 1000.f; 685 SkScalar scale = random->nextSScalar1() * 100.f; 686 SkMatrix viewMatrix; 687 viewMatrix.setRotate(rotate); 688 viewMatrix.postTranslate(translateX, translateY); 689 viewMatrix.postScale(scale, scale); 690 SkScalar insetWidth = random->nextSScalar1() * 72.f; 691 SkScalar blurWidth = random->nextSScalar1() * 72.f; 692 SkScalar blurClamp = random->nextSScalar1(); 693 bool isCircle = random->nextBool(); 694 // This op doesn't use a full GrPaint, just a color. 695 GrColor color = paint.getColor(); 696 if (isCircle) { 697 SkRect circle = GrTest::TestSquare(random); 698 SkRRect rrect = SkRRect::MakeOval(circle); 699 return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurWidth, insetWidth, blurClamp); 700 } else { 701 SkRRect rrect; 702 do { 703 // This may return a rrect with elliptical corners, which we don't support. 704 rrect = GrTest::TestRRectSimple(random); 705 } while (!rrect.isSimpleCircular()); 706 return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurWidth, insetWidth, blurClamp); 707 } 708 } 709 710 #endif 711