1 /* 2 * Copyright 2017 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 "SkShadowTessellator.h" 9 #include "SkColorPriv.h" 10 #include "SkGeometry.h" 11 #include "SkInsetConvexPolygon.h" 12 #include "SkPath.h" 13 #include "SkVertices.h" 14 15 #if SK_SUPPORT_GPU 16 #include "GrPathUtils.h" 17 #endif 18 19 20 /** 21 * Base class 22 */ 23 class SkBaseShadowTessellator { 24 public: 25 SkBaseShadowTessellator(SkShadowTessellator::HeightFunc, bool transparent); 26 virtual ~SkBaseShadowTessellator() {} 27 28 sk_sp<SkVertices> releaseVertices() { 29 if (!fSucceeded) { 30 return nullptr; 31 } 32 return SkVertices::MakeCopy(SkCanvas::kTriangles_VertexMode, this->vertexCount(), 33 fPositions.begin(), nullptr, fColors.begin(), 34 this->indexCount(), fIndices.begin()); 35 } 36 37 protected: 38 static constexpr auto kMinHeight = 0.1f; 39 40 int vertexCount() const { return fPositions.count(); } 41 int indexCount() const { return fIndices.count(); } 42 43 bool setZOffset(const SkRect& bounds, bool perspective); 44 45 virtual void handleLine(const SkPoint& p) = 0; 46 void handleLine(const SkMatrix& m, SkPoint* p); 47 48 void handleQuad(const SkPoint pts[3]); 49 void handleQuad(const SkMatrix& m, SkPoint pts[3]); 50 51 void handleCubic(const SkMatrix& m, SkPoint pts[4]); 52 53 void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w); 54 55 bool setTransformedHeightFunc(const SkMatrix& ctm); 56 57 void addArc(const SkVector& nextNormal, bool finishArc); 58 59 SkShadowTessellator::HeightFunc fHeightFunc; 60 std::function<SkScalar(const SkPoint&)> fTransformedHeightFunc; 61 SkScalar fZOffset; 62 // members for perspective height function 63 SkScalar fZParams[3]; 64 SkScalar fPartialDeterminants[3]; 65 66 // first two points 67 SkTDArray<SkPoint> fInitPoints; 68 // temporary buffer 69 SkTDArray<SkPoint> fPointBuffer; 70 71 SkTDArray<SkPoint> fPositions; 72 SkTDArray<SkColor> fColors; 73 SkTDArray<uint16_t> fIndices; 74 75 int fFirstVertex; 76 SkVector fFirstNormal; 77 SkPoint fFirstPoint; 78 79 bool fSucceeded; 80 bool fTransparent; 81 82 SkColor fUmbraColor; 83 SkColor fPenumbraColor; 84 85 SkScalar fRadius; 86 SkScalar fDirection; 87 int fPrevUmbraIndex; 88 SkVector fPrevNormal; 89 SkPoint fPrevPoint; 90 }; 91 92 static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir, 93 SkVector* newNormal) { 94 SkVector normal; 95 // compute perpendicular 96 normal.fX = p0.fY - p1.fY; 97 normal.fY = p1.fX - p0.fX; 98 normal *= dir; 99 if (!normal.normalize()) { 100 return false; 101 } 102 *newNormal = normal; 103 return true; 104 } 105 106 static void compute_radial_steps(const SkVector& v1, const SkVector& v2, SkScalar r, 107 SkScalar* rotSin, SkScalar* rotCos, int* n) { 108 const SkScalar kRecipPixelsPerArcSegment = 0.25f; 109 110 SkScalar rCos = v1.dot(v2); 111 SkScalar rSin = v1.cross(v2); 112 SkScalar theta = SkScalarATan2(rSin, rCos); 113 114 SkScalar steps = r*theta*kRecipPixelsPerArcSegment; 115 116 SkScalar dTheta = theta / steps; 117 *rotSin = SkScalarSinCos(dTheta, rotCos); 118 *n = SkScalarFloorToInt(steps); 119 } 120 121 SkBaseShadowTessellator::SkBaseShadowTessellator(SkShadowTessellator::HeightFunc heightFunc, 122 bool transparent) 123 : fHeightFunc(heightFunc) 124 , fZOffset(0) 125 , fFirstVertex(-1) 126 , fSucceeded(false) 127 , fTransparent(transparent) 128 , fDirection(1) 129 , fPrevUmbraIndex(-1) { 130 fInitPoints.setReserve(3); 131 132 // child classes will set reserve for positions, colors and indices 133 } 134 135 bool SkBaseShadowTessellator::setZOffset(const SkRect& bounds, bool perspective) { 136 SkScalar minZ = fHeightFunc(bounds.fLeft, bounds.fTop); 137 if (perspective) { 138 SkScalar z = fHeightFunc(bounds.fLeft, bounds.fBottom); 139 if (z < minZ) { 140 minZ = z; 141 } 142 z = fHeightFunc(bounds.fRight, bounds.fTop); 143 if (z < minZ) { 144 minZ = z; 145 } 146 z = fHeightFunc(bounds.fRight, bounds.fBottom); 147 if (z < minZ) { 148 minZ = z; 149 } 150 } 151 152 if (minZ < kMinHeight) { 153 fZOffset = -minZ + kMinHeight; 154 return true; 155 } 156 157 return false; 158 } 159 160 // tesselation tolerance values, in device space pixels 161 #if SK_SUPPORT_GPU 162 static const SkScalar kQuadTolerance = 0.2f; 163 static const SkScalar kCubicTolerance = 0.2f; 164 #endif 165 static const SkScalar kConicTolerance = 0.5f; 166 167 void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) { 168 m.mapPoints(p, 1); 169 this->handleLine(*p); 170 } 171 172 void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) { 173 #if SK_SUPPORT_GPU 174 // TODO: Pull PathUtils out of Ganesh? 175 int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance); 176 fPointBuffer.setReserve(maxCount); 177 SkPoint* target = fPointBuffer.begin(); 178 int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2], 179 kQuadTolerance, &target, maxCount); 180 fPointBuffer.setCount(count); 181 for (int i = 0; i < count; i++) { 182 this->handleLine(fPointBuffer[i]); 183 } 184 #else 185 // for now, just to draw something 186 this->handleLine(pts[1]); 187 this->handleLine(pts[2]); 188 #endif 189 } 190 191 void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) { 192 m.mapPoints(pts, 3); 193 this->handleQuad(pts); 194 } 195 196 void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) { 197 m.mapPoints(pts, 4); 198 #if SK_SUPPORT_GPU 199 // TODO: Pull PathUtils out of Ganesh? 200 int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance); 201 fPointBuffer.setReserve(maxCount); 202 SkPoint* target = fPointBuffer.begin(); 203 int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3], 204 kCubicTolerance, &target, maxCount); 205 fPointBuffer.setCount(count); 206 for (int i = 0; i < count; i++) { 207 this->handleLine(fPointBuffer[i]); 208 } 209 #else 210 // for now, just to draw something 211 this->handleLine(pts[1]); 212 this->handleLine(pts[2]); 213 this->handleLine(pts[3]); 214 #endif 215 } 216 217 void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) { 218 if (m.hasPerspective()) { 219 w = SkConic::TransformW(pts, w, m); 220 } 221 m.mapPoints(pts, 3); 222 SkAutoConicToQuads quadder; 223 const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance); 224 SkPoint lastPoint = *(quads++); 225 int count = quadder.countQuads(); 226 for (int i = 0; i < count; ++i) { 227 SkPoint quadPts[3]; 228 quadPts[0] = lastPoint; 229 quadPts[1] = quads[0]; 230 quadPts[2] = i == count - 1 ? pts[2] : quads[1]; 231 this->handleQuad(quadPts); 232 lastPoint = quadPts[2]; 233 quads += 2; 234 } 235 } 236 237 void SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc) { 238 // fill in fan from previous quad 239 SkScalar rotSin, rotCos; 240 int numSteps; 241 compute_radial_steps(fPrevNormal, nextNormal, fRadius, &rotSin, &rotCos, &numSteps); 242 SkVector prevNormal = fPrevNormal; 243 for (int i = 0; i < numSteps; ++i) { 244 SkVector currNormal; 245 currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin; 246 currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin; 247 *fPositions.push() = fPrevPoint + currNormal; 248 *fColors.push() = fPenumbraColor; 249 *fIndices.push() = fPrevUmbraIndex; 250 *fIndices.push() = fPositions.count() - 2; 251 *fIndices.push() = fPositions.count() - 1; 252 253 prevNormal = currNormal; 254 } 255 if (finishArc) { 256 *fPositions.push() = fPrevPoint + nextNormal; 257 *fColors.push() = fPenumbraColor; 258 *fIndices.push() = fPrevUmbraIndex; 259 *fIndices.push() = fPositions.count() - 2; 260 *fIndices.push() = fPositions.count() - 1; 261 } 262 fPrevNormal = nextNormal; 263 } 264 265 bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) { 266 if (!ctm.hasPerspective()) { 267 fTransformedHeightFunc = [this](const SkPoint& p) { 268 return this->fHeightFunc(0, 0); 269 }; 270 } else { 271 SkMatrix ctmInverse; 272 if (!ctm.invert(&ctmInverse)) { 273 return false; 274 } 275 SkScalar C = fHeightFunc(0, 0); 276 SkScalar A = fHeightFunc(1, 0) - C; 277 SkScalar B = fHeightFunc(0, 1) - C; 278 279 // multiply by transpose 280 fZParams[0] = ctmInverse[SkMatrix::kMScaleX] * A + 281 ctmInverse[SkMatrix::kMSkewY] * B + 282 ctmInverse[SkMatrix::kMPersp0] * C; 283 fZParams[1] = ctmInverse[SkMatrix::kMSkewX] * A + 284 ctmInverse[SkMatrix::kMScaleY] * B + 285 ctmInverse[SkMatrix::kMPersp1] * C; 286 fZParams[2] = ctmInverse[SkMatrix::kMTransX] * A + 287 ctmInverse[SkMatrix::kMTransY] * B + 288 ctmInverse[SkMatrix::kMPersp2] * C; 289 290 // We use Cramer's rule to solve for the W value for a given post-divide X and Y, 291 // so pre-compute those values that are independent of X and Y. 292 // W is det(ctmInverse)/(PD[0]*X + PD[1]*Y + PD[2]) 293 fPartialDeterminants[0] = ctm[SkMatrix::kMSkewY] * ctm[SkMatrix::kMPersp1] - 294 ctm[SkMatrix::kMScaleY] * ctm[SkMatrix::kMPersp0]; 295 fPartialDeterminants[1] = ctm[SkMatrix::kMPersp0] * ctm[SkMatrix::kMSkewX] - 296 ctm[SkMatrix::kMPersp1] * ctm[SkMatrix::kMScaleX]; 297 fPartialDeterminants[2] = ctm[SkMatrix::kMScaleX] * ctm[SkMatrix::kMScaleY] - 298 ctm[SkMatrix::kMSkewX] * ctm[SkMatrix::kMSkewY]; 299 SkScalar ctmDeterminant = ctm[SkMatrix::kMTransX] * fPartialDeterminants[0] + 300 ctm[SkMatrix::kMTransY] * fPartialDeterminants[1] + 301 ctm[SkMatrix::kMPersp2] * fPartialDeterminants[2]; 302 303 // Pre-bake the numerator of Cramer's rule into the zParams to avoid another multiply. 304 // TODO: this may introduce numerical instability, but I haven't seen any issues yet. 305 fZParams[0] *= ctmDeterminant; 306 fZParams[1] *= ctmDeterminant; 307 fZParams[2] *= ctmDeterminant; 308 309 fTransformedHeightFunc = [this](const SkPoint& p) { 310 SkScalar denom = p.fX * this->fPartialDeterminants[0] + 311 p.fY * this->fPartialDeterminants[1] + 312 this->fPartialDeterminants[2]; 313 SkScalar w = SkScalarFastInvert(denom); 314 return (this->fZParams[0] * p.fX + this->fZParams[1] * p.fY + this->fZParams[2])*w + 315 this->fZOffset; 316 }; 317 } 318 319 return true; 320 } 321 322 323 ////////////////////////////////////////////////////////////////////////////////////////////////// 324 325 class SkAmbientShadowTessellator : public SkBaseShadowTessellator { 326 public: 327 SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm, 328 SkShadowTessellator::HeightFunc heightFunc, 329 SkScalar ambientAlpha, bool transparent); 330 331 private: 332 void handleLine(const SkPoint& p) override; 333 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal); 334 335 static constexpr auto kHeightFactor = 1.0f / 128.0f; 336 static constexpr auto kGeomFactor = 64.0f; 337 static constexpr auto kMaxEdgeLenSqr = 20 * 20; 338 339 SkScalar offset(SkScalar z) { 340 return z * kHeightFactor * kGeomFactor; 341 } 342 SkColor umbraColor(SkScalar z) { 343 SkScalar umbraAlpha = SkScalarInvert((1.0f + SkTMax(z*kHeightFactor, 0.0f))); 344 return SkColorSetARGB(255, 0, fAmbientAlpha * 255.9999f, umbraAlpha * 255.9999f); 345 } 346 347 SkScalar fAmbientAlpha; 348 int fCentroidCount; 349 350 typedef SkBaseShadowTessellator INHERITED; 351 }; 352 353 SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path, 354 const SkMatrix& ctm, 355 SkShadowTessellator::HeightFunc heightFunc, 356 SkScalar ambientAlpha, 357 bool transparent) 358 : INHERITED(heightFunc, transparent) 359 , fAmbientAlpha(ambientAlpha) { 360 // Set base colors 361 SkScalar occluderHeight = heightFunc(0, 0); 362 SkScalar umbraAlpha = SkScalarInvert((1.0f + SkTMax(occluderHeight*kHeightFactor, 0.0f))); 363 // umbraColor is the interior value, penumbraColor the exterior value. 364 // umbraAlpha is the factor that is linearly interpolated from outside to inside, and 365 // then "blurred" by the GrBlurredEdgeFP. It is then multiplied by fAmbientAlpha to get 366 // the final alpha. 367 fUmbraColor = SkColorSetARGB(255, 0, ambientAlpha * 255.9999f, umbraAlpha * 255.9999f); 368 fPenumbraColor = SkColorSetARGB(255, 0, ambientAlpha * 255.9999f, 0); 369 370 // make sure we're not below the canvas plane 371 this->setZOffset(path.getBounds(), ctm.hasPerspective()); 372 373 this->setTransformedHeightFunc(ctm); 374 375 // Outer ring: 3*numPts 376 // Middle ring: numPts 377 fPositions.setReserve(4 * path.countPoints()); 378 fColors.setReserve(4 * path.countPoints()); 379 // Outer ring: 12*numPts 380 // Middle ring: 0 381 fIndices.setReserve(12 * path.countPoints()); 382 383 // walk around the path, tessellate and generate outer ring 384 // if original path is transparent, will accumulate sum of points for centroid 385 SkPath::Iter iter(path, true); 386 SkPoint pts[4]; 387 SkPath::Verb verb; 388 if (fTransparent) { 389 *fPositions.push() = SkPoint::Make(0, 0); 390 *fColors.push() = fUmbraColor; 391 fCentroidCount = 0; 392 } 393 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { 394 switch (verb) { 395 case SkPath::kLine_Verb: 396 this->INHERITED::handleLine(ctm, &pts[1]); 397 break; 398 case SkPath::kQuad_Verb: 399 this->handleQuad(ctm, pts); 400 break; 401 case SkPath::kCubic_Verb: 402 this->handleCubic(ctm, pts); 403 break; 404 case SkPath::kConic_Verb: 405 this->handleConic(ctm, pts, iter.conicWeight()); 406 break; 407 case SkPath::kMove_Verb: 408 case SkPath::kClose_Verb: 409 case SkPath::kDone_Verb: 410 break; 411 } 412 } 413 414 if (!this->indexCount()) { 415 return; 416 } 417 418 SkVector normal; 419 if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) { 420 SkScalar z = fTransformedHeightFunc(fPrevPoint); 421 fRadius = this->offset(z); 422 SkVector scaledNormal(normal); 423 scaledNormal *= fRadius; 424 this->addArc(scaledNormal, true); 425 426 // set up for final edge 427 z = fTransformedHeightFunc(fFirstPoint); 428 normal *= this->offset(z); 429 430 // make sure we don't end up with a sharp alpha edge along the quad diagonal 431 if (fColors[fPrevUmbraIndex] != fColors[fFirstVertex] && 432 fFirstPoint.distanceToSqd(fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) { 433 SkPoint centerPoint = fPositions[fPrevUmbraIndex] + fFirstPoint; 434 centerPoint *= 0.5f; 435 *fPositions.push() = centerPoint; 436 *fColors.push() = SkPMLerp(fColors[fFirstVertex], fColors[fPrevUmbraIndex], 128); 437 SkVector midNormal = fPrevNormal + normal; 438 midNormal *= 0.5f; 439 *fPositions.push() = centerPoint + midNormal; 440 *fColors.push() = fPenumbraColor; 441 442 *fIndices.push() = fPrevUmbraIndex; 443 *fIndices.push() = fPositions.count() - 3; 444 *fIndices.push() = fPositions.count() - 2; 445 446 *fIndices.push() = fPositions.count() - 3; 447 *fIndices.push() = fPositions.count() - 1; 448 *fIndices.push() = fPositions.count() - 2; 449 450 fPrevUmbraIndex = fPositions.count() - 2; 451 } 452 453 // final edge 454 *fPositions.push() = fFirstPoint + normal; 455 *fColors.push() = fPenumbraColor; 456 457 if (fColors[fPrevUmbraIndex] > fColors[fFirstVertex]) { 458 *fIndices.push() = fPrevUmbraIndex; 459 *fIndices.push() = fPositions.count() - 2; 460 *fIndices.push() = fFirstVertex; 461 462 *fIndices.push() = fPositions.count() - 2; 463 *fIndices.push() = fPositions.count() - 1; 464 *fIndices.push() = fFirstVertex; 465 } else { 466 *fIndices.push() = fPrevUmbraIndex; 467 *fIndices.push() = fPositions.count() - 2; 468 *fIndices.push() = fPositions.count() - 1; 469 470 *fIndices.push() = fPrevUmbraIndex; 471 *fIndices.push() = fPositions.count() - 1; 472 *fIndices.push() = fFirstVertex; 473 } 474 fPrevNormal = normal; 475 } 476 477 // finalize centroid 478 if (fTransparent) { 479 fPositions[0] *= SkScalarFastInvert(fCentroidCount); 480 481 *fIndices.push() = 0; 482 *fIndices.push() = fPrevUmbraIndex; 483 *fIndices.push() = fFirstVertex; 484 } 485 486 // final fan 487 if (fPositions.count() >= 3) { 488 fPrevUmbraIndex = fFirstVertex; 489 fPrevPoint = fFirstPoint; 490 fRadius = this->offset(fTransformedHeightFunc(fPrevPoint)); 491 this->addArc(fFirstNormal, false); 492 493 *fIndices.push() = fFirstVertex; 494 *fIndices.push() = fPositions.count() - 1; 495 *fIndices.push() = fFirstVertex + 1; 496 } 497 fSucceeded = true; 498 } 499 500 void SkAmbientShadowTessellator::handleLine(const SkPoint& p) { 501 if (fInitPoints.count() < 2) { 502 *fInitPoints.push() = p; 503 return; 504 } 505 506 if (fInitPoints.count() == 2) { 507 // determine if cw or ccw 508 SkVector v0 = fInitPoints[1] - fInitPoints[0]; 509 SkVector v1 = p - fInitPoints[0]; 510 SkScalar perpDot = v0.fX*v1.fY - v0.fY*v1.fX; 511 if (SkScalarNearlyZero(perpDot)) { 512 // nearly parallel, just treat as straight line and continue 513 fInitPoints[1] = p; 514 return; 515 } 516 517 // if perpDot > 0, winding is ccw 518 fDirection = (perpDot > 0) ? -1 : 1; 519 520 // add first quad 521 SkVector normal; 522 if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &normal)) { 523 // first two points are incident, make the third point the second and continue 524 fInitPoints[1] = p; 525 return; 526 } 527 528 fFirstPoint = fInitPoints[0]; 529 fFirstVertex = fPositions.count(); 530 SkScalar z = fTransformedHeightFunc(fFirstPoint); 531 fFirstNormal = normal; 532 fFirstNormal *= this->offset(z); 533 534 fPrevNormal = fFirstNormal; 535 fPrevPoint = fFirstPoint; 536 fPrevUmbraIndex = fFirstVertex; 537 538 *fPositions.push() = fFirstPoint; 539 *fColors.push() = this->umbraColor(z); 540 *fPositions.push() = fFirstPoint + fFirstNormal; 541 *fColors.push() = fPenumbraColor; 542 if (fTransparent) { 543 fPositions[0] += fFirstPoint; 544 fCentroidCount = 1; 545 } 546 547 // add the first quad 548 z = fTransformedHeightFunc(fInitPoints[1]); 549 fRadius = this->offset(z); 550 fUmbraColor = this->umbraColor(z); 551 normal *= fRadius; 552 this->addEdge(fInitPoints[1], normal); 553 554 // to ensure we skip this block next time 555 *fInitPoints.push() = p; 556 } 557 558 SkVector normal; 559 if (compute_normal(fPositions[fPrevUmbraIndex], p, fDirection, &normal)) { 560 SkVector scaledNormal = normal; 561 scaledNormal *= fRadius; 562 this->addArc(scaledNormal, true); 563 SkScalar z = fTransformedHeightFunc(p); 564 fRadius = this->offset(z); 565 fUmbraColor = this->umbraColor(z); 566 normal *= fRadius; 567 this->addEdge(p, normal); 568 } 569 } 570 571 void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) { 572 // make sure we don't end up with a sharp alpha edge along the quad diagonal 573 if (fColors[fPrevUmbraIndex] != fUmbraColor && 574 nextPoint.distanceToSqd(fPositions[fPrevUmbraIndex]) > kMaxEdgeLenSqr) { 575 SkPoint centerPoint = fPositions[fPrevUmbraIndex] + nextPoint; 576 centerPoint *= 0.5f; 577 *fPositions.push() = centerPoint; 578 *fColors.push() = SkPMLerp(fUmbraColor, fColors[fPrevUmbraIndex], 128); 579 SkVector midNormal = fPrevNormal + nextNormal; 580 midNormal *= 0.5f; 581 *fPositions.push() = centerPoint + midNormal; 582 *fColors.push() = fPenumbraColor; 583 584 // set triangularization to get best interpolation of color 585 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) { 586 *fIndices.push() = fPrevUmbraIndex; 587 *fIndices.push() = fPositions.count() - 3; 588 *fIndices.push() = fPositions.count() - 2; 589 590 *fIndices.push() = fPositions.count() - 3; 591 *fIndices.push() = fPositions.count() - 1; 592 *fIndices.push() = fPositions.count() - 2; 593 } else { 594 *fIndices.push() = fPrevUmbraIndex; 595 *fIndices.push() = fPositions.count() - 2; 596 *fIndices.push() = fPositions.count() - 1; 597 598 *fIndices.push() = fPrevUmbraIndex; 599 *fIndices.push() = fPositions.count() - 1; 600 *fIndices.push() = fPositions.count() - 3; 601 } 602 603 fPrevUmbraIndex = fPositions.count() - 2; 604 } 605 606 // add next quad 607 *fPositions.push() = nextPoint; 608 *fColors.push() = fUmbraColor; 609 *fPositions.push() = nextPoint + nextNormal; 610 *fColors.push() = fPenumbraColor; 611 612 // set triangularization to get best interpolation of color 613 if (fColors[fPrevUmbraIndex] > fColors[fPositions.count() - 2]) { 614 *fIndices.push() = fPrevUmbraIndex; 615 *fIndices.push() = fPositions.count() - 3; 616 *fIndices.push() = fPositions.count() - 2; 617 618 *fIndices.push() = fPositions.count() - 3; 619 *fIndices.push() = fPositions.count() - 1; 620 *fIndices.push() = fPositions.count() - 2; 621 } else { 622 *fIndices.push() = fPrevUmbraIndex; 623 *fIndices.push() = fPositions.count() - 2; 624 *fIndices.push() = fPositions.count() - 1; 625 626 *fIndices.push() = fPrevUmbraIndex; 627 *fIndices.push() = fPositions.count() - 1; 628 *fIndices.push() = fPositions.count() - 3; 629 } 630 631 // if transparent, add point to first one in array and add to center fan 632 if (fTransparent) { 633 fPositions[0] += nextPoint; 634 ++fCentroidCount; 635 636 *fIndices.push() = 0; 637 *fIndices.push() = fPrevUmbraIndex; 638 *fIndices.push() = fPositions.count() - 2; 639 } 640 641 fPrevUmbraIndex = fPositions.count() - 2; 642 fPrevNormal = nextNormal; 643 fPrevPoint = nextPoint; 644 } 645 646 /////////////////////////////////////////////////////////////////////////////////////////////////// 647 648 class SkSpotShadowTessellator : public SkBaseShadowTessellator { 649 public: 650 SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm, 651 SkShadowTessellator::HeightFunc heightFunc, 652 const SkPoint3& lightPos, SkScalar lightRadius, 653 SkScalar spotAlpha, bool transparent); 654 655 private: 656 void computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm, 657 const SkMatrix& shadowTransform); 658 void computeClipVectorsAndTestCentroid(); 659 bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint); 660 int getClosestUmbraPoint(const SkPoint& point); 661 662 void handleLine(const SkPoint& p) override; 663 void handlePolyPoint(const SkPoint& p); 664 665 void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count); 666 bool addInnerPoint(const SkPoint& pathPoint); 667 void addEdge(const SkVector& nextPoint, const SkVector& nextNormal); 668 669 SkScalar offset(SkScalar z) { 670 float zRatio = SkTPin(z / (fLightZ - z), 0.0f, 0.95f); 671 return fLightRadius*zRatio; 672 } 673 674 SkScalar fLightZ; 675 SkScalar fLightRadius; 676 SkScalar fOffsetAdjust; 677 678 SkTDArray<SkPoint> fClipPolygon; 679 SkTDArray<SkVector> fClipVectors; 680 SkPoint fCentroid; 681 SkScalar fArea; 682 683 SkTDArray<SkPoint> fPathPolygon; 684 SkTDArray<SkPoint> fUmbraPolygon; 685 int fCurrClipPoint; 686 int fCurrUmbraPoint; 687 bool fPrevUmbraOutside; 688 bool fFirstUmbraOutside; 689 bool fValidUmbra; 690 691 typedef SkBaseShadowTessellator INHERITED; 692 }; 693 694 SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm, 695 SkShadowTessellator::HeightFunc heightFunc, 696 const SkPoint3& lightPos, SkScalar lightRadius, 697 SkScalar spotAlpha, bool transparent) 698 : INHERITED(heightFunc, transparent) 699 , fLightZ(lightPos.fZ) 700 , fLightRadius(lightRadius) 701 , fOffsetAdjust(0) 702 , fCurrClipPoint(0) 703 , fPrevUmbraOutside(false) 704 , fFirstUmbraOutside(false) 705 , fValidUmbra(true) { 706 707 // make sure we're not below the canvas plane 708 if (this->setZOffset(path.getBounds(), ctm.hasPerspective())) { 709 // Adjust light height and radius 710 fLightRadius *= (fLightZ + fZOffset) / fLightZ; 711 fLightZ += fZOffset; 712 } 713 714 // Set radius and colors 715 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY()); 716 SkScalar occluderHeight = heightFunc(center.fX, center.fY) + fZOffset; 717 float zRatio = SkTPin(occluderHeight / (fLightZ - occluderHeight), 0.0f, 0.95f); 718 SkScalar radius = lightRadius * zRatio; 719 fRadius = radius; 720 fUmbraColor = SkColorSetARGB(255, 0, spotAlpha * 255.9999f, 255); 721 fPenumbraColor = SkColorSetARGB(255, 0, spotAlpha * 255.9999f, 0); 722 723 // Compute the scale and translation for the spot shadow. 724 SkMatrix shadowTransform; 725 if (!ctm.hasPerspective()) { 726 SkScalar scale = fLightZ / (fLightZ - occluderHeight); 727 SkVector translate = SkVector::Make(-zRatio * lightPos.fX, -zRatio * lightPos.fY); 728 shadowTransform.setScaleTranslate(scale, scale, translate.fX, translate.fY); 729 } else { 730 // For perspective, we have a scale, a z-shear, and another projective divide -- 731 // this varies at each point so we can't use an affine transform. 732 // We'll just apply this to each generated point in turn. 733 shadowTransform.reset(); 734 // Also can't cull the center (for now). 735 fTransparent = true; 736 } 737 SkMatrix fullTransform = SkMatrix::Concat(shadowTransform, ctm); 738 739 // Set up our reverse mapping 740 this->setTransformedHeightFunc(fullTransform); 741 742 // TODO: calculate these reserves better 743 // Penumbra ring: 3*numPts 744 // Umbra ring: numPts 745 // Inner ring: numPts 746 fPositions.setReserve(5 * path.countPoints()); 747 fColors.setReserve(5 * path.countPoints()); 748 // Penumbra ring: 12*numPts 749 // Umbra ring: 3*numPts 750 fIndices.setReserve(15 * path.countPoints()); 751 fClipPolygon.setReserve(path.countPoints()); 752 753 // compute rough clip bounds for umbra, plus offset polygon, plus centroid 754 this->computeClipAndPathPolygons(path, ctm, shadowTransform); 755 if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3) { 756 return; 757 } 758 759 // check to see if umbra collapses 760 SkScalar minDistSq = fCentroid.distanceToLineSegmentBetweenSqd(fPathPolygon[0], 761 fPathPolygon[1]); 762 SkRect bounds; 763 bounds.setBounds(&fPathPolygon[0], fPathPolygon.count()); 764 for (int i = 1; i < fPathPolygon.count(); ++i) { 765 int j = i + 1; 766 if (i == fPathPolygon.count() - 1) { 767 j = 0; 768 } 769 SkPoint currPoint = fPathPolygon[i]; 770 SkPoint nextPoint = fPathPolygon[j]; 771 SkScalar distSq = fCentroid.distanceToLineSegmentBetweenSqd(currPoint, nextPoint); 772 if (distSq < minDistSq) { 773 minDistSq = distSq; 774 } 775 } 776 static constexpr auto kTolerance = 1.0e-2f; 777 if (minDistSq < (radius + kTolerance)*(radius + kTolerance)) { 778 // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha 779 SkScalar newRadius = SkScalarSqrt(minDistSq) - kTolerance; 780 fOffsetAdjust = newRadius - radius; 781 SkScalar ratio = 256 * newRadius / radius; 782 // they aren't PMColors, but the interpolation algorithm is the same 783 fUmbraColor = SkPMLerp(fUmbraColor, fPenumbraColor, (unsigned)ratio); 784 radius = newRadius; 785 } 786 787 // compute vectors for clip tests 788 this->computeClipVectorsAndTestCentroid(); 789 790 // generate inner ring 791 if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), radius, 792 &fUmbraPolygon)) { 793 // this shouldn't happen, but just in case we'll inset using the centroid 794 fValidUmbra = false; 795 } 796 797 // walk around the path polygon, generate outer ring and connect to inner ring 798 if (fTransparent) { 799 *fPositions.push() = fCentroid; 800 *fColors.push() = fUmbraColor; 801 } 802 fCurrUmbraPoint = 0; 803 for (int i = 0; i < fPathPolygon.count(); ++i) { 804 this->handlePolyPoint(fPathPolygon[i]); 805 } 806 807 if (!this->indexCount()) { 808 return; 809 } 810 811 // finish up the final verts 812 SkVector normal; 813 if (compute_normal(fPrevPoint, fFirstPoint, fDirection, &normal)) { 814 normal *= fRadius; 815 this->addArc(normal, true); 816 817 // add to center fan 818 if (fTransparent) { 819 *fIndices.push() = 0; 820 *fIndices.push() = fPrevUmbraIndex; 821 *fIndices.push() = fFirstVertex; 822 // or to clip ring 823 } else { 824 if (fFirstUmbraOutside) { 825 *fIndices.push() = fPrevUmbraIndex; 826 *fIndices.push() = fFirstVertex; 827 *fIndices.push() = fFirstVertex + 1; 828 if (fPrevUmbraOutside) { 829 // fill out quad 830 *fIndices.push() = fPrevUmbraIndex; 831 *fIndices.push() = fFirstVertex + 1; 832 *fIndices.push() = fPrevUmbraIndex + 1; 833 } 834 } else if (fPrevUmbraOutside) { 835 // add tri 836 *fIndices.push() = fPrevUmbraIndex; 837 *fIndices.push() = fFirstVertex; 838 *fIndices.push() = fPrevUmbraIndex + 1; 839 } 840 } 841 842 // add final edge 843 *fPositions.push() = fFirstPoint + normal; 844 *fColors.push() = fPenumbraColor; 845 846 *fIndices.push() = fPrevUmbraIndex; 847 *fIndices.push() = fPositions.count() - 2; 848 *fIndices.push() = fFirstVertex; 849 850 *fIndices.push() = fPositions.count() - 2; 851 *fIndices.push() = fPositions.count() - 1; 852 *fIndices.push() = fFirstVertex; 853 854 fPrevNormal = normal; 855 } 856 857 // final fan 858 if (fPositions.count() >= 3) { 859 fPrevUmbraIndex = fFirstVertex; 860 fPrevPoint = fFirstPoint; 861 this->addArc(fFirstNormal, false); 862 863 *fIndices.push() = fFirstVertex; 864 *fIndices.push() = fPositions.count() - 1; 865 if (fFirstUmbraOutside) { 866 *fIndices.push() = fFirstVertex + 2; 867 } else { 868 *fIndices.push() = fFirstVertex + 1; 869 } 870 } 871 872 if (ctm.hasPerspective()) { 873 for (int i = 0; i < fPositions.count(); ++i) { 874 SkScalar pathZ = fTransformedHeightFunc(fPositions[i]); 875 SkScalar factor = SkScalarInvert(fLightZ - pathZ); 876 fPositions[i].fX = (fPositions[i].fX*fLightZ - lightPos.fX*pathZ)*factor; 877 fPositions[i].fY = (fPositions[i].fY*fLightZ - lightPos.fY*pathZ)*factor; 878 } 879 #ifdef DRAW_CENTROID 880 SkScalar pathZ = fTransformedHeightFunc(fCentroid); 881 SkScalar factor = SkScalarInvert(fLightZ - pathZ); 882 fCentroid.fX = (fCentroid.fX*fLightZ - lightPos.fX*pathZ)*factor; 883 fCentroid.fY = (fCentroid.fY*fLightZ - lightPos.fY*pathZ)*factor; 884 #endif 885 } 886 #ifdef DRAW_CENTROID 887 *fPositions.push() = fCentroid + SkVector::Make(-2, -2); 888 *fColors.push() = SkColorSetARGB(255, 0, 255, 255); 889 *fPositions.push() = fCentroid + SkVector::Make(2, -2); 890 *fColors.push() = SkColorSetARGB(255, 0, 255, 255); 891 *fPositions.push() = fCentroid + SkVector::Make(-2, 2); 892 *fColors.push() = SkColorSetARGB(255, 0, 255, 255); 893 *fPositions.push() = fCentroid + SkVector::Make(2, 2); 894 *fColors.push() = SkColorSetARGB(255, 0, 255, 255); 895 896 *fIndices.push() = fPositions.count() - 4; 897 *fIndices.push() = fPositions.count() - 2; 898 *fIndices.push() = fPositions.count() - 1; 899 900 *fIndices.push() = fPositions.count() - 4; 901 *fIndices.push() = fPositions.count() - 1; 902 *fIndices.push() = fPositions.count() - 3; 903 #endif 904 905 fSucceeded = true; 906 } 907 908 void SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm, 909 const SkMatrix& shadowTransform) { 910 911 fPathPolygon.setReserve(path.countPoints()); 912 913 // Walk around the path and compute clip polygon and path polygon. 914 // Will also accumulate sum of areas for centroid. 915 // For Bezier curves, we compute additional interior points on curve. 916 SkPath::Iter iter(path, true); 917 SkPoint pts[4]; 918 SkPath::Verb verb; 919 920 fClipPolygon.reset(); 921 922 // init centroid 923 fCentroid = SkPoint::Make(0, 0); 924 fArea = 0; 925 926 // coefficients to compute cubic Bezier at t = 5/16 927 static constexpr SkScalar kA = 0.32495117187f; 928 static constexpr SkScalar kB = 0.44311523437f; 929 static constexpr SkScalar kC = 0.20141601562f; 930 static constexpr SkScalar kD = 0.03051757812f; 931 932 SkPoint curvePoint; 933 SkScalar w; 934 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { 935 switch (verb) { 936 case SkPath::kLine_Verb: 937 ctm.mapPoints(&pts[1], 1); 938 *fClipPolygon.push() = pts[1]; 939 this->INHERITED::handleLine(shadowTransform, &pts[1]); 940 break; 941 case SkPath::kQuad_Verb: 942 ctm.mapPoints(pts, 3); 943 // point at t = 1/2 944 curvePoint.fX = 0.25f*pts[0].fX + 0.5f*pts[1].fX + 0.25f*pts[2].fX; 945 curvePoint.fY = 0.25f*pts[0].fY + 0.5f*pts[1].fY + 0.25f*pts[2].fY; 946 *fClipPolygon.push() = curvePoint; 947 *fClipPolygon.push() = pts[2]; 948 this->handleQuad(shadowTransform, pts); 949 break; 950 case SkPath::kConic_Verb: 951 ctm.mapPoints(pts, 3); 952 w = iter.conicWeight(); 953 // point at t = 1/2 954 curvePoint.fX = 0.25f*pts[0].fX + w*0.5f*pts[1].fX + 0.25f*pts[2].fX; 955 curvePoint.fY = 0.25f*pts[0].fY + w*0.5f*pts[1].fY + 0.25f*pts[2].fY; 956 curvePoint *= SkScalarInvert(0.5f + 0.5f*w); 957 *fClipPolygon.push() = curvePoint; 958 *fClipPolygon.push() = pts[2]; 959 this->handleConic(shadowTransform, pts, w); 960 break; 961 case SkPath::kCubic_Verb: 962 ctm.mapPoints(pts, 4); 963 // point at t = 5/16 964 curvePoint.fX = kA*pts[0].fX + kB*pts[1].fX + kC*pts[2].fX + kD*pts[3].fX; 965 curvePoint.fY = kA*pts[0].fY + kB*pts[1].fY + kC*pts[2].fY + kD*pts[3].fY; 966 *fClipPolygon.push() = curvePoint; 967 // point at t = 11/16 968 curvePoint.fX = kD*pts[0].fX + kC*pts[1].fX + kB*pts[2].fX + kA*pts[3].fX; 969 curvePoint.fY = kD*pts[0].fY + kC*pts[1].fY + kB*pts[2].fY + kA*pts[3].fY; 970 *fClipPolygon.push() = curvePoint; 971 *fClipPolygon.push() = pts[3]; 972 this->handleCubic(shadowTransform, pts); 973 break; 974 case SkPath::kMove_Verb: 975 case SkPath::kClose_Verb: 976 case SkPath::kDone_Verb: 977 break; 978 default: 979 SkDEBUGFAIL("unknown verb"); 980 } 981 } 982 983 // finish centroid 984 if (fPathPolygon.count() > 0) { 985 SkPoint currPoint = fPathPolygon[fPathPolygon.count() - 1]; 986 SkPoint nextPoint = fPathPolygon[0]; 987 SkScalar quadArea = currPoint.cross(nextPoint); 988 fCentroid.fX += (currPoint.fX + nextPoint.fX) * quadArea; 989 fCentroid.fY += (currPoint.fY + nextPoint.fY) * quadArea; 990 fArea += quadArea; 991 fCentroid *= SK_Scalar1 / (3 * fArea); 992 } 993 994 fCurrClipPoint = fClipPolygon.count() - 1; 995 } 996 997 void SkSpotShadowTessellator::computeClipVectorsAndTestCentroid() { 998 SkASSERT(fClipPolygon.count() >= 3); 999 1000 // init clip vectors 1001 SkVector v0 = fClipPolygon[1] - fClipPolygon[0]; 1002 *fClipVectors.push() = v0; 1003 1004 // init centroid check 1005 bool hiddenCentroid = true; 1006 SkVector v1 = fCentroid - fClipPolygon[0]; 1007 SkScalar initCross = v0.cross(v1); 1008 1009 for (int p = 1; p < fClipPolygon.count(); ++p) { 1010 // add to clip vectors 1011 v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p]; 1012 *fClipVectors.push() = v0; 1013 // Determine if transformed centroid is inside clipPolygon. 1014 v1 = fCentroid - fClipPolygon[p]; 1015 if (initCross*v0.cross(v1) <= 0) { 1016 hiddenCentroid = false; 1017 } 1018 } 1019 SkASSERT(fClipVectors.count() == fClipPolygon.count()); 1020 1021 fTransparent = fTransparent || !hiddenCentroid; 1022 } 1023 1024 bool SkSpotShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, 1025 SkPoint* clipPoint) { 1026 SkVector segmentVector = centroid - umbraPoint; 1027 1028 int startClipPoint = fCurrClipPoint; 1029 do { 1030 SkVector dp = umbraPoint - fClipPolygon[fCurrClipPoint]; 1031 SkScalar denom = fClipVectors[fCurrClipPoint].cross(segmentVector); 1032 SkScalar t_num = dp.cross(segmentVector); 1033 // if line segments are nearly parallel 1034 if (SkScalarNearlyZero(denom)) { 1035 // and collinear 1036 if (SkScalarNearlyZero(t_num)) { 1037 return false; 1038 } 1039 // otherwise are separate, will try the next poly segment 1040 // else if crossing lies within poly segment 1041 } else if (t_num >= 0 && t_num <= denom) { 1042 SkScalar s_num = dp.cross(fClipVectors[fCurrClipPoint]); 1043 // if umbra point is inside the clip polygon 1044 if (s_num >= 0 && s_num <= denom) { 1045 segmentVector *= s_num/denom; 1046 *clipPoint = umbraPoint + segmentVector; 1047 return true; 1048 } 1049 } 1050 fCurrClipPoint = (fCurrClipPoint + 1) % fClipPolygon.count(); 1051 } while (fCurrClipPoint != startClipPoint); 1052 1053 return false; 1054 } 1055 1056 int SkSpotShadowTessellator::getClosestUmbraPoint(const SkPoint& p) { 1057 SkScalar minDistance = p.distanceToSqd(fUmbraPolygon[fCurrUmbraPoint]); 1058 int index = fCurrUmbraPoint; 1059 int dir = 1; 1060 int next = (index + dir) % fUmbraPolygon.count(); 1061 1062 // init travel direction 1063 SkScalar distance = p.distanceToSqd(fUmbraPolygon[next]); 1064 if (distance < minDistance) { 1065 index = next; 1066 minDistance = distance; 1067 } else { 1068 dir = fUmbraPolygon.count()-1; 1069 } 1070 1071 // iterate until we find a point that increases the distance 1072 next = (index + dir) % fUmbraPolygon.count(); 1073 distance = p.distanceToSqd(fUmbraPolygon[next]); 1074 while (distance < minDistance) { 1075 index = next; 1076 minDistance = distance; 1077 next = (index + dir) % fUmbraPolygon.count(); 1078 distance = p.distanceToSqd(fUmbraPolygon[next]); 1079 } 1080 1081 fCurrUmbraPoint = index; 1082 return index; 1083 } 1084 1085 void SkSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate, 1086 SkPoint* pts, int count) { 1087 // TODO: vectorize 1088 for (int i = 0; i < count; ++i) { 1089 pts[i] *= scale; 1090 pts[i] += xlate; 1091 } 1092 } 1093 1094 static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) { 1095 static constexpr SkScalar kClose = (SK_Scalar1 / 16); 1096 static constexpr SkScalar kCloseSqd = kClose*kClose; 1097 1098 SkScalar distSq = p0.distanceToSqd(p1); 1099 return distSq < kCloseSqd; 1100 } 1101 1102 static bool is_collinear(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) { 1103 SkVector v0 = p1 - p0; 1104 SkVector v1 = p2 - p0; 1105 return (SkScalarNearlyZero(v0.cross(v1))); 1106 } 1107 1108 void SkSpotShadowTessellator::handleLine(const SkPoint& p) { 1109 // remove coincident points and add to centroid 1110 if (fPathPolygon.count() > 0) { 1111 const SkPoint& lastPoint = fPathPolygon[fPathPolygon.count() - 1]; 1112 if (duplicate_pt(p, lastPoint)) { 1113 return; 1114 } 1115 SkScalar quadArea = lastPoint.cross(p); 1116 fCentroid.fX += (p.fX + lastPoint.fX) * quadArea; 1117 fCentroid.fY += (p.fY + lastPoint.fY) * quadArea; 1118 fArea += quadArea; 1119 } 1120 1121 // try to remove collinear points 1122 if (fPathPolygon.count() > 1 && is_collinear(fPathPolygon[fPathPolygon.count()-2], 1123 fPathPolygon[fPathPolygon.count()-1], 1124 p)) { 1125 fPathPolygon[fPathPolygon.count() - 1] = p; 1126 } else { 1127 *fPathPolygon.push() = p; 1128 } 1129 } 1130 1131 void SkSpotShadowTessellator::handlePolyPoint(const SkPoint& p) { 1132 if (fInitPoints.count() < 2) { 1133 *fInitPoints.push() = p; 1134 return; 1135 } 1136 1137 if (fInitPoints.count() == 2) { 1138 // determine if cw or ccw 1139 SkVector v0 = fInitPoints[1] - fInitPoints[0]; 1140 SkVector v1 = p - fInitPoints[0]; 1141 SkScalar perpDot = v0.cross(v1); 1142 if (SkScalarNearlyZero(perpDot)) { 1143 // nearly parallel, just treat as straight line and continue 1144 fInitPoints[1] = p; 1145 return; 1146 } 1147 1148 // if perpDot > 0, winding is ccw 1149 fDirection = (perpDot > 0) ? -1 : 1; 1150 1151 // add first quad 1152 if (!compute_normal(fInitPoints[0], fInitPoints[1], fDirection, &fFirstNormal)) { 1153 // first two points are incident, make the third point the second and continue 1154 fInitPoints[1] = p; 1155 return; 1156 } 1157 1158 fFirstNormal *= fRadius; 1159 fFirstPoint = fInitPoints[0]; 1160 fFirstVertex = fPositions.count(); 1161 fPrevNormal = fFirstNormal; 1162 fPrevPoint = fFirstPoint; 1163 fPrevUmbraIndex = fFirstVertex; 1164 1165 this->addInnerPoint(fFirstPoint); 1166 1167 if (!fTransparent) { 1168 SkPoint clipPoint; 1169 bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertex], fCentroid, &clipPoint); 1170 if (isOutside) { 1171 *fPositions.push() = clipPoint; 1172 *fColors.push() = fUmbraColor; 1173 } 1174 fPrevUmbraOutside = isOutside; 1175 fFirstUmbraOutside = isOutside; 1176 } 1177 1178 SkPoint newPoint = fFirstPoint + fFirstNormal; 1179 *fPositions.push() = newPoint; 1180 *fColors.push() = fPenumbraColor; 1181 this->addEdge(fInitPoints[1], fFirstNormal); 1182 1183 // to ensure we skip this block next time 1184 *fInitPoints.push() = p; 1185 } 1186 1187 SkVector normal; 1188 if (compute_normal(fPrevPoint, p, fDirection, &normal)) { 1189 normal *= fRadius; 1190 this->addArc(normal, true); 1191 this->addEdge(p, normal); 1192 } 1193 } 1194 1195 bool SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint) { 1196 SkPoint umbraPoint; 1197 if (!fValidUmbra) { 1198 SkVector v = fCentroid - pathPoint; 1199 v *= 0.95f; 1200 umbraPoint = pathPoint + v; 1201 } else { 1202 umbraPoint = fUmbraPolygon[this->getClosestUmbraPoint(pathPoint)]; 1203 } 1204 1205 fPrevPoint = pathPoint; 1206 1207 // merge "close" points 1208 if (fPrevUmbraIndex == fFirstVertex || 1209 !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) { 1210 *fPositions.push() = umbraPoint; 1211 *fColors.push() = fUmbraColor; 1212 1213 return false; 1214 } else { 1215 return true; 1216 } 1217 } 1218 1219 void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) { 1220 // add next umbra point 1221 bool duplicate = this->addInnerPoint(nextPoint); 1222 int prevPenumbraIndex = duplicate ? fPositions.count()-1 : fPositions.count()-2; 1223 int currUmbraIndex = duplicate ? fPrevUmbraIndex : fPositions.count()-1; 1224 1225 if (!duplicate) { 1226 // add to center fan if transparent or centroid showing 1227 if (fTransparent) { 1228 *fIndices.push() = 0; 1229 *fIndices.push() = fPrevUmbraIndex; 1230 *fIndices.push() = currUmbraIndex; 1231 // otherwise add to clip ring 1232 } else { 1233 SkPoint clipPoint; 1234 bool isOutside = this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid, 1235 &clipPoint); 1236 if (isOutside) { 1237 *fPositions.push() = clipPoint; 1238 *fColors.push() = fUmbraColor; 1239 1240 *fIndices.push() = fPrevUmbraIndex; 1241 *fIndices.push() = currUmbraIndex; 1242 *fIndices.push() = currUmbraIndex + 1; 1243 if (fPrevUmbraOutside) { 1244 // fill out quad 1245 *fIndices.push() = fPrevUmbraIndex; 1246 *fIndices.push() = currUmbraIndex + 1; 1247 *fIndices.push() = fPrevUmbraIndex + 1; 1248 } 1249 } else if (fPrevUmbraOutside) { 1250 // add tri 1251 *fIndices.push() = fPrevUmbraIndex; 1252 *fIndices.push() = currUmbraIndex; 1253 *fIndices.push() = fPrevUmbraIndex + 1; 1254 } 1255 fPrevUmbraOutside = isOutside; 1256 } 1257 } 1258 1259 // add next penumbra point and quad 1260 SkPoint newPoint = nextPoint + nextNormal; 1261 *fPositions.push() = newPoint; 1262 *fColors.push() = fPenumbraColor; 1263 1264 if (!duplicate) { 1265 *fIndices.push() = fPrevUmbraIndex; 1266 *fIndices.push() = prevPenumbraIndex; 1267 *fIndices.push() = currUmbraIndex; 1268 } 1269 1270 *fIndices.push() = prevPenumbraIndex; 1271 *fIndices.push() = fPositions.count() - 1; 1272 *fIndices.push() = currUmbraIndex; 1273 1274 fPrevUmbraIndex = currUmbraIndex; 1275 fPrevNormal = nextNormal; 1276 } 1277 1278 /////////////////////////////////////////////////////////////////////////////////////////////////// 1279 1280 sk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm, 1281 HeightFunc heightFunc, SkScalar ambientAlpha, 1282 bool transparent) { 1283 SkAmbientShadowTessellator ambientTess(path, ctm, heightFunc, ambientAlpha, transparent); 1284 return ambientTess.releaseVertices(); 1285 } 1286 1287 sk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm, 1288 HeightFunc heightFunc, 1289 const SkPoint3& lightPos, SkScalar lightRadius, 1290 SkScalar spotAlpha, bool transparent) { 1291 SkSpotShadowTessellator spotTess(path, ctm, heightFunc, lightPos, lightRadius, 1292 spotAlpha, transparent); 1293 return spotTess.releaseVertices(); 1294 } 1295