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