1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2017 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "include/core/SkPath.h" 9cb93a386Sopenharmony_ci#include "include/core/SkPoint3.h" 10cb93a386Sopenharmony_ci#include "include/core/SkVertices.h" 11cb93a386Sopenharmony_ci#include "include/private/SkColorData.h" 12cb93a386Sopenharmony_ci#include "include/private/SkTPin.h" 13cb93a386Sopenharmony_ci#include "src/core/SkDrawShadowInfo.h" 14cb93a386Sopenharmony_ci#include "src/core/SkGeometry.h" 15cb93a386Sopenharmony_ci#include "src/core/SkPointPriv.h" 16cb93a386Sopenharmony_ci#include "src/utils/SkPolyUtils.h" 17cb93a386Sopenharmony_ci#include "src/utils/SkShadowTessellator.h" 18cb93a386Sopenharmony_ci 19cb93a386Sopenharmony_ci#if SK_SUPPORT_GPU 20cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrPathUtils.h" 21cb93a386Sopenharmony_ci#endif 22cb93a386Sopenharmony_ci 23cb93a386Sopenharmony_ci 24cb93a386Sopenharmony_ci/** 25cb93a386Sopenharmony_ci * Base class 26cb93a386Sopenharmony_ci */ 27cb93a386Sopenharmony_ciclass SkBaseShadowTessellator { 28cb93a386Sopenharmony_cipublic: 29cb93a386Sopenharmony_ci SkBaseShadowTessellator(const SkPoint3& zPlaneParams, const SkRect& bounds, bool transparent); 30cb93a386Sopenharmony_ci virtual ~SkBaseShadowTessellator() {} 31cb93a386Sopenharmony_ci 32cb93a386Sopenharmony_ci sk_sp<SkVertices> releaseVertices() { 33cb93a386Sopenharmony_ci if (!fSucceeded) { 34cb93a386Sopenharmony_ci return nullptr; 35cb93a386Sopenharmony_ci } 36cb93a386Sopenharmony_ci return SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, this->vertexCount(), 37cb93a386Sopenharmony_ci fPositions.begin(), nullptr, fColors.begin(), 38cb93a386Sopenharmony_ci this->indexCount(), fIndices.begin()); 39cb93a386Sopenharmony_ci } 40cb93a386Sopenharmony_ci 41cb93a386Sopenharmony_ciprotected: 42cb93a386Sopenharmony_ci inline static constexpr auto kMinHeight = 0.1f; 43cb93a386Sopenharmony_ci inline static constexpr auto kPenumbraColor = SK_ColorTRANSPARENT; 44cb93a386Sopenharmony_ci inline static constexpr auto kUmbraColor = SK_ColorBLACK; 45cb93a386Sopenharmony_ci 46cb93a386Sopenharmony_ci int vertexCount() const { return fPositions.count(); } 47cb93a386Sopenharmony_ci int indexCount() const { return fIndices.count(); } 48cb93a386Sopenharmony_ci 49cb93a386Sopenharmony_ci // initialization methods 50cb93a386Sopenharmony_ci bool accumulateCentroid(const SkPoint& c, const SkPoint& n); 51cb93a386Sopenharmony_ci bool checkConvexity(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2); 52cb93a386Sopenharmony_ci void finishPathPolygon(); 53cb93a386Sopenharmony_ci 54cb93a386Sopenharmony_ci // convex shadow methods 55cb93a386Sopenharmony_ci bool computeConvexShadow(SkScalar inset, SkScalar outset, bool doClip); 56cb93a386Sopenharmony_ci void computeClipVectorsAndTestCentroid(); 57cb93a386Sopenharmony_ci bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint); 58cb93a386Sopenharmony_ci void addEdge(const SkVector& nextPoint, const SkVector& nextNormal, SkColor umbraColor, 59cb93a386Sopenharmony_ci const SkTDArray<SkPoint>& umbraPolygon, bool lastEdge, bool doClip); 60cb93a386Sopenharmony_ci bool addInnerPoint(const SkPoint& pathPoint, SkColor umbraColor, 61cb93a386Sopenharmony_ci const SkTDArray<SkPoint>& umbraPolygon, int* currUmbraIndex); 62cb93a386Sopenharmony_ci int getClosestUmbraIndex(const SkPoint& point, const SkTDArray<SkPoint>& umbraPolygon); 63cb93a386Sopenharmony_ci 64cb93a386Sopenharmony_ci // concave shadow methods 65cb93a386Sopenharmony_ci bool computeConcaveShadow(SkScalar inset, SkScalar outset); 66cb93a386Sopenharmony_ci void stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon, 67cb93a386Sopenharmony_ci SkTDArray<int>* umbraIndices, 68cb93a386Sopenharmony_ci const SkTDArray<SkPoint>& penumbraPolygon, 69cb93a386Sopenharmony_ci SkTDArray<int>* penumbraIndices); 70cb93a386Sopenharmony_ci 71cb93a386Sopenharmony_ci void handleLine(const SkPoint& p); 72cb93a386Sopenharmony_ci void handleLine(const SkMatrix& m, SkPoint* p); 73cb93a386Sopenharmony_ci 74cb93a386Sopenharmony_ci void handleQuad(const SkPoint pts[3]); 75cb93a386Sopenharmony_ci void handleQuad(const SkMatrix& m, SkPoint pts[3]); 76cb93a386Sopenharmony_ci 77cb93a386Sopenharmony_ci void handleCubic(const SkMatrix& m, SkPoint pts[4]); 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_ci void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w); 80cb93a386Sopenharmony_ci 81cb93a386Sopenharmony_ci bool addArc(const SkVector& nextNormal, SkScalar offset, bool finishArc); 82cb93a386Sopenharmony_ci 83cb93a386Sopenharmony_ci void appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2); 84cb93a386Sopenharmony_ci void appendQuad(uint16_t index0, uint16_t index1, uint16_t index2, uint16_t index3); 85cb93a386Sopenharmony_ci 86cb93a386Sopenharmony_ci SkScalar heightFunc(SkScalar x, SkScalar y) { 87cb93a386Sopenharmony_ci return fZPlaneParams.fX*x + fZPlaneParams.fY*y + fZPlaneParams.fZ; 88cb93a386Sopenharmony_ci } 89cb93a386Sopenharmony_ci 90cb93a386Sopenharmony_ci SkPoint3 fZPlaneParams; 91cb93a386Sopenharmony_ci 92cb93a386Sopenharmony_ci // temporary buffer 93cb93a386Sopenharmony_ci SkTDArray<SkPoint> fPointBuffer; 94cb93a386Sopenharmony_ci 95cb93a386Sopenharmony_ci SkTDArray<SkPoint> fPositions; 96cb93a386Sopenharmony_ci SkTDArray<SkColor> fColors; 97cb93a386Sopenharmony_ci SkTDArray<uint16_t> fIndices; 98cb93a386Sopenharmony_ci 99cb93a386Sopenharmony_ci SkTDArray<SkPoint> fPathPolygon; 100cb93a386Sopenharmony_ci SkTDArray<SkPoint> fClipPolygon; 101cb93a386Sopenharmony_ci SkTDArray<SkVector> fClipVectors; 102cb93a386Sopenharmony_ci 103cb93a386Sopenharmony_ci SkRect fPathBounds; 104cb93a386Sopenharmony_ci SkPoint fCentroid; 105cb93a386Sopenharmony_ci SkScalar fArea; 106cb93a386Sopenharmony_ci SkScalar fLastArea; 107cb93a386Sopenharmony_ci SkScalar fLastCross; 108cb93a386Sopenharmony_ci 109cb93a386Sopenharmony_ci int fFirstVertexIndex; 110cb93a386Sopenharmony_ci SkVector fFirstOutset; 111cb93a386Sopenharmony_ci SkPoint fFirstPoint; 112cb93a386Sopenharmony_ci 113cb93a386Sopenharmony_ci bool fSucceeded; 114cb93a386Sopenharmony_ci bool fTransparent; 115cb93a386Sopenharmony_ci bool fIsConvex; 116cb93a386Sopenharmony_ci bool fValidUmbra; 117cb93a386Sopenharmony_ci 118cb93a386Sopenharmony_ci SkScalar fDirection; 119cb93a386Sopenharmony_ci int fPrevUmbraIndex; 120cb93a386Sopenharmony_ci int fCurrUmbraIndex; 121cb93a386Sopenharmony_ci int fCurrClipIndex; 122cb93a386Sopenharmony_ci bool fPrevUmbraOutside; 123cb93a386Sopenharmony_ci bool fFirstUmbraOutside; 124cb93a386Sopenharmony_ci SkVector fPrevOutset; 125cb93a386Sopenharmony_ci SkPoint fPrevPoint; 126cb93a386Sopenharmony_ci}; 127cb93a386Sopenharmony_ci 128cb93a386Sopenharmony_cistatic bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar dir, 129cb93a386Sopenharmony_ci SkVector* newNormal) { 130cb93a386Sopenharmony_ci SkVector normal; 131cb93a386Sopenharmony_ci // compute perpendicular 132cb93a386Sopenharmony_ci normal.fX = p0.fY - p1.fY; 133cb93a386Sopenharmony_ci normal.fY = p1.fX - p0.fX; 134cb93a386Sopenharmony_ci normal *= dir; 135cb93a386Sopenharmony_ci if (!normal.normalize()) { 136cb93a386Sopenharmony_ci return false; 137cb93a386Sopenharmony_ci } 138cb93a386Sopenharmony_ci *newNormal = normal; 139cb93a386Sopenharmony_ci return true; 140cb93a386Sopenharmony_ci} 141cb93a386Sopenharmony_ci 142cb93a386Sopenharmony_cistatic bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) { 143cb93a386Sopenharmony_ci static constexpr SkScalar kClose = (SK_Scalar1 / 16); 144cb93a386Sopenharmony_ci static constexpr SkScalar kCloseSqd = kClose * kClose; 145cb93a386Sopenharmony_ci 146cb93a386Sopenharmony_ci SkScalar distSq = SkPointPriv::DistanceToSqd(p0, p1); 147cb93a386Sopenharmony_ci return distSq < kCloseSqd; 148cb93a386Sopenharmony_ci} 149cb93a386Sopenharmony_ci 150cb93a386Sopenharmony_cistatic SkScalar perp_dot(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) { 151cb93a386Sopenharmony_ci SkVector v0 = p1 - p0; 152cb93a386Sopenharmony_ci SkVector v1 = p2 - p1; 153cb93a386Sopenharmony_ci return v0.cross(v1); 154cb93a386Sopenharmony_ci} 155cb93a386Sopenharmony_ci 156cb93a386Sopenharmony_ciSkBaseShadowTessellator::SkBaseShadowTessellator(const SkPoint3& zPlaneParams, const SkRect& bounds, 157cb93a386Sopenharmony_ci bool transparent) 158cb93a386Sopenharmony_ci : fZPlaneParams(zPlaneParams) 159cb93a386Sopenharmony_ci , fPathBounds(bounds) 160cb93a386Sopenharmony_ci , fCentroid({0, 0}) 161cb93a386Sopenharmony_ci , fArea(0) 162cb93a386Sopenharmony_ci , fLastArea(0) 163cb93a386Sopenharmony_ci , fLastCross(0) 164cb93a386Sopenharmony_ci , fFirstVertexIndex(-1) 165cb93a386Sopenharmony_ci , fSucceeded(false) 166cb93a386Sopenharmony_ci , fTransparent(transparent) 167cb93a386Sopenharmony_ci , fIsConvex(true) 168cb93a386Sopenharmony_ci , fValidUmbra(true) 169cb93a386Sopenharmony_ci , fDirection(1) 170cb93a386Sopenharmony_ci , fPrevUmbraIndex(-1) 171cb93a386Sopenharmony_ci , fCurrUmbraIndex(0) 172cb93a386Sopenharmony_ci , fCurrClipIndex(0) 173cb93a386Sopenharmony_ci , fPrevUmbraOutside(false) 174cb93a386Sopenharmony_ci , fFirstUmbraOutside(false) { 175cb93a386Sopenharmony_ci // child classes will set reserve for positions, colors and indices 176cb93a386Sopenharmony_ci} 177cb93a386Sopenharmony_ci 178cb93a386Sopenharmony_cibool SkBaseShadowTessellator::accumulateCentroid(const SkPoint& curr, const SkPoint& next) { 179cb93a386Sopenharmony_ci if (duplicate_pt(curr, next)) { 180cb93a386Sopenharmony_ci return false; 181cb93a386Sopenharmony_ci } 182cb93a386Sopenharmony_ci 183cb93a386Sopenharmony_ci SkASSERT(fPathPolygon.count() > 0); 184cb93a386Sopenharmony_ci SkVector v0 = curr - fPathPolygon[0]; 185cb93a386Sopenharmony_ci SkVector v1 = next - fPathPolygon[0]; 186cb93a386Sopenharmony_ci SkScalar quadArea = v0.cross(v1); 187cb93a386Sopenharmony_ci fCentroid.fX += (v0.fX + v1.fX) * quadArea; 188cb93a386Sopenharmony_ci fCentroid.fY += (v0.fY + v1.fY) * quadArea; 189cb93a386Sopenharmony_ci fArea += quadArea; 190cb93a386Sopenharmony_ci // convexity check 191cb93a386Sopenharmony_ci if (quadArea*fLastArea < 0) { 192cb93a386Sopenharmony_ci fIsConvex = false; 193cb93a386Sopenharmony_ci } 194cb93a386Sopenharmony_ci if (0 != quadArea) { 195cb93a386Sopenharmony_ci fLastArea = quadArea; 196cb93a386Sopenharmony_ci } 197cb93a386Sopenharmony_ci 198cb93a386Sopenharmony_ci return true; 199cb93a386Sopenharmony_ci} 200cb93a386Sopenharmony_ci 201cb93a386Sopenharmony_cibool SkBaseShadowTessellator::checkConvexity(const SkPoint& p0, 202cb93a386Sopenharmony_ci const SkPoint& p1, 203cb93a386Sopenharmony_ci const SkPoint& p2) { 204cb93a386Sopenharmony_ci SkScalar cross = perp_dot(p0, p1, p2); 205cb93a386Sopenharmony_ci // skip collinear point 206cb93a386Sopenharmony_ci if (SkScalarNearlyZero(cross)) { 207cb93a386Sopenharmony_ci return false; 208cb93a386Sopenharmony_ci } 209cb93a386Sopenharmony_ci 210cb93a386Sopenharmony_ci // check for convexity 211cb93a386Sopenharmony_ci if (fLastCross*cross < 0) { 212cb93a386Sopenharmony_ci fIsConvex = false; 213cb93a386Sopenharmony_ci } 214cb93a386Sopenharmony_ci if (0 != cross) { 215cb93a386Sopenharmony_ci fLastCross = cross; 216cb93a386Sopenharmony_ci } 217cb93a386Sopenharmony_ci 218cb93a386Sopenharmony_ci return true; 219cb93a386Sopenharmony_ci} 220cb93a386Sopenharmony_ci 221cb93a386Sopenharmony_civoid SkBaseShadowTessellator::finishPathPolygon() { 222cb93a386Sopenharmony_ci if (fPathPolygon.count() > 1) { 223cb93a386Sopenharmony_ci if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], fPathPolygon[0])) { 224cb93a386Sopenharmony_ci // remove coincident point 225cb93a386Sopenharmony_ci fPathPolygon.pop(); 226cb93a386Sopenharmony_ci } 227cb93a386Sopenharmony_ci } 228cb93a386Sopenharmony_ci 229cb93a386Sopenharmony_ci if (fPathPolygon.count() > 2) { 230cb93a386Sopenharmony_ci // do this before the final convexity check, so we use the correct fPathPolygon[0] 231cb93a386Sopenharmony_ci fCentroid *= sk_ieee_float_divide(1, 3 * fArea); 232cb93a386Sopenharmony_ci fCentroid += fPathPolygon[0]; 233cb93a386Sopenharmony_ci if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2], 234cb93a386Sopenharmony_ci fPathPolygon[fPathPolygon.count() - 1], 235cb93a386Sopenharmony_ci fPathPolygon[0])) { 236cb93a386Sopenharmony_ci // remove collinear point 237cb93a386Sopenharmony_ci fPathPolygon[0] = fPathPolygon[fPathPolygon.count() - 1]; 238cb93a386Sopenharmony_ci fPathPolygon.pop(); 239cb93a386Sopenharmony_ci } 240cb93a386Sopenharmony_ci } 241cb93a386Sopenharmony_ci 242cb93a386Sopenharmony_ci // if area is positive, winding is ccw 243cb93a386Sopenharmony_ci fDirection = fArea > 0 ? -1 : 1; 244cb93a386Sopenharmony_ci} 245cb93a386Sopenharmony_ci 246cb93a386Sopenharmony_cibool SkBaseShadowTessellator::computeConvexShadow(SkScalar inset, SkScalar outset, bool doClip) { 247cb93a386Sopenharmony_ci if (doClip) { 248cb93a386Sopenharmony_ci this->computeClipVectorsAndTestCentroid(); 249cb93a386Sopenharmony_ci } 250cb93a386Sopenharmony_ci 251cb93a386Sopenharmony_ci // adjust inset distance and umbra color if necessary 252cb93a386Sopenharmony_ci auto umbraColor = kUmbraColor; 253cb93a386Sopenharmony_ci SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, 254cb93a386Sopenharmony_ci fPathPolygon[0], 255cb93a386Sopenharmony_ci fPathPolygon[1]); 256cb93a386Sopenharmony_ci SkRect bounds; 257cb93a386Sopenharmony_ci bounds.setBounds(&fPathPolygon[0], fPathPolygon.count()); 258cb93a386Sopenharmony_ci for (int i = 1; i < fPathPolygon.count(); ++i) { 259cb93a386Sopenharmony_ci int j = i + 1; 260cb93a386Sopenharmony_ci if (i == fPathPolygon.count() - 1) { 261cb93a386Sopenharmony_ci j = 0; 262cb93a386Sopenharmony_ci } 263cb93a386Sopenharmony_ci SkPoint currPoint = fPathPolygon[i]; 264cb93a386Sopenharmony_ci SkPoint nextPoint = fPathPolygon[j]; 265cb93a386Sopenharmony_ci SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint, 266cb93a386Sopenharmony_ci nextPoint); 267cb93a386Sopenharmony_ci if (distSq < minDistSq) { 268cb93a386Sopenharmony_ci minDistSq = distSq; 269cb93a386Sopenharmony_ci } 270cb93a386Sopenharmony_ci } 271cb93a386Sopenharmony_ci 272cb93a386Sopenharmony_ci SkTDArray<SkPoint> insetPolygon; 273cb93a386Sopenharmony_ci if (inset > SK_ScalarNearlyZero) { 274cb93a386Sopenharmony_ci static constexpr auto kTolerance = 1.0e-2f; 275cb93a386Sopenharmony_ci if (minDistSq < (inset + kTolerance)*(inset + kTolerance)) { 276cb93a386Sopenharmony_ci // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha 277cb93a386Sopenharmony_ci auto newInset = SkScalarSqrt(minDistSq) - kTolerance; 278cb93a386Sopenharmony_ci auto ratio = 128 * (newInset / inset + 1); 279cb93a386Sopenharmony_ci SkASSERT(SkScalarIsFinite(ratio)); 280cb93a386Sopenharmony_ci // they aren't PMColors, but the interpolation algorithm is the same 281cb93a386Sopenharmony_ci umbraColor = SkPMLerp(kUmbraColor, kPenumbraColor, (unsigned)ratio); 282cb93a386Sopenharmony_ci inset = newInset; 283cb93a386Sopenharmony_ci } 284cb93a386Sopenharmony_ci 285cb93a386Sopenharmony_ci // generate inner ring 286cb93a386Sopenharmony_ci if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.count(), inset, 287cb93a386Sopenharmony_ci &insetPolygon)) { 288cb93a386Sopenharmony_ci // not ideal, but in this case we'll inset using the centroid 289cb93a386Sopenharmony_ci fValidUmbra = false; 290cb93a386Sopenharmony_ci } 291cb93a386Sopenharmony_ci } 292cb93a386Sopenharmony_ci const SkTDArray<SkPoint>& umbraPolygon = (inset > SK_ScalarNearlyZero) ? insetPolygon 293cb93a386Sopenharmony_ci : fPathPolygon; 294cb93a386Sopenharmony_ci 295cb93a386Sopenharmony_ci // walk around the path polygon, generate outer ring and connect to inner ring 296cb93a386Sopenharmony_ci if (fTransparent) { 297cb93a386Sopenharmony_ci fPositions.push_back(fCentroid); 298cb93a386Sopenharmony_ci fColors.push_back(umbraColor); 299cb93a386Sopenharmony_ci } 300cb93a386Sopenharmony_ci fCurrUmbraIndex = 0; 301cb93a386Sopenharmony_ci 302cb93a386Sopenharmony_ci // initial setup 303cb93a386Sopenharmony_ci // add first quad 304cb93a386Sopenharmony_ci int polyCount = fPathPolygon.count(); 305cb93a386Sopenharmony_ci if (!compute_normal(fPathPolygon[polyCount - 1], fPathPolygon[0], fDirection, &fFirstOutset)) { 306cb93a386Sopenharmony_ci // polygon should be sanitized by this point, so this is unrecoverable 307cb93a386Sopenharmony_ci return false; 308cb93a386Sopenharmony_ci } 309cb93a386Sopenharmony_ci 310cb93a386Sopenharmony_ci fFirstOutset *= outset; 311cb93a386Sopenharmony_ci fFirstPoint = fPathPolygon[polyCount - 1]; 312cb93a386Sopenharmony_ci fFirstVertexIndex = fPositions.count(); 313cb93a386Sopenharmony_ci fPrevOutset = fFirstOutset; 314cb93a386Sopenharmony_ci fPrevPoint = fFirstPoint; 315cb93a386Sopenharmony_ci fPrevUmbraIndex = -1; 316cb93a386Sopenharmony_ci 317cb93a386Sopenharmony_ci this->addInnerPoint(fFirstPoint, umbraColor, umbraPolygon, &fPrevUmbraIndex); 318cb93a386Sopenharmony_ci 319cb93a386Sopenharmony_ci if (!fTransparent && doClip) { 320cb93a386Sopenharmony_ci SkPoint clipPoint; 321cb93a386Sopenharmony_ci bool isOutside = this->clipUmbraPoint(fPositions[fFirstVertexIndex], 322cb93a386Sopenharmony_ci fCentroid, &clipPoint); 323cb93a386Sopenharmony_ci if (isOutside) { 324cb93a386Sopenharmony_ci fPositions.push_back(clipPoint); 325cb93a386Sopenharmony_ci fColors.push_back(umbraColor); 326cb93a386Sopenharmony_ci } 327cb93a386Sopenharmony_ci fPrevUmbraOutside = isOutside; 328cb93a386Sopenharmony_ci fFirstUmbraOutside = isOutside; 329cb93a386Sopenharmony_ci } 330cb93a386Sopenharmony_ci 331cb93a386Sopenharmony_ci SkPoint newPoint = fFirstPoint + fFirstOutset; 332cb93a386Sopenharmony_ci fPositions.push_back(newPoint); 333cb93a386Sopenharmony_ci fColors.push_back(kPenumbraColor); 334cb93a386Sopenharmony_ci this->addEdge(fPathPolygon[0], fFirstOutset, umbraColor, umbraPolygon, false, doClip); 335cb93a386Sopenharmony_ci 336cb93a386Sopenharmony_ci for (int i = 1; i < polyCount; ++i) { 337cb93a386Sopenharmony_ci SkVector normal; 338cb93a386Sopenharmony_ci if (!compute_normal(fPrevPoint, fPathPolygon[i], fDirection, &normal)) { 339cb93a386Sopenharmony_ci return false; 340cb93a386Sopenharmony_ci } 341cb93a386Sopenharmony_ci normal *= outset; 342cb93a386Sopenharmony_ci this->addArc(normal, outset, true); 343cb93a386Sopenharmony_ci this->addEdge(fPathPolygon[i], normal, umbraColor, umbraPolygon, 344cb93a386Sopenharmony_ci i == polyCount - 1, doClip); 345cb93a386Sopenharmony_ci } 346cb93a386Sopenharmony_ci SkASSERT(this->indexCount()); 347cb93a386Sopenharmony_ci 348cb93a386Sopenharmony_ci // final fan 349cb93a386Sopenharmony_ci SkASSERT(fPositions.count() >= 3); 350cb93a386Sopenharmony_ci if (this->addArc(fFirstOutset, outset, false)) { 351cb93a386Sopenharmony_ci if (fFirstUmbraOutside) { 352cb93a386Sopenharmony_ci this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1, 353cb93a386Sopenharmony_ci fFirstVertexIndex + 2); 354cb93a386Sopenharmony_ci } else { 355cb93a386Sopenharmony_ci this->appendTriangle(fFirstVertexIndex, fPositions.count() - 1, 356cb93a386Sopenharmony_ci fFirstVertexIndex + 1); 357cb93a386Sopenharmony_ci } 358cb93a386Sopenharmony_ci } else { 359cb93a386Sopenharmony_ci // no arc added, fix up by setting first penumbra point position to last one 360cb93a386Sopenharmony_ci if (fFirstUmbraOutside) { 361cb93a386Sopenharmony_ci fPositions[fFirstVertexIndex + 2] = fPositions[fPositions.count() - 1]; 362cb93a386Sopenharmony_ci } else { 363cb93a386Sopenharmony_ci fPositions[fFirstVertexIndex + 1] = fPositions[fPositions.count() - 1]; 364cb93a386Sopenharmony_ci } 365cb93a386Sopenharmony_ci } 366cb93a386Sopenharmony_ci 367cb93a386Sopenharmony_ci return true; 368cb93a386Sopenharmony_ci} 369cb93a386Sopenharmony_ci 370cb93a386Sopenharmony_civoid SkBaseShadowTessellator::computeClipVectorsAndTestCentroid() { 371cb93a386Sopenharmony_ci SkASSERT(fClipPolygon.count() >= 3); 372cb93a386Sopenharmony_ci fCurrClipIndex = fClipPolygon.count() - 1; 373cb93a386Sopenharmony_ci 374cb93a386Sopenharmony_ci // init clip vectors 375cb93a386Sopenharmony_ci SkVector v0 = fClipPolygon[1] - fClipPolygon[0]; 376cb93a386Sopenharmony_ci SkVector v1 = fClipPolygon[2] - fClipPolygon[0]; 377cb93a386Sopenharmony_ci fClipVectors.push_back(v0); 378cb93a386Sopenharmony_ci 379cb93a386Sopenharmony_ci // init centroid check 380cb93a386Sopenharmony_ci bool hiddenCentroid = true; 381cb93a386Sopenharmony_ci v1 = fCentroid - fClipPolygon[0]; 382cb93a386Sopenharmony_ci SkScalar initCross = v0.cross(v1); 383cb93a386Sopenharmony_ci 384cb93a386Sopenharmony_ci for (int p = 1; p < fClipPolygon.count(); ++p) { 385cb93a386Sopenharmony_ci // add to clip vectors 386cb93a386Sopenharmony_ci v0 = fClipPolygon[(p + 1) % fClipPolygon.count()] - fClipPolygon[p]; 387cb93a386Sopenharmony_ci fClipVectors.push_back(v0); 388cb93a386Sopenharmony_ci // Determine if transformed centroid is inside clipPolygon. 389cb93a386Sopenharmony_ci v1 = fCentroid - fClipPolygon[p]; 390cb93a386Sopenharmony_ci if (initCross*v0.cross(v1) <= 0) { 391cb93a386Sopenharmony_ci hiddenCentroid = false; 392cb93a386Sopenharmony_ci } 393cb93a386Sopenharmony_ci } 394cb93a386Sopenharmony_ci SkASSERT(fClipVectors.count() == fClipPolygon.count()); 395cb93a386Sopenharmony_ci 396cb93a386Sopenharmony_ci fTransparent = fTransparent || !hiddenCentroid; 397cb93a386Sopenharmony_ci} 398cb93a386Sopenharmony_ci 399cb93a386Sopenharmony_civoid SkBaseShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal, 400cb93a386Sopenharmony_ci SkColor umbraColor, const SkTDArray<SkPoint>& umbraPolygon, 401cb93a386Sopenharmony_ci bool lastEdge, bool doClip) { 402cb93a386Sopenharmony_ci // add next umbra point 403cb93a386Sopenharmony_ci int currUmbraIndex; 404cb93a386Sopenharmony_ci bool duplicate; 405cb93a386Sopenharmony_ci if (lastEdge) { 406cb93a386Sopenharmony_ci duplicate = false; 407cb93a386Sopenharmony_ci currUmbraIndex = fFirstVertexIndex; 408cb93a386Sopenharmony_ci fPrevPoint = nextPoint; 409cb93a386Sopenharmony_ci } else { 410cb93a386Sopenharmony_ci duplicate = this->addInnerPoint(nextPoint, umbraColor, umbraPolygon, &currUmbraIndex); 411cb93a386Sopenharmony_ci } 412cb93a386Sopenharmony_ci int prevPenumbraIndex = duplicate || (currUmbraIndex == fFirstVertexIndex) 413cb93a386Sopenharmony_ci ? fPositions.count() - 1 414cb93a386Sopenharmony_ci : fPositions.count() - 2; 415cb93a386Sopenharmony_ci if (!duplicate) { 416cb93a386Sopenharmony_ci // add to center fan if transparent or centroid showing 417cb93a386Sopenharmony_ci if (fTransparent) { 418cb93a386Sopenharmony_ci this->appendTriangle(0, fPrevUmbraIndex, currUmbraIndex); 419cb93a386Sopenharmony_ci // otherwise add to clip ring 420cb93a386Sopenharmony_ci } else if (doClip) { 421cb93a386Sopenharmony_ci SkPoint clipPoint; 422cb93a386Sopenharmony_ci bool isOutside = lastEdge ? fFirstUmbraOutside 423cb93a386Sopenharmony_ci : this->clipUmbraPoint(fPositions[currUmbraIndex], fCentroid, 424cb93a386Sopenharmony_ci &clipPoint); 425cb93a386Sopenharmony_ci if (isOutside) { 426cb93a386Sopenharmony_ci if (!lastEdge) { 427cb93a386Sopenharmony_ci fPositions.push_back(clipPoint); 428cb93a386Sopenharmony_ci fColors.push_back(umbraColor); 429cb93a386Sopenharmony_ci } 430cb93a386Sopenharmony_ci this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, currUmbraIndex + 1); 431cb93a386Sopenharmony_ci if (fPrevUmbraOutside) { 432cb93a386Sopenharmony_ci // fill out quad 433cb93a386Sopenharmony_ci this->appendTriangle(fPrevUmbraIndex, currUmbraIndex + 1, 434cb93a386Sopenharmony_ci fPrevUmbraIndex + 1); 435cb93a386Sopenharmony_ci } 436cb93a386Sopenharmony_ci } else if (fPrevUmbraOutside) { 437cb93a386Sopenharmony_ci // add tri 438cb93a386Sopenharmony_ci this->appendTriangle(fPrevUmbraIndex, currUmbraIndex, fPrevUmbraIndex + 1); 439cb93a386Sopenharmony_ci } 440cb93a386Sopenharmony_ci 441cb93a386Sopenharmony_ci fPrevUmbraOutside = isOutside; 442cb93a386Sopenharmony_ci } 443cb93a386Sopenharmony_ci } 444cb93a386Sopenharmony_ci 445cb93a386Sopenharmony_ci // add next penumbra point and quad 446cb93a386Sopenharmony_ci SkPoint newPoint = nextPoint + nextNormal; 447cb93a386Sopenharmony_ci fPositions.push_back(newPoint); 448cb93a386Sopenharmony_ci fColors.push_back(kPenumbraColor); 449cb93a386Sopenharmony_ci 450cb93a386Sopenharmony_ci if (!duplicate) { 451cb93a386Sopenharmony_ci this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex); 452cb93a386Sopenharmony_ci } 453cb93a386Sopenharmony_ci this->appendTriangle(prevPenumbraIndex, fPositions.count() - 1, currUmbraIndex); 454cb93a386Sopenharmony_ci 455cb93a386Sopenharmony_ci fPrevUmbraIndex = currUmbraIndex; 456cb93a386Sopenharmony_ci fPrevOutset = nextNormal; 457cb93a386Sopenharmony_ci} 458cb93a386Sopenharmony_ci 459cb93a386Sopenharmony_cibool SkBaseShadowTessellator::clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, 460cb93a386Sopenharmony_ci SkPoint* clipPoint) { 461cb93a386Sopenharmony_ci SkVector segmentVector = centroid - umbraPoint; 462cb93a386Sopenharmony_ci 463cb93a386Sopenharmony_ci int startClipPoint = fCurrClipIndex; 464cb93a386Sopenharmony_ci do { 465cb93a386Sopenharmony_ci SkVector dp = umbraPoint - fClipPolygon[fCurrClipIndex]; 466cb93a386Sopenharmony_ci SkScalar denom = fClipVectors[fCurrClipIndex].cross(segmentVector); 467cb93a386Sopenharmony_ci SkScalar t_num = dp.cross(segmentVector); 468cb93a386Sopenharmony_ci // if line segments are nearly parallel 469cb93a386Sopenharmony_ci if (SkScalarNearlyZero(denom)) { 470cb93a386Sopenharmony_ci // and collinear 471cb93a386Sopenharmony_ci if (SkScalarNearlyZero(t_num)) { 472cb93a386Sopenharmony_ci return false; 473cb93a386Sopenharmony_ci } 474cb93a386Sopenharmony_ci // otherwise are separate, will try the next poly segment 475cb93a386Sopenharmony_ci // else if crossing lies within poly segment 476cb93a386Sopenharmony_ci } else if (t_num >= 0 && t_num <= denom) { 477cb93a386Sopenharmony_ci SkScalar s_num = dp.cross(fClipVectors[fCurrClipIndex]); 478cb93a386Sopenharmony_ci // if umbra point is inside the clip polygon 479cb93a386Sopenharmony_ci if (s_num >= 0 && s_num <= denom) { 480cb93a386Sopenharmony_ci segmentVector *= s_num / denom; 481cb93a386Sopenharmony_ci *clipPoint = umbraPoint + segmentVector; 482cb93a386Sopenharmony_ci return true; 483cb93a386Sopenharmony_ci } 484cb93a386Sopenharmony_ci } 485cb93a386Sopenharmony_ci fCurrClipIndex = (fCurrClipIndex + 1) % fClipPolygon.count(); 486cb93a386Sopenharmony_ci } while (fCurrClipIndex != startClipPoint); 487cb93a386Sopenharmony_ci 488cb93a386Sopenharmony_ci return false; 489cb93a386Sopenharmony_ci} 490cb93a386Sopenharmony_ci 491cb93a386Sopenharmony_cibool SkBaseShadowTessellator::addInnerPoint(const SkPoint& pathPoint, SkColor umbraColor, 492cb93a386Sopenharmony_ci const SkTDArray<SkPoint>& umbraPolygon, 493cb93a386Sopenharmony_ci int* currUmbraIndex) { 494cb93a386Sopenharmony_ci SkPoint umbraPoint; 495cb93a386Sopenharmony_ci if (!fValidUmbra) { 496cb93a386Sopenharmony_ci SkVector v = fCentroid - pathPoint; 497cb93a386Sopenharmony_ci v *= 0.95f; 498cb93a386Sopenharmony_ci umbraPoint = pathPoint + v; 499cb93a386Sopenharmony_ci } else { 500cb93a386Sopenharmony_ci umbraPoint = umbraPolygon[this->getClosestUmbraIndex(pathPoint, umbraPolygon)]; 501cb93a386Sopenharmony_ci } 502cb93a386Sopenharmony_ci 503cb93a386Sopenharmony_ci fPrevPoint = pathPoint; 504cb93a386Sopenharmony_ci 505cb93a386Sopenharmony_ci // merge "close" points 506cb93a386Sopenharmony_ci if (fPrevUmbraIndex == -1 || 507cb93a386Sopenharmony_ci !duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) { 508cb93a386Sopenharmony_ci // if we've wrapped around, don't add a new point 509cb93a386Sopenharmony_ci if (fPrevUmbraIndex >= 0 && duplicate_pt(umbraPoint, fPositions[fFirstVertexIndex])) { 510cb93a386Sopenharmony_ci *currUmbraIndex = fFirstVertexIndex; 511cb93a386Sopenharmony_ci } else { 512cb93a386Sopenharmony_ci *currUmbraIndex = fPositions.count(); 513cb93a386Sopenharmony_ci fPositions.push_back(umbraPoint); 514cb93a386Sopenharmony_ci fColors.push_back(umbraColor); 515cb93a386Sopenharmony_ci } 516cb93a386Sopenharmony_ci return false; 517cb93a386Sopenharmony_ci } else { 518cb93a386Sopenharmony_ci *currUmbraIndex = fPrevUmbraIndex; 519cb93a386Sopenharmony_ci return true; 520cb93a386Sopenharmony_ci } 521cb93a386Sopenharmony_ci} 522cb93a386Sopenharmony_ci 523cb93a386Sopenharmony_ciint SkBaseShadowTessellator::getClosestUmbraIndex(const SkPoint& p, 524cb93a386Sopenharmony_ci const SkTDArray<SkPoint>& umbraPolygon) { 525cb93a386Sopenharmony_ci SkScalar minDistance = SkPointPriv::DistanceToSqd(p, umbraPolygon[fCurrUmbraIndex]); 526cb93a386Sopenharmony_ci int index = fCurrUmbraIndex; 527cb93a386Sopenharmony_ci int dir = 1; 528cb93a386Sopenharmony_ci int next = (index + dir) % umbraPolygon.count(); 529cb93a386Sopenharmony_ci 530cb93a386Sopenharmony_ci // init travel direction 531cb93a386Sopenharmony_ci SkScalar distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]); 532cb93a386Sopenharmony_ci if (distance < minDistance) { 533cb93a386Sopenharmony_ci index = next; 534cb93a386Sopenharmony_ci minDistance = distance; 535cb93a386Sopenharmony_ci } else { 536cb93a386Sopenharmony_ci dir = umbraPolygon.count() - 1; 537cb93a386Sopenharmony_ci } 538cb93a386Sopenharmony_ci 539cb93a386Sopenharmony_ci // iterate until we find a point that increases the distance 540cb93a386Sopenharmony_ci next = (index + dir) % umbraPolygon.count(); 541cb93a386Sopenharmony_ci distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]); 542cb93a386Sopenharmony_ci while (distance < minDistance) { 543cb93a386Sopenharmony_ci index = next; 544cb93a386Sopenharmony_ci minDistance = distance; 545cb93a386Sopenharmony_ci next = (index + dir) % umbraPolygon.count(); 546cb93a386Sopenharmony_ci distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]); 547cb93a386Sopenharmony_ci } 548cb93a386Sopenharmony_ci 549cb93a386Sopenharmony_ci fCurrUmbraIndex = index; 550cb93a386Sopenharmony_ci return index; 551cb93a386Sopenharmony_ci} 552cb93a386Sopenharmony_ci 553cb93a386Sopenharmony_cibool SkBaseShadowTessellator::computeConcaveShadow(SkScalar inset, SkScalar outset) { 554cb93a386Sopenharmony_ci if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) { 555cb93a386Sopenharmony_ci return false; 556cb93a386Sopenharmony_ci } 557cb93a386Sopenharmony_ci 558cb93a386Sopenharmony_ci // generate inner ring 559cb93a386Sopenharmony_ci SkTDArray<SkPoint> umbraPolygon; 560cb93a386Sopenharmony_ci SkTDArray<int> umbraIndices; 561cb93a386Sopenharmony_ci umbraIndices.setReserve(fPathPolygon.count()); 562cb93a386Sopenharmony_ci if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), fPathBounds, inset, 563cb93a386Sopenharmony_ci &umbraPolygon, &umbraIndices)) { 564cb93a386Sopenharmony_ci // TODO: figure out how to handle this case 565cb93a386Sopenharmony_ci return false; 566cb93a386Sopenharmony_ci } 567cb93a386Sopenharmony_ci 568cb93a386Sopenharmony_ci // generate outer ring 569cb93a386Sopenharmony_ci SkTDArray<SkPoint> penumbraPolygon; 570cb93a386Sopenharmony_ci SkTDArray<int> penumbraIndices; 571cb93a386Sopenharmony_ci penumbraPolygon.setReserve(umbraPolygon.count()); 572cb93a386Sopenharmony_ci penumbraIndices.setReserve(umbraPolygon.count()); 573cb93a386Sopenharmony_ci if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.count(), fPathBounds, -outset, 574cb93a386Sopenharmony_ci &penumbraPolygon, &penumbraIndices)) { 575cb93a386Sopenharmony_ci // TODO: figure out how to handle this case 576cb93a386Sopenharmony_ci return false; 577cb93a386Sopenharmony_ci } 578cb93a386Sopenharmony_ci 579cb93a386Sopenharmony_ci if (!umbraPolygon.count() || !penumbraPolygon.count()) { 580cb93a386Sopenharmony_ci return false; 581cb93a386Sopenharmony_ci } 582cb93a386Sopenharmony_ci 583cb93a386Sopenharmony_ci // attach the rings together 584cb93a386Sopenharmony_ci this->stitchConcaveRings(umbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices); 585cb93a386Sopenharmony_ci 586cb93a386Sopenharmony_ci return true; 587cb93a386Sopenharmony_ci} 588cb93a386Sopenharmony_ci 589cb93a386Sopenharmony_civoid SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon, 590cb93a386Sopenharmony_ci SkTDArray<int>* umbraIndices, 591cb93a386Sopenharmony_ci const SkTDArray<SkPoint>& penumbraPolygon, 592cb93a386Sopenharmony_ci SkTDArray<int>* penumbraIndices) { 593cb93a386Sopenharmony_ci // TODO: only create and fill indexMap when fTransparent is true? 594cb93a386Sopenharmony_ci SkAutoSTMalloc<64, uint16_t> indexMap(umbraPolygon.count()); 595cb93a386Sopenharmony_ci 596cb93a386Sopenharmony_ci // find minimum indices 597cb93a386Sopenharmony_ci int minIndex = 0; 598cb93a386Sopenharmony_ci int min = (*penumbraIndices)[0]; 599cb93a386Sopenharmony_ci for (int i = 1; i < (*penumbraIndices).count(); ++i) { 600cb93a386Sopenharmony_ci if ((*penumbraIndices)[i] < min) { 601cb93a386Sopenharmony_ci min = (*penumbraIndices)[i]; 602cb93a386Sopenharmony_ci minIndex = i; 603cb93a386Sopenharmony_ci } 604cb93a386Sopenharmony_ci } 605cb93a386Sopenharmony_ci int currPenumbra = minIndex; 606cb93a386Sopenharmony_ci 607cb93a386Sopenharmony_ci minIndex = 0; 608cb93a386Sopenharmony_ci min = (*umbraIndices)[0]; 609cb93a386Sopenharmony_ci for (int i = 1; i < (*umbraIndices).count(); ++i) { 610cb93a386Sopenharmony_ci if ((*umbraIndices)[i] < min) { 611cb93a386Sopenharmony_ci min = (*umbraIndices)[i]; 612cb93a386Sopenharmony_ci minIndex = i; 613cb93a386Sopenharmony_ci } 614cb93a386Sopenharmony_ci } 615cb93a386Sopenharmony_ci int currUmbra = minIndex; 616cb93a386Sopenharmony_ci 617cb93a386Sopenharmony_ci // now find a case where the indices are equal (there should be at least one) 618cb93a386Sopenharmony_ci int maxPenumbraIndex = fPathPolygon.count() - 1; 619cb93a386Sopenharmony_ci int maxUmbraIndex = fPathPolygon.count() - 1; 620cb93a386Sopenharmony_ci while ((*penumbraIndices)[currPenumbra] != (*umbraIndices)[currUmbra]) { 621cb93a386Sopenharmony_ci if ((*penumbraIndices)[currPenumbra] < (*umbraIndices)[currUmbra]) { 622cb93a386Sopenharmony_ci (*penumbraIndices)[currPenumbra] += fPathPolygon.count(); 623cb93a386Sopenharmony_ci maxPenumbraIndex = (*penumbraIndices)[currPenumbra]; 624cb93a386Sopenharmony_ci currPenumbra = (currPenumbra + 1) % penumbraPolygon.count(); 625cb93a386Sopenharmony_ci } else { 626cb93a386Sopenharmony_ci (*umbraIndices)[currUmbra] += fPathPolygon.count(); 627cb93a386Sopenharmony_ci maxUmbraIndex = (*umbraIndices)[currUmbra]; 628cb93a386Sopenharmony_ci currUmbra = (currUmbra + 1) % umbraPolygon.count(); 629cb93a386Sopenharmony_ci } 630cb93a386Sopenharmony_ci } 631cb93a386Sopenharmony_ci 632cb93a386Sopenharmony_ci fPositions.push_back(penumbraPolygon[currPenumbra]); 633cb93a386Sopenharmony_ci fColors.push_back(kPenumbraColor); 634cb93a386Sopenharmony_ci int prevPenumbraIndex = 0; 635cb93a386Sopenharmony_ci fPositions.push_back(umbraPolygon[currUmbra]); 636cb93a386Sopenharmony_ci fColors.push_back(kUmbraColor); 637cb93a386Sopenharmony_ci fPrevUmbraIndex = 1; 638cb93a386Sopenharmony_ci indexMap[currUmbra] = 1; 639cb93a386Sopenharmony_ci 640cb93a386Sopenharmony_ci int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count(); 641cb93a386Sopenharmony_ci int nextUmbra = (currUmbra + 1) % umbraPolygon.count(); 642cb93a386Sopenharmony_ci while ((*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex || 643cb93a386Sopenharmony_ci (*umbraIndices)[nextUmbra] <= maxUmbraIndex) { 644cb93a386Sopenharmony_ci 645cb93a386Sopenharmony_ci if ((*umbraIndices)[nextUmbra] == (*penumbraIndices)[nextPenumbra]) { 646cb93a386Sopenharmony_ci // advance both one step 647cb93a386Sopenharmony_ci fPositions.push_back(penumbraPolygon[nextPenumbra]); 648cb93a386Sopenharmony_ci fColors.push_back(kPenumbraColor); 649cb93a386Sopenharmony_ci int currPenumbraIndex = fPositions.count() - 1; 650cb93a386Sopenharmony_ci 651cb93a386Sopenharmony_ci fPositions.push_back(umbraPolygon[nextUmbra]); 652cb93a386Sopenharmony_ci fColors.push_back(kUmbraColor); 653cb93a386Sopenharmony_ci int currUmbraIndex = fPositions.count() - 1; 654cb93a386Sopenharmony_ci indexMap[nextUmbra] = currUmbraIndex; 655cb93a386Sopenharmony_ci 656cb93a386Sopenharmony_ci this->appendQuad(prevPenumbraIndex, currPenumbraIndex, 657cb93a386Sopenharmony_ci fPrevUmbraIndex, currUmbraIndex); 658cb93a386Sopenharmony_ci 659cb93a386Sopenharmony_ci prevPenumbraIndex = currPenumbraIndex; 660cb93a386Sopenharmony_ci (*penumbraIndices)[currPenumbra] += fPathPolygon.count(); 661cb93a386Sopenharmony_ci currPenumbra = nextPenumbra; 662cb93a386Sopenharmony_ci nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count(); 663cb93a386Sopenharmony_ci 664cb93a386Sopenharmony_ci fPrevUmbraIndex = currUmbraIndex; 665cb93a386Sopenharmony_ci (*umbraIndices)[currUmbra] += fPathPolygon.count(); 666cb93a386Sopenharmony_ci currUmbra = nextUmbra; 667cb93a386Sopenharmony_ci nextUmbra = (currUmbra + 1) % umbraPolygon.count(); 668cb93a386Sopenharmony_ci } 669cb93a386Sopenharmony_ci 670cb93a386Sopenharmony_ci while ((*penumbraIndices)[nextPenumbra] < (*umbraIndices)[nextUmbra] && 671cb93a386Sopenharmony_ci (*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex) { 672cb93a386Sopenharmony_ci // fill out penumbra arc 673cb93a386Sopenharmony_ci fPositions.push_back(penumbraPolygon[nextPenumbra]); 674cb93a386Sopenharmony_ci fColors.push_back(kPenumbraColor); 675cb93a386Sopenharmony_ci int currPenumbraIndex = fPositions.count() - 1; 676cb93a386Sopenharmony_ci 677cb93a386Sopenharmony_ci this->appendTriangle(prevPenumbraIndex, currPenumbraIndex, fPrevUmbraIndex); 678cb93a386Sopenharmony_ci 679cb93a386Sopenharmony_ci prevPenumbraIndex = currPenumbraIndex; 680cb93a386Sopenharmony_ci // this ensures the ordering when we wrap around 681cb93a386Sopenharmony_ci (*penumbraIndices)[currPenumbra] += fPathPolygon.count(); 682cb93a386Sopenharmony_ci currPenumbra = nextPenumbra; 683cb93a386Sopenharmony_ci nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count(); 684cb93a386Sopenharmony_ci } 685cb93a386Sopenharmony_ci 686cb93a386Sopenharmony_ci while ((*umbraIndices)[nextUmbra] < (*penumbraIndices)[nextPenumbra] && 687cb93a386Sopenharmony_ci (*umbraIndices)[nextUmbra] <= maxUmbraIndex) { 688cb93a386Sopenharmony_ci // fill out umbra arc 689cb93a386Sopenharmony_ci fPositions.push_back(umbraPolygon[nextUmbra]); 690cb93a386Sopenharmony_ci fColors.push_back(kUmbraColor); 691cb93a386Sopenharmony_ci int currUmbraIndex = fPositions.count() - 1; 692cb93a386Sopenharmony_ci indexMap[nextUmbra] = currUmbraIndex; 693cb93a386Sopenharmony_ci 694cb93a386Sopenharmony_ci this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex); 695cb93a386Sopenharmony_ci 696cb93a386Sopenharmony_ci fPrevUmbraIndex = currUmbraIndex; 697cb93a386Sopenharmony_ci // this ensures the ordering when we wrap around 698cb93a386Sopenharmony_ci (*umbraIndices)[currUmbra] += fPathPolygon.count(); 699cb93a386Sopenharmony_ci currUmbra = nextUmbra; 700cb93a386Sopenharmony_ci nextUmbra = (currUmbra + 1) % umbraPolygon.count(); 701cb93a386Sopenharmony_ci } 702cb93a386Sopenharmony_ci } 703cb93a386Sopenharmony_ci // finish up by advancing both one step 704cb93a386Sopenharmony_ci fPositions.push_back(penumbraPolygon[nextPenumbra]); 705cb93a386Sopenharmony_ci fColors.push_back(kPenumbraColor); 706cb93a386Sopenharmony_ci int currPenumbraIndex = fPositions.count() - 1; 707cb93a386Sopenharmony_ci 708cb93a386Sopenharmony_ci fPositions.push_back(umbraPolygon[nextUmbra]); 709cb93a386Sopenharmony_ci fColors.push_back(kUmbraColor); 710cb93a386Sopenharmony_ci int currUmbraIndex = fPositions.count() - 1; 711cb93a386Sopenharmony_ci indexMap[nextUmbra] = currUmbraIndex; 712cb93a386Sopenharmony_ci 713cb93a386Sopenharmony_ci this->appendQuad(prevPenumbraIndex, currPenumbraIndex, 714cb93a386Sopenharmony_ci fPrevUmbraIndex, currUmbraIndex); 715cb93a386Sopenharmony_ci 716cb93a386Sopenharmony_ci if (fTransparent) { 717cb93a386Sopenharmony_ci SkTriangulateSimplePolygon(umbraPolygon.begin(), indexMap, umbraPolygon.count(), 718cb93a386Sopenharmony_ci &fIndices); 719cb93a386Sopenharmony_ci } 720cb93a386Sopenharmony_ci} 721cb93a386Sopenharmony_ci 722cb93a386Sopenharmony_ci 723cb93a386Sopenharmony_ci// tesselation tolerance values, in device space pixels 724cb93a386Sopenharmony_ci#if SK_SUPPORT_GPU 725cb93a386Sopenharmony_cistatic const SkScalar kQuadTolerance = 0.2f; 726cb93a386Sopenharmony_cistatic const SkScalar kCubicTolerance = 0.2f; 727cb93a386Sopenharmony_ci#endif 728cb93a386Sopenharmony_cistatic const SkScalar kConicTolerance = 0.25f; 729cb93a386Sopenharmony_ci 730cb93a386Sopenharmony_ci// clamps the point to the nearest 16th of a pixel 731cb93a386Sopenharmony_cistatic void sanitize_point(const SkPoint& in, SkPoint* out) { 732cb93a386Sopenharmony_ci out->fX = SkScalarRoundToScalar(16.f*in.fX)*0.0625f; 733cb93a386Sopenharmony_ci out->fY = SkScalarRoundToScalar(16.f*in.fY)*0.0625f; 734cb93a386Sopenharmony_ci} 735cb93a386Sopenharmony_ci 736cb93a386Sopenharmony_civoid SkBaseShadowTessellator::handleLine(const SkPoint& p) { 737cb93a386Sopenharmony_ci SkPoint pSanitized; 738cb93a386Sopenharmony_ci sanitize_point(p, &pSanitized); 739cb93a386Sopenharmony_ci 740cb93a386Sopenharmony_ci if (fPathPolygon.count() > 0) { 741cb93a386Sopenharmony_ci if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.count() - 1], pSanitized)) { 742cb93a386Sopenharmony_ci // skip coincident point 743cb93a386Sopenharmony_ci return; 744cb93a386Sopenharmony_ci } 745cb93a386Sopenharmony_ci } 746cb93a386Sopenharmony_ci 747cb93a386Sopenharmony_ci if (fPathPolygon.count() > 1) { 748cb93a386Sopenharmony_ci if (!checkConvexity(fPathPolygon[fPathPolygon.count() - 2], 749cb93a386Sopenharmony_ci fPathPolygon[fPathPolygon.count() - 1], 750cb93a386Sopenharmony_ci pSanitized)) { 751cb93a386Sopenharmony_ci // remove collinear point 752cb93a386Sopenharmony_ci fPathPolygon.pop(); 753cb93a386Sopenharmony_ci // it's possible that the previous point is coincident with the new one now 754cb93a386Sopenharmony_ci if (duplicate_pt(fPathPolygon[fPathPolygon.count() - 1], pSanitized)) { 755cb93a386Sopenharmony_ci fPathPolygon.pop(); 756cb93a386Sopenharmony_ci } 757cb93a386Sopenharmony_ci } 758cb93a386Sopenharmony_ci } 759cb93a386Sopenharmony_ci 760cb93a386Sopenharmony_ci fPathPolygon.push_back(pSanitized); 761cb93a386Sopenharmony_ci} 762cb93a386Sopenharmony_ci 763cb93a386Sopenharmony_civoid SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) { 764cb93a386Sopenharmony_ci m.mapPoints(p, 1); 765cb93a386Sopenharmony_ci 766cb93a386Sopenharmony_ci this->handleLine(*p); 767cb93a386Sopenharmony_ci} 768cb93a386Sopenharmony_ci 769cb93a386Sopenharmony_civoid SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) { 770cb93a386Sopenharmony_ci#if SK_SUPPORT_GPU 771cb93a386Sopenharmony_ci // check for degeneracy 772cb93a386Sopenharmony_ci SkVector v0 = pts[1] - pts[0]; 773cb93a386Sopenharmony_ci SkVector v1 = pts[2] - pts[0]; 774cb93a386Sopenharmony_ci if (SkScalarNearlyZero(v0.cross(v1))) { 775cb93a386Sopenharmony_ci return; 776cb93a386Sopenharmony_ci } 777cb93a386Sopenharmony_ci // TODO: Pull PathUtils out of Ganesh? 778cb93a386Sopenharmony_ci int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance); 779cb93a386Sopenharmony_ci fPointBuffer.setCount(maxCount); 780cb93a386Sopenharmony_ci SkPoint* target = fPointBuffer.begin(); 781cb93a386Sopenharmony_ci int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2], 782cb93a386Sopenharmony_ci kQuadTolerance, &target, maxCount); 783cb93a386Sopenharmony_ci fPointBuffer.setCount(count); 784cb93a386Sopenharmony_ci for (int i = 0; i < count; i++) { 785cb93a386Sopenharmony_ci this->handleLine(fPointBuffer[i]); 786cb93a386Sopenharmony_ci } 787cb93a386Sopenharmony_ci#else 788cb93a386Sopenharmony_ci // for now, just to draw something 789cb93a386Sopenharmony_ci this->handleLine(pts[1]); 790cb93a386Sopenharmony_ci this->handleLine(pts[2]); 791cb93a386Sopenharmony_ci#endif 792cb93a386Sopenharmony_ci} 793cb93a386Sopenharmony_ci 794cb93a386Sopenharmony_civoid SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) { 795cb93a386Sopenharmony_ci m.mapPoints(pts, 3); 796cb93a386Sopenharmony_ci this->handleQuad(pts); 797cb93a386Sopenharmony_ci} 798cb93a386Sopenharmony_ci 799cb93a386Sopenharmony_civoid SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) { 800cb93a386Sopenharmony_ci m.mapPoints(pts, 4); 801cb93a386Sopenharmony_ci#if SK_SUPPORT_GPU 802cb93a386Sopenharmony_ci // TODO: Pull PathUtils out of Ganesh? 803cb93a386Sopenharmony_ci int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance); 804cb93a386Sopenharmony_ci fPointBuffer.setCount(maxCount); 805cb93a386Sopenharmony_ci SkPoint* target = fPointBuffer.begin(); 806cb93a386Sopenharmony_ci int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3], 807cb93a386Sopenharmony_ci kCubicTolerance, &target, maxCount); 808cb93a386Sopenharmony_ci fPointBuffer.setCount(count); 809cb93a386Sopenharmony_ci for (int i = 0; i < count; i++) { 810cb93a386Sopenharmony_ci this->handleLine(fPointBuffer[i]); 811cb93a386Sopenharmony_ci } 812cb93a386Sopenharmony_ci#else 813cb93a386Sopenharmony_ci // for now, just to draw something 814cb93a386Sopenharmony_ci this->handleLine(pts[1]); 815cb93a386Sopenharmony_ci this->handleLine(pts[2]); 816cb93a386Sopenharmony_ci this->handleLine(pts[3]); 817cb93a386Sopenharmony_ci#endif 818cb93a386Sopenharmony_ci} 819cb93a386Sopenharmony_ci 820cb93a386Sopenharmony_civoid SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) { 821cb93a386Sopenharmony_ci if (m.hasPerspective()) { 822cb93a386Sopenharmony_ci w = SkConic::TransformW(pts, w, m); 823cb93a386Sopenharmony_ci } 824cb93a386Sopenharmony_ci m.mapPoints(pts, 3); 825cb93a386Sopenharmony_ci SkAutoConicToQuads quadder; 826cb93a386Sopenharmony_ci const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance); 827cb93a386Sopenharmony_ci SkPoint lastPoint = *(quads++); 828cb93a386Sopenharmony_ci int count = quadder.countQuads(); 829cb93a386Sopenharmony_ci for (int i = 0; i < count; ++i) { 830cb93a386Sopenharmony_ci SkPoint quadPts[3]; 831cb93a386Sopenharmony_ci quadPts[0] = lastPoint; 832cb93a386Sopenharmony_ci quadPts[1] = quads[0]; 833cb93a386Sopenharmony_ci quadPts[2] = i == count - 1 ? pts[2] : quads[1]; 834cb93a386Sopenharmony_ci this->handleQuad(quadPts); 835cb93a386Sopenharmony_ci lastPoint = quadPts[2]; 836cb93a386Sopenharmony_ci quads += 2; 837cb93a386Sopenharmony_ci } 838cb93a386Sopenharmony_ci} 839cb93a386Sopenharmony_ci 840cb93a386Sopenharmony_cibool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, SkScalar offset, bool finishArc) { 841cb93a386Sopenharmony_ci // fill in fan from previous quad 842cb93a386Sopenharmony_ci SkScalar rotSin, rotCos; 843cb93a386Sopenharmony_ci int numSteps; 844cb93a386Sopenharmony_ci if (!SkComputeRadialSteps(fPrevOutset, nextNormal, offset, &rotSin, &rotCos, &numSteps)) { 845cb93a386Sopenharmony_ci // recover as best we can 846cb93a386Sopenharmony_ci numSteps = 0; 847cb93a386Sopenharmony_ci } 848cb93a386Sopenharmony_ci SkVector prevNormal = fPrevOutset; 849cb93a386Sopenharmony_ci for (int i = 0; i < numSteps-1; ++i) { 850cb93a386Sopenharmony_ci SkVector currNormal; 851cb93a386Sopenharmony_ci currNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin; 852cb93a386Sopenharmony_ci currNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin; 853cb93a386Sopenharmony_ci fPositions.push_back(fPrevPoint + currNormal); 854cb93a386Sopenharmony_ci fColors.push_back(kPenumbraColor); 855cb93a386Sopenharmony_ci this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2); 856cb93a386Sopenharmony_ci 857cb93a386Sopenharmony_ci prevNormal = currNormal; 858cb93a386Sopenharmony_ci } 859cb93a386Sopenharmony_ci if (finishArc && numSteps) { 860cb93a386Sopenharmony_ci fPositions.push_back(fPrevPoint + nextNormal); 861cb93a386Sopenharmony_ci fColors.push_back(kPenumbraColor); 862cb93a386Sopenharmony_ci this->appendTriangle(fPrevUmbraIndex, fPositions.count() - 1, fPositions.count() - 2); 863cb93a386Sopenharmony_ci } 864cb93a386Sopenharmony_ci fPrevOutset = nextNormal; 865cb93a386Sopenharmony_ci 866cb93a386Sopenharmony_ci return (numSteps > 0); 867cb93a386Sopenharmony_ci} 868cb93a386Sopenharmony_ci 869cb93a386Sopenharmony_civoid SkBaseShadowTessellator::appendTriangle(uint16_t index0, uint16_t index1, uint16_t index2) { 870cb93a386Sopenharmony_ci auto indices = fIndices.append(3); 871cb93a386Sopenharmony_ci 872cb93a386Sopenharmony_ci indices[0] = index0; 873cb93a386Sopenharmony_ci indices[1] = index1; 874cb93a386Sopenharmony_ci indices[2] = index2; 875cb93a386Sopenharmony_ci} 876cb93a386Sopenharmony_ci 877cb93a386Sopenharmony_civoid SkBaseShadowTessellator::appendQuad(uint16_t index0, uint16_t index1, 878cb93a386Sopenharmony_ci uint16_t index2, uint16_t index3) { 879cb93a386Sopenharmony_ci auto indices = fIndices.append(6); 880cb93a386Sopenharmony_ci 881cb93a386Sopenharmony_ci indices[0] = index0; 882cb93a386Sopenharmony_ci indices[1] = index1; 883cb93a386Sopenharmony_ci indices[2] = index2; 884cb93a386Sopenharmony_ci 885cb93a386Sopenharmony_ci indices[3] = index2; 886cb93a386Sopenharmony_ci indices[4] = index1; 887cb93a386Sopenharmony_ci indices[5] = index3; 888cb93a386Sopenharmony_ci} 889cb93a386Sopenharmony_ci 890cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////////////////////////// 891cb93a386Sopenharmony_ci 892cb93a386Sopenharmony_ciclass SkAmbientShadowTessellator : public SkBaseShadowTessellator { 893cb93a386Sopenharmony_cipublic: 894cb93a386Sopenharmony_ci SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm, 895cb93a386Sopenharmony_ci const SkPoint3& zPlaneParams, bool transparent); 896cb93a386Sopenharmony_ci 897cb93a386Sopenharmony_ciprivate: 898cb93a386Sopenharmony_ci bool computePathPolygon(const SkPath& path, const SkMatrix& ctm); 899cb93a386Sopenharmony_ci 900cb93a386Sopenharmony_ci using INHERITED = SkBaseShadowTessellator; 901cb93a386Sopenharmony_ci}; 902cb93a386Sopenharmony_ci 903cb93a386Sopenharmony_ciSkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path, 904cb93a386Sopenharmony_ci const SkMatrix& ctm, 905cb93a386Sopenharmony_ci const SkPoint3& zPlaneParams, 906cb93a386Sopenharmony_ci bool transparent) 907cb93a386Sopenharmony_ci : INHERITED(zPlaneParams, path.getBounds(), transparent) { 908cb93a386Sopenharmony_ci // Set base colors 909cb93a386Sopenharmony_ci auto baseZ = heightFunc(fPathBounds.centerX(), fPathBounds.centerY()); 910cb93a386Sopenharmony_ci // umbraColor is the interior value, penumbraColor the exterior value. 911cb93a386Sopenharmony_ci auto outset = SkDrawShadowMetrics::AmbientBlurRadius(baseZ); 912cb93a386Sopenharmony_ci auto inset = outset * SkDrawShadowMetrics::AmbientRecipAlpha(baseZ) - outset; 913cb93a386Sopenharmony_ci inset = SkTPin(inset, 0.0f, std::min(path.getBounds().width(), 914cb93a386Sopenharmony_ci path.getBounds().height())); 915cb93a386Sopenharmony_ci 916cb93a386Sopenharmony_ci if (!this->computePathPolygon(path, ctm)) { 917cb93a386Sopenharmony_ci return; 918cb93a386Sopenharmony_ci } 919cb93a386Sopenharmony_ci if (fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) { 920cb93a386Sopenharmony_ci fSucceeded = true; // We don't want to try to blur these cases, so we will 921cb93a386Sopenharmony_ci // return an empty SkVertices instead. 922cb93a386Sopenharmony_ci return; 923cb93a386Sopenharmony_ci } 924cb93a386Sopenharmony_ci 925cb93a386Sopenharmony_ci // Outer ring: 3*numPts 926cb93a386Sopenharmony_ci // Middle ring: numPts 927cb93a386Sopenharmony_ci fPositions.setReserve(4 * path.countPoints()); 928cb93a386Sopenharmony_ci fColors.setReserve(4 * path.countPoints()); 929cb93a386Sopenharmony_ci // Outer ring: 12*numPts 930cb93a386Sopenharmony_ci // Middle ring: 0 931cb93a386Sopenharmony_ci fIndices.setReserve(12 * path.countPoints()); 932cb93a386Sopenharmony_ci 933cb93a386Sopenharmony_ci if (fIsConvex) { 934cb93a386Sopenharmony_ci fSucceeded = this->computeConvexShadow(inset, outset, false); 935cb93a386Sopenharmony_ci } else { 936cb93a386Sopenharmony_ci fSucceeded = this->computeConcaveShadow(inset, outset); 937cb93a386Sopenharmony_ci } 938cb93a386Sopenharmony_ci} 939cb93a386Sopenharmony_ci 940cb93a386Sopenharmony_cibool SkAmbientShadowTessellator::computePathPolygon(const SkPath& path, const SkMatrix& ctm) { 941cb93a386Sopenharmony_ci fPathPolygon.setReserve(path.countPoints()); 942cb93a386Sopenharmony_ci 943cb93a386Sopenharmony_ci // walk around the path, tessellate and generate outer ring 944cb93a386Sopenharmony_ci // if original path is transparent, will accumulate sum of points for centroid 945cb93a386Sopenharmony_ci SkPath::Iter iter(path, true); 946cb93a386Sopenharmony_ci SkPoint pts[4]; 947cb93a386Sopenharmony_ci SkPath::Verb verb; 948cb93a386Sopenharmony_ci bool verbSeen = false; 949cb93a386Sopenharmony_ci bool closeSeen = false; 950cb93a386Sopenharmony_ci while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { 951cb93a386Sopenharmony_ci if (closeSeen) { 952cb93a386Sopenharmony_ci return false; 953cb93a386Sopenharmony_ci } 954cb93a386Sopenharmony_ci switch (verb) { 955cb93a386Sopenharmony_ci case SkPath::kLine_Verb: 956cb93a386Sopenharmony_ci this->handleLine(ctm, &pts[1]); 957cb93a386Sopenharmony_ci break; 958cb93a386Sopenharmony_ci case SkPath::kQuad_Verb: 959cb93a386Sopenharmony_ci this->handleQuad(ctm, pts); 960cb93a386Sopenharmony_ci break; 961cb93a386Sopenharmony_ci case SkPath::kCubic_Verb: 962cb93a386Sopenharmony_ci this->handleCubic(ctm, pts); 963cb93a386Sopenharmony_ci break; 964cb93a386Sopenharmony_ci case SkPath::kConic_Verb: 965cb93a386Sopenharmony_ci this->handleConic(ctm, pts, iter.conicWeight()); 966cb93a386Sopenharmony_ci break; 967cb93a386Sopenharmony_ci case SkPath::kMove_Verb: 968cb93a386Sopenharmony_ci if (verbSeen) { 969cb93a386Sopenharmony_ci return false; 970cb93a386Sopenharmony_ci } 971cb93a386Sopenharmony_ci break; 972cb93a386Sopenharmony_ci case SkPath::kClose_Verb: 973cb93a386Sopenharmony_ci case SkPath::kDone_Verb: 974cb93a386Sopenharmony_ci closeSeen = true; 975cb93a386Sopenharmony_ci break; 976cb93a386Sopenharmony_ci } 977cb93a386Sopenharmony_ci verbSeen = true; 978cb93a386Sopenharmony_ci } 979cb93a386Sopenharmony_ci 980cb93a386Sopenharmony_ci this->finishPathPolygon(); 981cb93a386Sopenharmony_ci return true; 982cb93a386Sopenharmony_ci} 983cb93a386Sopenharmony_ci 984cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////////////////////////// 985cb93a386Sopenharmony_ci 986cb93a386Sopenharmony_ciclass SkSpotShadowTessellator : public SkBaseShadowTessellator { 987cb93a386Sopenharmony_cipublic: 988cb93a386Sopenharmony_ci SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm, 989cb93a386Sopenharmony_ci const SkPoint3& zPlaneParams, const SkPoint3& lightPos, 990cb93a386Sopenharmony_ci SkScalar lightRadius, bool transparent, bool directional, bool isLimitElevation = false); 991cb93a386Sopenharmony_ci 992cb93a386Sopenharmony_ciprivate: 993cb93a386Sopenharmony_ci bool computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm, 994cb93a386Sopenharmony_ci const SkMatrix& shadowTransform); 995cb93a386Sopenharmony_ci void addToClip(const SkVector& nextPoint); 996cb93a386Sopenharmony_ci 997cb93a386Sopenharmony_ci using INHERITED = SkBaseShadowTessellator; 998cb93a386Sopenharmony_ci}; 999cb93a386Sopenharmony_ci 1000cb93a386Sopenharmony_ciSkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm, 1001cb93a386Sopenharmony_ci const SkPoint3& zPlaneParams, 1002cb93a386Sopenharmony_ci const SkPoint3& lightPos, SkScalar lightRadius, 1003cb93a386Sopenharmony_ci bool transparent, bool directional, bool isLimitElevation) 1004cb93a386Sopenharmony_ci : INHERITED(zPlaneParams, path.getBounds(), transparent) { 1005cb93a386Sopenharmony_ci 1006cb93a386Sopenharmony_ci // Compute the blur radius, scale and translation for the spot shadow. 1007cb93a386Sopenharmony_ci SkMatrix shadowTransform; 1008cb93a386Sopenharmony_ci SkScalar outset; 1009cb93a386Sopenharmony_ci if (!SkDrawShadowMetrics::GetSpotShadowTransform(lightPos, lightRadius, ctm, zPlaneParams, 1010cb93a386Sopenharmony_ci path.getBounds(), directional, 1011cb93a386Sopenharmony_ci &shadowTransform, &outset, isLimitElevation)) { 1012cb93a386Sopenharmony_ci return; 1013cb93a386Sopenharmony_ci } 1014cb93a386Sopenharmony_ci SkScalar inset = outset; 1015cb93a386Sopenharmony_ci 1016cb93a386Sopenharmony_ci // compute rough clip bounds for umbra, plus offset polygon, plus centroid 1017cb93a386Sopenharmony_ci if (!this->computeClipAndPathPolygons(path, ctm, shadowTransform)) { 1018cb93a386Sopenharmony_ci return; 1019cb93a386Sopenharmony_ci } 1020cb93a386Sopenharmony_ci if (fClipPolygon.count() < 3 || fPathPolygon.count() < 3 || !SkScalarIsFinite(fArea)) { 1021cb93a386Sopenharmony_ci fSucceeded = true; // We don't want to try to blur these cases, so we will 1022cb93a386Sopenharmony_ci // return an empty SkVertices instead. 1023cb93a386Sopenharmony_ci return; 1024cb93a386Sopenharmony_ci } 1025cb93a386Sopenharmony_ci 1026cb93a386Sopenharmony_ci // TODO: calculate these reserves better 1027cb93a386Sopenharmony_ci // Penumbra ring: 3*numPts 1028cb93a386Sopenharmony_ci // Umbra ring: numPts 1029cb93a386Sopenharmony_ci // Inner ring: numPts 1030cb93a386Sopenharmony_ci fPositions.setReserve(5 * path.countPoints()); 1031cb93a386Sopenharmony_ci fColors.setReserve(5 * path.countPoints()); 1032cb93a386Sopenharmony_ci // Penumbra ring: 12*numPts 1033cb93a386Sopenharmony_ci // Umbra ring: 3*numPts 1034cb93a386Sopenharmony_ci fIndices.setReserve(15 * path.countPoints()); 1035cb93a386Sopenharmony_ci 1036cb93a386Sopenharmony_ci if (fIsConvex) { 1037cb93a386Sopenharmony_ci fSucceeded = this->computeConvexShadow(inset, outset, true); 1038cb93a386Sopenharmony_ci } else { 1039cb93a386Sopenharmony_ci fSucceeded = this->computeConcaveShadow(inset, outset); 1040cb93a386Sopenharmony_ci } 1041cb93a386Sopenharmony_ci 1042cb93a386Sopenharmony_ci if (!fSucceeded) { 1043cb93a386Sopenharmony_ci return; 1044cb93a386Sopenharmony_ci } 1045cb93a386Sopenharmony_ci 1046cb93a386Sopenharmony_ci fSucceeded = true; 1047cb93a386Sopenharmony_ci} 1048cb93a386Sopenharmony_ci 1049cb93a386Sopenharmony_cibool SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, const SkMatrix& ctm, 1050cb93a386Sopenharmony_ci const SkMatrix& shadowTransform) { 1051cb93a386Sopenharmony_ci 1052cb93a386Sopenharmony_ci fPathPolygon.setReserve(path.countPoints()); 1053cb93a386Sopenharmony_ci fClipPolygon.setReserve(path.countPoints()); 1054cb93a386Sopenharmony_ci 1055cb93a386Sopenharmony_ci // Walk around the path and compute clip polygon and path polygon. 1056cb93a386Sopenharmony_ci // Will also accumulate sum of areas for centroid. 1057cb93a386Sopenharmony_ci // For Bezier curves, we compute additional interior points on curve. 1058cb93a386Sopenharmony_ci SkPath::Iter iter(path, true); 1059cb93a386Sopenharmony_ci SkPoint pts[4]; 1060cb93a386Sopenharmony_ci SkPoint clipPts[4]; 1061cb93a386Sopenharmony_ci SkPath::Verb verb; 1062cb93a386Sopenharmony_ci 1063cb93a386Sopenharmony_ci // coefficients to compute cubic Bezier at t = 5/16 1064cb93a386Sopenharmony_ci static constexpr SkScalar kA = 0.32495117187f; 1065cb93a386Sopenharmony_ci static constexpr SkScalar kB = 0.44311523437f; 1066cb93a386Sopenharmony_ci static constexpr SkScalar kC = 0.20141601562f; 1067cb93a386Sopenharmony_ci static constexpr SkScalar kD = 0.03051757812f; 1068cb93a386Sopenharmony_ci 1069cb93a386Sopenharmony_ci SkPoint curvePoint; 1070cb93a386Sopenharmony_ci SkScalar w; 1071cb93a386Sopenharmony_ci bool closeSeen = false; 1072cb93a386Sopenharmony_ci bool verbSeen = false; 1073cb93a386Sopenharmony_ci while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { 1074cb93a386Sopenharmony_ci if (closeSeen) { 1075cb93a386Sopenharmony_ci return false; 1076cb93a386Sopenharmony_ci } 1077cb93a386Sopenharmony_ci switch (verb) { 1078cb93a386Sopenharmony_ci case SkPath::kLine_Verb: 1079cb93a386Sopenharmony_ci ctm.mapPoints(clipPts, &pts[1], 1); 1080cb93a386Sopenharmony_ci this->addToClip(clipPts[0]); 1081cb93a386Sopenharmony_ci this->handleLine(shadowTransform, &pts[1]); 1082cb93a386Sopenharmony_ci break; 1083cb93a386Sopenharmony_ci case SkPath::kQuad_Verb: 1084cb93a386Sopenharmony_ci ctm.mapPoints(clipPts, pts, 3); 1085cb93a386Sopenharmony_ci // point at t = 1/2 1086cb93a386Sopenharmony_ci curvePoint.fX = 0.25f*clipPts[0].fX + 0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX; 1087cb93a386Sopenharmony_ci curvePoint.fY = 0.25f*clipPts[0].fY + 0.5f*clipPts[1].fY + 0.25f*clipPts[2].fY; 1088cb93a386Sopenharmony_ci this->addToClip(curvePoint); 1089cb93a386Sopenharmony_ci this->addToClip(clipPts[2]); 1090cb93a386Sopenharmony_ci this->handleQuad(shadowTransform, pts); 1091cb93a386Sopenharmony_ci break; 1092cb93a386Sopenharmony_ci case SkPath::kConic_Verb: 1093cb93a386Sopenharmony_ci ctm.mapPoints(clipPts, pts, 3); 1094cb93a386Sopenharmony_ci w = iter.conicWeight(); 1095cb93a386Sopenharmony_ci // point at t = 1/2 1096cb93a386Sopenharmony_ci curvePoint.fX = 0.25f*clipPts[0].fX + w*0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX; 1097cb93a386Sopenharmony_ci curvePoint.fY = 0.25f*clipPts[0].fY + w*0.5f*clipPts[1].fY + 0.25f*clipPts[2].fY; 1098cb93a386Sopenharmony_ci curvePoint *= SkScalarInvert(0.5f + 0.5f*w); 1099cb93a386Sopenharmony_ci this->addToClip(curvePoint); 1100cb93a386Sopenharmony_ci this->addToClip(clipPts[2]); 1101cb93a386Sopenharmony_ci this->handleConic(shadowTransform, pts, w); 1102cb93a386Sopenharmony_ci break; 1103cb93a386Sopenharmony_ci case SkPath::kCubic_Verb: 1104cb93a386Sopenharmony_ci ctm.mapPoints(clipPts, pts, 4); 1105cb93a386Sopenharmony_ci // point at t = 5/16 1106cb93a386Sopenharmony_ci curvePoint.fX = kA*clipPts[0].fX + kB*clipPts[1].fX 1107cb93a386Sopenharmony_ci + kC*clipPts[2].fX + kD*clipPts[3].fX; 1108cb93a386Sopenharmony_ci curvePoint.fY = kA*clipPts[0].fY + kB*clipPts[1].fY 1109cb93a386Sopenharmony_ci + kC*clipPts[2].fY + kD*clipPts[3].fY; 1110cb93a386Sopenharmony_ci this->addToClip(curvePoint); 1111cb93a386Sopenharmony_ci // point at t = 11/16 1112cb93a386Sopenharmony_ci curvePoint.fX = kD*clipPts[0].fX + kC*clipPts[1].fX 1113cb93a386Sopenharmony_ci + kB*clipPts[2].fX + kA*clipPts[3].fX; 1114cb93a386Sopenharmony_ci curvePoint.fY = kD*clipPts[0].fY + kC*clipPts[1].fY 1115cb93a386Sopenharmony_ci + kB*clipPts[2].fY + kA*clipPts[3].fY; 1116cb93a386Sopenharmony_ci this->addToClip(curvePoint); 1117cb93a386Sopenharmony_ci this->addToClip(clipPts[3]); 1118cb93a386Sopenharmony_ci this->handleCubic(shadowTransform, pts); 1119cb93a386Sopenharmony_ci break; 1120cb93a386Sopenharmony_ci case SkPath::kMove_Verb: 1121cb93a386Sopenharmony_ci if (verbSeen) { 1122cb93a386Sopenharmony_ci return false; 1123cb93a386Sopenharmony_ci } 1124cb93a386Sopenharmony_ci break; 1125cb93a386Sopenharmony_ci case SkPath::kClose_Verb: 1126cb93a386Sopenharmony_ci case SkPath::kDone_Verb: 1127cb93a386Sopenharmony_ci closeSeen = true; 1128cb93a386Sopenharmony_ci break; 1129cb93a386Sopenharmony_ci default: 1130cb93a386Sopenharmony_ci SkDEBUGFAIL("unknown verb"); 1131cb93a386Sopenharmony_ci } 1132cb93a386Sopenharmony_ci verbSeen = true; 1133cb93a386Sopenharmony_ci } 1134cb93a386Sopenharmony_ci 1135cb93a386Sopenharmony_ci this->finishPathPolygon(); 1136cb93a386Sopenharmony_ci return true; 1137cb93a386Sopenharmony_ci} 1138cb93a386Sopenharmony_ci 1139cb93a386Sopenharmony_civoid SkSpotShadowTessellator::addToClip(const SkPoint& point) { 1140cb93a386Sopenharmony_ci if (fClipPolygon.isEmpty() || !duplicate_pt(point, fClipPolygon[fClipPolygon.count() - 1])) { 1141cb93a386Sopenharmony_ci fClipPolygon.push_back(point); 1142cb93a386Sopenharmony_ci } 1143cb93a386Sopenharmony_ci} 1144cb93a386Sopenharmony_ci 1145cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////////////////////////// 1146cb93a386Sopenharmony_ci 1147cb93a386Sopenharmony_cisk_sp<SkVertices> SkShadowTessellator::MakeAmbient(const SkPath& path, const SkMatrix& ctm, 1148cb93a386Sopenharmony_ci const SkPoint3& zPlane, bool transparent) { 1149cb93a386Sopenharmony_ci if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite()) { 1150cb93a386Sopenharmony_ci return nullptr; 1151cb93a386Sopenharmony_ci } 1152cb93a386Sopenharmony_ci SkAmbientShadowTessellator ambientTess(path, ctm, zPlane, transparent); 1153cb93a386Sopenharmony_ci return ambientTess.releaseVertices(); 1154cb93a386Sopenharmony_ci} 1155cb93a386Sopenharmony_ci 1156cb93a386Sopenharmony_cisk_sp<SkVertices> SkShadowTessellator::MakeSpot(const SkPath& path, const SkMatrix& ctm, 1157cb93a386Sopenharmony_ci const SkPoint3& zPlane, const SkPoint3& lightPos, 1158cb93a386Sopenharmony_ci SkScalar lightRadius, bool transparent, 1159cb93a386Sopenharmony_ci bool directional, bool isLimitElevation) { 1160cb93a386Sopenharmony_ci if (!ctm.mapRect(path.getBounds()).isFinite() || !zPlane.isFinite() || 1161cb93a386Sopenharmony_ci !lightPos.isFinite() || !(lightPos.fZ >= SK_ScalarNearlyZero) || 1162cb93a386Sopenharmony_ci !SkScalarIsFinite(lightRadius) || !(lightRadius >= SK_ScalarNearlyZero)) { 1163cb93a386Sopenharmony_ci return nullptr; 1164cb93a386Sopenharmony_ci } 1165cb93a386Sopenharmony_ci SkSpotShadowTessellator spotTess(path, ctm, zPlane, lightPos, lightRadius, transparent, 1166cb93a386Sopenharmony_ci directional, isLimitElevation); 1167cb93a386Sopenharmony_ci return spotTess.releaseVertices(); 1168cb93a386Sopenharmony_ci} 1169