Flutter Engine
The Flutter Engine
SkDrawShadowInfo.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9
11#include "include/core/SkPath.h"
12#include "include/core/SkRect.h"
15
17
19 return x*params.fX + y*params.fY + params.fZ;
20}
21
22bool GetSpotShadowTransform(const SkPoint3& lightPos, SkScalar lightRadius,
23 const SkMatrix& ctm, const SkPoint3& zPlaneParams,
24 const SkRect& pathBounds, bool directional,
25 SkMatrix* shadowTransform, SkScalar* radius) {
26 auto heightFunc = [zPlaneParams] (SkScalar x, SkScalar y) {
27 return zPlaneParams.fX*x + zPlaneParams.fY*y + zPlaneParams.fZ;
28 };
29 SkScalar occluderHeight = heightFunc(pathBounds.centerX(), pathBounds.centerY());
30
31 // TODO: have directional lights support tilt via the zPlaneParams
32 if (!ctm.hasPerspective() || directional) {
34 SkVector translate;
35 if (directional) {
36 SkDrawShadowMetrics::GetDirectionalParams(occluderHeight, lightPos.fX, lightPos.fY,
37 lightPos.fZ, lightRadius, radius,
38 &scale, &translate);
39 } else {
40 SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY,
41 lightPos.fZ, lightRadius, radius,
42 &scale, &translate);
43 }
44 shadowTransform->setScaleTranslate(scale, scale, translate.fX, translate.fY);
45 shadowTransform->preConcat(ctm);
46 } else {
47 if (SkScalarNearlyZero(pathBounds.width()) || SkScalarNearlyZero(pathBounds.height())) {
48 return false;
49 }
50
51 // get rotated quad in 3D
52 SkPoint pts[4];
53 ctm.mapRectToQuad(pts, pathBounds);
54
55 SkPoint3 pts3D[4];
56 SkScalar z = heightFunc(pathBounds.fLeft, pathBounds.fTop);
57 pts3D[0].set(pts[0].fX, pts[0].fY, z);
58 z = heightFunc(pathBounds.fRight, pathBounds.fTop);
59 pts3D[1].set(pts[1].fX, pts[1].fY, z);
60 z = heightFunc(pathBounds.fRight, pathBounds.fBottom);
61 pts3D[2].set(pts[2].fX, pts[2].fY, z);
62 z = heightFunc(pathBounds.fLeft, pathBounds.fBottom);
63 pts3D[3].set(pts[3].fX, pts[3].fY, z);
64
65 // project from light through corners to z=0 plane
66 for (int i = 0; i < 4; ++i) {
67 SkScalar dz = lightPos.fZ - pts3D[i].fZ;
68 // light shouldn't be below or at a corner's z-location
69 if (dz <= SK_ScalarNearlyZero) {
70 return false;
71 }
72 SkScalar zRatio = pts3D[i].fZ / dz;
73 pts3D[i].fX -= (lightPos.fX - pts3D[i].fX)*zRatio;
74 pts3D[i].fY -= (lightPos.fY - pts3D[i].fY)*zRatio;
75 pts3D[i].fZ = SK_Scalar1;
76 }
77
78 // Generate matrix that projects from [-1,1]x[-1,1] square to projected quad
79 SkPoint3 h0, h1, h2;
80 // Compute homogenous crossing point between top and bottom edges (gives new x-axis).
81 h0 = (pts3D[1].cross(pts3D[0])).cross(pts3D[2].cross(pts3D[3]));
82 // Compute homogenous crossing point between left and right edges (gives new y-axis).
83 h1 = (pts3D[0].cross(pts3D[3])).cross(pts3D[1].cross(pts3D[2]));
84 // Compute homogenous crossing point between diagonals (gives new origin).
85 h2 = (pts3D[0].cross(pts3D[2])).cross(pts3D[1].cross(pts3D[3]));
86 // If h2 is a vector (z=0 in 2D homogeneous space), that means that at least
87 // two of the quad corners are coincident and we don't have a realistic projection
88 if (SkScalarNearlyZero(h2.fZ)) {
89 return false;
90 }
91 // In some cases the crossing points are in the wrong direction
92 // to map (-1,-1) to pts3D[0], so we need to correct for that.
93 // Want h0 to be to the right of the left edge.
94 SkVector3 v = pts3D[3] - pts3D[0];
95 SkVector3 w = h0 - pts3D[0];
96 SkScalar perpDot = v.fX*w.fY - v.fY*w.fX;
97 if (perpDot > 0) {
98 h0 = -h0;
99 }
100 // Want h1 to be above the bottom edge.
101 v = pts3D[1] - pts3D[0];
102 perpDot = v.fX*w.fY - v.fY*w.fX;
103 if (perpDot < 0) {
104 h1 = -h1;
105 }
106 shadowTransform->setAll(h0.fX / h2.fZ, h1.fX / h2.fZ, h2.fX / h2.fZ,
107 h0.fY / h2.fZ, h1.fY / h2.fZ, h2.fY / h2.fZ,
108 h0.fZ / h2.fZ, h1.fZ / h2.fZ, 1);
109 // generate matrix that transforms from bounds to [-1,1]x[-1,1] square
110 SkMatrix toHomogeneous;
111 SkScalar xScale = 2/(pathBounds.fRight - pathBounds.fLeft);
112 SkScalar yScale = 2/(pathBounds.fBottom - pathBounds.fTop);
113 toHomogeneous.setAll(xScale, 0, -xScale*pathBounds.fLeft - 1,
114 0, yScale, -yScale*pathBounds.fTop - 1,
115 0, 0, 1);
116 shadowTransform->preConcat(toHomogeneous);
117
118 *radius = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius);
119 }
120
121 return true;
122}
123
124void GetLocalBounds(const SkPath& path, const SkDrawShadowRec& rec, const SkMatrix& ctm,
125 SkRect* bounds) {
126 SkRect ambientBounds = path.getBounds();
127 SkScalar occluderZ;
129 occluderZ = rec.fZPlaneParams.fZ;
130 } else {
131 occluderZ = compute_z(ambientBounds.fLeft, ambientBounds.fTop, rec.fZPlaneParams);
132 occluderZ = std::max(occluderZ, compute_z(ambientBounds.fRight, ambientBounds.fTop,
133 rec.fZPlaneParams));
134 occluderZ = std::max(occluderZ, compute_z(ambientBounds.fLeft, ambientBounds.fBottom,
135 rec.fZPlaneParams));
136 occluderZ = std::max(occluderZ, compute_z(ambientBounds.fRight, ambientBounds.fBottom,
137 rec.fZPlaneParams));
138 }
139 SkScalar ambientBlur;
140 SkScalar spotBlur;
141 SkScalar spotScale;
142 SkPoint spotOffset;
143 if (ctm.hasPerspective()) {
144 // transform ambient and spot bounds into device space
145 ctm.mapRect(&ambientBounds);
146
147 // get ambient blur (in device space)
148 ambientBlur = SkDrawShadowMetrics::AmbientBlurRadius(occluderZ);
149
150 // get spot params (in device space)
153 rec.fLightPos.fZ, rec.fLightRadius,
154 &spotBlur, &spotScale, &spotOffset);
155 } else {
156 SkPoint devLightPos = SkPoint::Make(rec.fLightPos.fX, rec.fLightPos.fY);
157 ctm.mapPoints(&devLightPos, 1);
158 SkDrawShadowMetrics::GetSpotParams(occluderZ, devLightPos.fX, devLightPos.fY,
159 rec.fLightPos.fZ, rec.fLightRadius,
160 &spotBlur, &spotScale, &spotOffset);
161 }
162 } else {
163 SkScalar devToSrcScale = SkScalarInvert(ctm.getMinScale());
164
165 // get ambient blur (in local space)
166 SkScalar devSpaceAmbientBlur = SkDrawShadowMetrics::AmbientBlurRadius(occluderZ);
167 ambientBlur = devSpaceAmbientBlur*devToSrcScale;
168
169 // get spot params (in local space)
172 rec.fLightPos.fZ, rec.fLightRadius,
173 &spotBlur, &spotScale, &spotOffset);
174 // light dir is in device space, so need to map spot offset back into local space
175 SkMatrix inverse;
176 if (ctm.invert(&inverse)) {
177 inverse.mapVectors(&spotOffset, 1);
178 }
179 } else {
181 rec.fLightPos.fZ, rec.fLightRadius,
182 &spotBlur, &spotScale, &spotOffset);
183 }
184
185 // convert spot blur to local space
186 spotBlur *= devToSrcScale;
187 }
188
189 // in both cases, adjust ambient and spot bounds
190 SkRect spotBounds = ambientBounds;
191 ambientBounds.outset(ambientBlur, ambientBlur);
192 spotBounds.fLeft *= spotScale;
193 spotBounds.fTop *= spotScale;
194 spotBounds.fRight *= spotScale;
195 spotBounds.fBottom *= spotScale;
196 spotBounds.offset(spotOffset.fX, spotOffset.fY);
197 spotBounds.outset(spotBlur, spotBlur);
198
199 // merge bounds
200 *bounds = ambientBounds;
201 bounds->join(spotBounds);
202 // outset a bit to account for floating point error
203 bounds->outset(1, 1);
204
205 // if perspective, transform back to src space
206 if (ctm.hasPerspective()) {
207 // TODO: create tighter mapping from dev rect back to src rect
208 SkMatrix inverse;
209 if (ctm.invert(&inverse)) {
210 inverse.mapRect(bounds);
211 }
212 }
213}
214
215
216} // namespace SkDrawShadowMetrics
217
#define SkScalarInvert(x)
Definition: SkScalar.h:73
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:101
#define SK_Scalar1
Definition: SkScalar.h:18
#define SK_ScalarNearlyZero
Definition: SkScalar.h:99
@ kDirectionalLight_ShadowFlag
Definition: SkShadowUtils.h:31
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
void mapVectors(SkVector dst[], const SkVector src[], int count) const
Definition: SkMatrix.cpp:1097
SkMatrix & setAll(SkScalar scaleX, SkScalar skewX, SkScalar transX, SkScalar skewY, SkScalar scaleY, SkScalar transY, SkScalar persp0, SkScalar persp1, SkScalar persp2)
Definition: SkMatrix.h:562
void setScaleTranslate(SkScalar sx, SkScalar sy, SkScalar tx, SkScalar ty)
Definition: SkMatrix.h:1803
void mapPoints(SkPoint dst[], const SkPoint src[], int count) const
Definition: SkMatrix.cpp:770
bool invert(SkMatrix *inverse) const
Definition: SkMatrix.h:1206
SkScalar getMinScale() const
Definition: SkMatrix.cpp:1522
SkMatrix & preConcat(const SkMatrix &other)
Definition: SkMatrix.cpp:674
bool hasPerspective() const
Definition: SkMatrix.h:312
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
void mapRectToQuad(SkPoint dst[4], const SkRect &rect) const
Definition: SkMatrix.h:1620
Definition: SkPath.h:59
const EmbeddedViewParams * params
float SkScalar
Definition: extension.cpp:12
static float max(float r, float g, float b)
Definition: hsl.cpp:49
double y
double x
bool GetSpotShadowTransform(const SkPoint3 &lightPos, SkScalar lightRadius, const SkMatrix &ctm, const SkPoint3 &zPlaneParams, const SkRect &pathBounds, bool directional, SkMatrix *shadowTransform, SkScalar *radius)
void GetLocalBounds(const SkPath &path, const SkDrawShadowRec &rec, const SkMatrix &ctm, SkRect *bounds)
static SkScalar compute_z(SkScalar x, SkScalar y, const SkPoint3 &params)
void GetDirectionalParams(SkScalar occluderZ, SkScalar lightX, SkScalar lightY, SkScalar lightZ, SkScalar lightRadius, SkScalar *blurRadius, SkScalar *scale, SkVector *translate)
SkScalar AmbientBlurRadius(SkScalar height)
void GetSpotParams(SkScalar occluderZ, SkScalar lightX, SkScalar lightY, SkScalar lightZ, SkScalar lightRadius, SkScalar *blurRadius, SkScalar *scale, SkVector *translate)
SkScalar SpotBlurRadius(SkScalar occluderZ, SkScalar lightZ, SkScalar lightRadius)
Optional< SkRect > bounds
Definition: SkRecords.h:189
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
int64_t cross(Point d0, Point d1)
Definition: Myers.cpp:55
SkScalar w
const Scalar scale
SkPoint3 fZPlaneParams
SkPoint3 cross(const SkPoint3 &vec) const
Definition: SkPoint3.h:141
SkScalar fX
Definition: SkPoint3.h:16
SkScalar fZ
Definition: SkPoint3.h:16
void set(SkScalar x, SkScalar y, SkScalar z)
Definition: SkPoint3.h:28
SkScalar fY
Definition: SkPoint3.h:16
float fX
x-axis value
Definition: SkPoint_impl.h:164
static constexpr SkPoint Make(float x, float y)
Definition: SkPoint_impl.h:173
float fY
y-axis value
Definition: SkPoint_impl.h:165
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
void outset(float dx, float dy)
Definition: SkRect.h:1077
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
constexpr float centerX() const
Definition: SkRect.h:776
void offset(float dx, float dy)
Definition: SkRect.h:1016
constexpr float height() const
Definition: SkRect.h:769
constexpr float centerY() const
Definition: SkRect.h:785
constexpr float width() const
Definition: SkRect.h:762
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15