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 "SkRRectPriv.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. 20static 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. 31static 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 45static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices); 46static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices); 47static const int kVertsPerStrokeCircle = 16; 48static const int kVertsPerFillCircle = 9; 49 50static int circle_type_to_vert_count(bool stroked) { 51 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle; 52} 53 54static int circle_type_to_index_count(bool stroked) { 55 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle; 56} 57 58static 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 101static 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 129static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6; 130// simple stroke count skips overstroke indices 131static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6*4; 132// fill count adds final quad to stroke count 133static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6; 134static const int kVertsPerStrokeRRect = 24; 135static const int kVertsPerOverstrokeRRect = 28; 136static const int kVertsPerFillRRect = 24; 137 138enum RRectType { 139 kFill_RRectType, 140 kStroke_RRectType, 141 kOverstroke_RRectType, 142}; 143 144static 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 SK_ABORT("Invalid type"); 154 return 0; 155} 156 157static 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 SK_ABORT("Invalid type"); 167 return 0; 168} 169 170static 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 SK_ABORT("Invalid type"); 179 return nullptr; 180} 181 182/////////////////////////////////////////////////////////////////////////////// 183namespace { 184 185class ShadowCircularRRectOp final : public GrMeshDrawOp { 186public: 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*, 258 GrPixelConfigIsClamped) override { 259 return RequiresDstTexture::kNo; 260 } 261 262private: 263 struct Geometry { 264 GrColor fColor; 265 SkScalar fOuterRadius; 266 SkScalar fUmbraInset; 267 SkScalar fInnerRadius; 268 SkScalar fBlurRadius; 269 SkScalar fClampValue; 270 SkRect fDevBounds; 271 RRectType fType; 272 bool fIsCircle; 273 }; 274 275 struct CircleVertex { 276 SkPoint fPos; 277 GrColor fColor; 278 SkPoint fOffset; 279 SkScalar fDistanceCorrection; 280 SkScalar fClampValue; 281 }; 282 283 void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const { 284 285 GrColor color = args.fColor; 286 SkScalar outerRadius = args.fOuterRadius; 287 SkScalar innerRadius = args.fInnerRadius; 288 SkScalar blurRadius = args.fBlurRadius; 289 SkScalar distanceCorrection = outerRadius / blurRadius; 290 SkScalar clampValue = args.fClampValue; 291 292 const SkRect& bounds = args.fDevBounds; 293 294 // The inner radius in the vertex data must be specified in normalized space. 295 innerRadius = innerRadius / outerRadius; 296 297 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY()); 298 SkScalar halfWidth = 0.5f * bounds.width(); 299 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1 300 301 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth); 302 (*verts)->fColor = color; 303 (*verts)->fOffset = SkPoint::Make(-octOffset, -1); 304 (*verts)->fDistanceCorrection = distanceCorrection; 305 (*verts)->fClampValue = clampValue; 306 (*verts)++; 307 308 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth); 309 (*verts)->fColor = color; 310 (*verts)->fOffset = SkPoint::Make(octOffset, -1); 311 (*verts)->fDistanceCorrection = distanceCorrection; 312 (*verts)->fClampValue = clampValue; 313 (*verts)++; 314 315 (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth); 316 (*verts)->fColor = color; 317 (*verts)->fOffset = SkPoint::Make(1, -octOffset); 318 (*verts)->fDistanceCorrection = distanceCorrection; 319 (*verts)->fClampValue = clampValue; 320 (*verts)++; 321 322 (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth); 323 (*verts)->fColor = color; 324 (*verts)->fOffset = SkPoint::Make(1, octOffset); 325 (*verts)->fDistanceCorrection = distanceCorrection; 326 (*verts)->fClampValue = clampValue; 327 (*verts)++; 328 329 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth); 330 (*verts)->fColor = color; 331 (*verts)->fOffset = SkPoint::Make(octOffset, 1); 332 (*verts)->fDistanceCorrection = distanceCorrection; 333 (*verts)->fClampValue = clampValue; 334 (*verts)++; 335 336 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth); 337 (*verts)->fColor = color; 338 (*verts)->fOffset = SkPoint::Make(-octOffset, 1); 339 (*verts)->fDistanceCorrection = distanceCorrection; 340 (*verts)->fClampValue = clampValue; 341 (*verts)++; 342 343 (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth); 344 (*verts)->fColor = color; 345 (*verts)->fOffset = SkPoint::Make(-1, octOffset); 346 (*verts)->fDistanceCorrection = distanceCorrection; 347 (*verts)->fClampValue = clampValue; 348 (*verts)++; 349 350 (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth); 351 (*verts)->fColor = color; 352 (*verts)->fOffset = SkPoint::Make(-1, -octOffset); 353 (*verts)->fDistanceCorrection = distanceCorrection; 354 (*verts)->fClampValue = clampValue; 355 (*verts)++; 356 357 if (isStroked) { 358 // compute the inner ring 359 360 // cosine and sine of pi/8 361 SkScalar c = 0.923579533f; 362 SkScalar s = 0.382683432f; 363 SkScalar r = args.fInnerRadius; 364 365 (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r); 366 (*verts)->fColor = color; 367 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius); 368 (*verts)->fDistanceCorrection = distanceCorrection; 369 (*verts)->fClampValue = clampValue; 370 (*verts)++; 371 372 (*verts)->fPos = center + SkPoint::Make(s * r, -c * r); 373 (*verts)->fColor = color; 374 (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius); 375 (*verts)->fDistanceCorrection = distanceCorrection; 376 (*verts)->fClampValue = clampValue; 377 (*verts)++; 378 379 (*verts)->fPos = center + SkPoint::Make(c * r, -s * r); 380 (*verts)->fColor = color; 381 (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius); 382 (*verts)->fDistanceCorrection = distanceCorrection; 383 (*verts)->fClampValue = clampValue; 384 (*verts)++; 385 386 (*verts)->fPos = center + SkPoint::Make(c * r, s * r); 387 (*verts)->fColor = color; 388 (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius); 389 (*verts)->fDistanceCorrection = distanceCorrection; 390 (*verts)->fClampValue = clampValue; 391 (*verts)++; 392 393 (*verts)->fPos = center + SkPoint::Make(s * r, c * r); 394 (*verts)->fColor = color; 395 (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius); 396 (*verts)->fDistanceCorrection = distanceCorrection; 397 (*verts)->fClampValue = clampValue; 398 (*verts)++; 399 400 (*verts)->fPos = center + SkPoint::Make(-s * r, c * r); 401 (*verts)->fColor = color; 402 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius); 403 (*verts)->fDistanceCorrection = distanceCorrection; 404 (*verts)->fClampValue = clampValue; 405 (*verts)++; 406 407 (*verts)->fPos = center + SkPoint::Make(-c * r, s * r); 408 (*verts)->fColor = color; 409 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius); 410 (*verts)->fDistanceCorrection = distanceCorrection; 411 (*verts)->fClampValue = clampValue; 412 (*verts)++; 413 414 (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r); 415 (*verts)->fColor = color; 416 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius); 417 (*verts)->fDistanceCorrection = distanceCorrection; 418 (*verts)->fClampValue = clampValue; 419 (*verts)++; 420 } else { 421 // filled 422 (*verts)->fPos = center; 423 (*verts)->fColor = color; 424 (*verts)->fOffset = SkPoint::Make(0, 0); 425 (*verts)->fDistanceCorrection = distanceCorrection; 426 (*verts)->fClampValue = clampValue; 427 (*verts)++; 428 } 429 } 430 431 void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const { 432 GrColor color = args.fColor; 433 SkScalar outerRadius = args.fOuterRadius; 434 435 const SkRect& bounds = args.fDevBounds; 436 437 SkScalar umbraInset = args.fUmbraInset; 438 SkScalar minDim = 0.5f*SkTMin(bounds.width(), bounds.height()); 439 if (umbraInset > minDim) { 440 umbraInset = minDim; 441 } 442 443 SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset, 444 bounds.fLeft + umbraInset, bounds.fRight - umbraInset }; 445 SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius, 446 bounds.fLeft + outerRadius, bounds.fRight - outerRadius }; 447 SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight, 448 bounds.fLeft, bounds.fRight }; 449 SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset, 450 bounds.fBottom - umbraInset, bounds.fBottom - umbraInset }; 451 SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius, 452 bounds.fBottom - outerRadius, bounds.fBottom - outerRadius }; 453 SkScalar yOuter[4] = { bounds.fTop, bounds.fTop, 454 bounds.fBottom, bounds.fBottom }; 455 456 SkScalar blurRadius = args.fBlurRadius; 457 458 // In the case where we have to inset more for the umbra, our two triangles in the 459 // corner get skewed to a diamond rather than a square. To correct for that, 460 // we also skew the vectors we send to the shader that help define the circle. 461 // By doing so, we end up with a quarter circle in the corner rather than the 462 // elliptical curve. 463 SkVector outerVec = SkVector::Make(0.5f*(outerRadius - umbraInset), -umbraInset); 464 outerVec.normalize(); 465 SkVector diagVec = SkVector::Make(outerVec.fX + outerVec.fY, 466 outerVec.fX + outerVec.fY); 467 diagVec *= umbraInset / (2 * umbraInset - outerRadius); 468 SkScalar distanceCorrection = umbraInset / blurRadius; 469 SkScalar clampValue = args.fClampValue; 470 471 // build corner by corner 472 for (int i = 0; i < 4; ++i) { 473 // inner point 474 (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]); 475 (*verts)->fColor = color; 476 (*verts)->fOffset = SkVector::Make(0, 0); 477 (*verts)->fDistanceCorrection = distanceCorrection; 478 (*verts)->fClampValue = clampValue; 479 (*verts)++; 480 481 // outer points 482 (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]); 483 (*verts)->fColor = color; 484 (*verts)->fOffset = SkVector::Make(0, -1); 485 (*verts)->fDistanceCorrection = distanceCorrection; 486 (*verts)->fClampValue = clampValue; 487 (*verts)++; 488 489 (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]); 490 (*verts)->fColor = color; 491 (*verts)->fOffset = outerVec; 492 (*verts)->fDistanceCorrection = distanceCorrection; 493 (*verts)->fClampValue = clampValue; 494 (*verts)++; 495 496 (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]); 497 (*verts)->fColor = color; 498 (*verts)->fOffset = diagVec; 499 (*verts)->fDistanceCorrection = distanceCorrection; 500 (*verts)->fClampValue = clampValue; 501 (*verts)++; 502 503 (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]); 504 (*verts)->fColor = color; 505 (*verts)->fOffset = outerVec; 506 (*verts)->fDistanceCorrection = distanceCorrection; 507 (*verts)->fClampValue = clampValue; 508 (*verts)++; 509 510 (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]); 511 (*verts)->fColor = color; 512 (*verts)->fOffset = SkVector::Make(0, -1); 513 (*verts)->fDistanceCorrection = distanceCorrection; 514 (*verts)->fClampValue = clampValue; 515 (*verts)++; 516 } 517 518 // Add the additional vertices for overstroked rrects. 519 // Effectively this is an additional stroked rrect, with its 520 // parameters equal to those in the center of the 9-patch. This will 521 // give constant values across this inner ring. 522 if (kOverstroke_RRectType == args.fType) { 523 SkASSERT(args.fInnerRadius > 0.0f); 524 525 SkScalar inset = umbraInset + args.fInnerRadius; 526 527 // TL 528 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset); 529 (*verts)->fColor = color; 530 (*verts)->fOffset = SkPoint::Make(0, 0); 531 (*verts)->fDistanceCorrection = distanceCorrection; 532 (*verts)->fClampValue = clampValue; 533 (*verts)++; 534 535 // TR 536 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset); 537 (*verts)->fColor = color; 538 (*verts)->fOffset = SkPoint::Make(0, 0); 539 (*verts)->fDistanceCorrection = distanceCorrection; 540 (*verts)->fClampValue = clampValue; 541 (*verts)++; 542 543 // BL 544 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset); 545 (*verts)->fColor = color; 546 (*verts)->fOffset = SkPoint::Make(0, 0); 547 (*verts)->fDistanceCorrection = distanceCorrection; 548 (*verts)->fClampValue = clampValue; 549 (*verts)++; 550 551 // BR 552 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset); 553 (*verts)->fColor = color; 554 (*verts)->fOffset = SkPoint::Make(0, 0); 555 (*verts)->fDistanceCorrection = distanceCorrection; 556 (*verts)->fClampValue = clampValue; 557 (*verts)++; 558 } 559 560 } 561 562 void onPrepareDraws(Target* target) override { 563 // Setup geometry processor 564 sk_sp<GrGeometryProcessor> gp = GrRRectShadowGeoProc::Make(); 565 566 int instanceCount = fGeoData.count(); 567 size_t vertexStride = gp->getVertexStride(); 568 SkASSERT(sizeof(CircleVertex) == vertexStride); 569 570 const GrBuffer* vertexBuffer; 571 int firstVertex; 572 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount, 573 &vertexBuffer, &firstVertex); 574 if (!verts) { 575 SkDebugf("Could not allocate vertices\n"); 576 return; 577 } 578 579 const GrBuffer* indexBuffer = nullptr; 580 int firstIndex = 0; 581 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); 582 if (!indices) { 583 SkDebugf("Could not allocate indices\n"); 584 return; 585 } 586 587 int currStartVertex = 0; 588 for (int i = 0; i < instanceCount; i++) { 589 const Geometry& args = fGeoData[i]; 590 591 if (args.fIsCircle) { 592 bool isStroked = SkToBool(kStroke_RRectType == args.fType); 593 this->fillInCircleVerts(args, isStroked, &verts); 594 595 const uint16_t* primIndices = circle_type_to_indices(isStroked); 596 const int primIndexCount = circle_type_to_index_count(isStroked); 597 for (int i = 0; i < primIndexCount; ++i) { 598 *indices++ = primIndices[i] + currStartVertex; 599 } 600 601 currStartVertex += circle_type_to_vert_count(isStroked); 602 603 } else { 604 this->fillInRRectVerts(args, &verts); 605 606 const uint16_t* primIndices = rrect_type_to_indices(args.fType); 607 const int primIndexCount = rrect_type_to_index_count(args.fType); 608 for (int i = 0; i < primIndexCount; ++i) { 609 *indices++ = primIndices[i] + currStartVertex; 610 } 611 612 currStartVertex += rrect_type_to_vert_count(args.fType); 613 } 614 } 615 616 static const uint32_t kPipelineFlags = 0; 617 const GrPipeline* pipeline = target->makePipeline( 618 kPipelineFlags, GrProcessorSet::MakeEmptySet(), target->detachAppliedClip()); 619 620 GrMesh mesh(GrPrimitiveType::kTriangles); 621 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1); 622 mesh.setVertexData(vertexBuffer, firstVertex); 623 target->draw(gp.get(), pipeline, mesh); 624 } 625 626 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 627 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>(); 628 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin()); 629 this->joinBounds(*that); 630 fVertCount += that->fVertCount; 631 fIndexCount += that->fIndexCount; 632 return true; 633 } 634 635 SkSTArray<1, Geometry, true> fGeoData; 636 int fVertCount; 637 int fIndexCount; 638 639 typedef GrMeshDrawOp INHERITED; 640}; 641 642} // anonymous namespace 643 644/////////////////////////////////////////////////////////////////////////////// 645 646namespace GrShadowRRectOp { 647std::unique_ptr<GrDrawOp> Make(GrColor color, 648 const SkMatrix& viewMatrix, 649 const SkRRect& rrect, 650 SkScalar blurWidth, 651 SkScalar insetWidth, 652 SkScalar blurClamp) { 653 // Shadow rrect ops only handle simple circular rrects. 654 SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect)); 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 = SkRRectPriv::GetSimpleRadii(rrect).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 680GR_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 (!SkRRectPriv::IsSimpleCircular(rrect)); 706 return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurWidth, insetWidth, blurClamp); 707 } 708} 709 710#endif 711