Skip to content

Commit 334fa80

Browse files
authored
[flow] Switch to directional shadows (#27124)
1 parent 14a9e9a commit 334fa80

File tree

5 files changed

+121
-80
lines changed

5 files changed

+121
-80
lines changed

flow/display_list_utils.cc

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -340,22 +340,8 @@ void DisplayListBoundsCalculator::drawShadow(const SkPath& path,
340340
const SkScalar elevation,
341341
bool occludes,
342342
SkScalar dpr) {
343-
// Constants from physical_shape_layer.cc
344-
const SkScalar kLightHeight = 600;
345-
const SkScalar kLightRadius = 800;
346-
347-
SkShadowFlags flags = occludes
348-
? SkShadowFlags::kTransparentOccluder_ShadowFlag
349-
: SkShadowFlags::kNone_ShadowFlag;
350-
const SkRect& bounds = path.getBounds();
351-
SkScalar shadow_x = (bounds.left() + bounds.right()) / 2;
352-
SkScalar shadow_y = bounds.top() - 600.0f;
353-
SkRect shadow_bounds;
354-
SkShadowUtils::GetLocalBounds(
355-
matrix(), path, SkPoint3::Make(0, 0, dpr * elevation),
356-
SkPoint3::Make(shadow_x, shadow_y, dpr * kLightHeight),
357-
dpr * kLightRadius, flags, &shadow_bounds);
358-
accumulateRect(shadow_bounds);
343+
accumulateRect(
344+
PhysicalShapeLayer::ComputeShadowBounds(path, elevation, dpr, matrix()));
359345
}
360346

361347
void DisplayListBoundsCalculator::accumulateRect(const SkRect& rect,

flow/layers/checkerboard_layertree_unittests.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,8 @@ TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerNotCheckBoard) {
294294
// The Fuchsia system compositor handles all elevated PhysicalShapeLayers and
295295
// their shadows , so we do not do any painting there.
296296
EXPECT_EQ(layer->paint_bounds(),
297-
PhysicalShapeLayer::ComputeShadowBounds(layer_path.getBounds(),
298-
initial_elevation, 1.0f));
297+
PhysicalShapeLayer::ComputeShadowBounds(
298+
layer_path, initial_elevation, 1.0f, SkMatrix()));
299299
EXPECT_TRUE(layer->needs_painting(paint_context()));
300300
EXPECT_EQ(layer->elevation(), initial_elevation);
301301

@@ -333,8 +333,8 @@ TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerCheckBoard) {
333333
// The Fuchsia system compositor handles all elevated PhysicalShapeLayers and
334334
// their shadows , so we do not do any painting there.
335335
EXPECT_EQ(layer->paint_bounds(),
336-
PhysicalShapeLayer::ComputeShadowBounds(layer_path.getBounds(),
337-
initial_elevation, 1.0f));
336+
PhysicalShapeLayer::ComputeShadowBounds(
337+
layer_path, initial_elevation, 1.0f, SkMatrix()));
338338
EXPECT_TRUE(layer->needs_painting(paint_context()));
339339
EXPECT_EQ(layer->elevation(), initial_elevation);
340340

flow/layers/physical_shape_layer.cc

Lines changed: 21 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ void PhysicalShapeLayer::Diff(DiffContext* context, const Layer* old_layer) {
4141
if (elevation_ == 0) {
4242
bounds = path_.getBounds();
4343
} else {
44-
bounds = ComputeShadowBounds(path_.getBounds(), elevation_,
45-
context->frame_device_pixel_ratio());
44+
bounds = ComputeShadowBounds(path_, elevation_,
45+
context->frame_device_pixel_ratio(),
46+
context->GetTransform());
4647
}
4748

4849
context->AddLayerBounds(bounds);
@@ -70,8 +71,8 @@ void PhysicalShapeLayer::Preroll(PrerollContext* context,
7071
// We will draw the shadow in Paint(), so add some margin to the paint
7172
// bounds to leave space for the shadow. We fill this whole region and clip
7273
// children to it so we don't need to join the child paint bounds.
73-
set_paint_bounds(ComputeShadowBounds(path_.getBounds(), elevation_,
74-
context->frame_device_pixel_ratio));
74+
set_paint_bounds(ComputeShadowBounds(
75+
path_, elevation_, context->frame_device_pixel_ratio, matrix));
7576
}
7677
}
7778

@@ -127,47 +128,15 @@ void PhysicalShapeLayer::Paint(PaintContext& context) const {
127128
}
128129
}
129130

130-
SkRect PhysicalShapeLayer::ComputeShadowBounds(const SkRect& bounds,
131+
SkRect PhysicalShapeLayer::ComputeShadowBounds(const SkPath& path,
131132
float elevation,
132-
float pixel_ratio) {
133-
// The shadow offset is calculated as follows:
134-
// .--- (kLightRadius)
135-
// -------/ (light)
136-
// | /
137-
// | /
138-
// |/
139-
// |O
140-
// /| (kLightHeight)
141-
// / |
142-
// / |
143-
// / |
144-
// / |
145-
// ------------- (layer)
146-
// /| |
147-
// / | | (elevation)
148-
// A / | |B
149-
// ------------------------------------------------ (canvas)
150-
// --- (extent of shadow)
151-
//
152-
// E = lt } t = (r + w/2)/h
153-
// } =>
154-
// r + w/2 = ht } E = (l/h)(r + w/2)
155-
//
156-
// Where: E = extent of shadow
157-
// l = elevation of layer
158-
// r = radius of the light source
159-
// w = width of the layer
160-
// h = light height
161-
// t = tangent of AOB, i.e., multiplier for elevation to extent
162-
// tangent for x
163-
double tx =
164-
(kLightRadius * pixel_ratio + bounds.width() * 0.5) / kLightHeight;
165-
// tangent for y
166-
double ty =
167-
(kLightRadius * pixel_ratio + bounds.height() * 0.5) / kLightHeight;
168-
SkRect shadow_bounds(bounds);
169-
shadow_bounds.outset(elevation * tx, elevation * ty);
170-
133+
SkScalar dpr,
134+
const SkMatrix& ctm) {
135+
SkRect shadow_bounds(path.getBounds());
136+
SkShadowUtils::GetLocalBounds(
137+
ctm, path, SkPoint3::Make(0, 0, dpr * elevation),
138+
SkPoint3::Make(0, -1, 1), kLightRadius / kLightHeight,
139+
SkShadowFlags::kDirectionalLight_ShadowFlag, &shadow_bounds);
171140
return shadow_bounds;
172141
}
173142

@@ -180,21 +149,19 @@ void PhysicalShapeLayer::DrawShadow(SkCanvas* canvas,
180149
const SkScalar kAmbientAlpha = 0.039f;
181150
const SkScalar kSpotAlpha = 0.25f;
182151

183-
SkShadowFlags flags = transparentOccluder
184-
? SkShadowFlags::kTransparentOccluder_ShadowFlag
185-
: SkShadowFlags::kNone_ShadowFlag;
186-
const SkRect& bounds = path.getBounds();
187-
SkScalar shadow_x = (bounds.left() + bounds.right()) / 2;
188-
SkScalar shadow_y = bounds.top() - 600.0f;
152+
uint32_t flags = transparentOccluder
153+
? SkShadowFlags::kTransparentOccluder_ShadowFlag
154+
: SkShadowFlags::kNone_ShadowFlag;
155+
flags |= SkShadowFlags::kDirectionalLight_ShadowFlag;
189156
SkColor inAmbient = SkColorSetA(color, kAmbientAlpha * SkColorGetA(color));
190157
SkColor inSpot = SkColorSetA(color, kSpotAlpha * SkColorGetA(color));
191158
SkColor ambientColor, spotColor;
192159
SkShadowUtils::ComputeTonalColors(inAmbient, inSpot, &ambientColor,
193160
&spotColor);
194-
SkShadowUtils::DrawShadow(
195-
canvas, path, SkPoint3::Make(0, 0, dpr * elevation),
196-
SkPoint3::Make(shadow_x, shadow_y, dpr * kLightHeight),
197-
dpr * kLightRadius, ambientColor, spotColor, flags);
161+
SkShadowUtils::DrawShadow(canvas, path, SkPoint3::Make(0, 0, dpr * elevation),
162+
SkPoint3::Make(0, -1, 1),
163+
kLightRadius / kLightHeight, ambientColor,
164+
spotColor, flags);
198165
}
199166

200167
} // namespace flutter

flow/layers/physical_shape_layer.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ class PhysicalShapeLayer : public ContainerLayer {
1717
const SkPath& path,
1818
Clip clip_behavior);
1919

20-
static SkRect ComputeShadowBounds(const SkRect& bounds,
20+
static SkRect ComputeShadowBounds(const SkPath& path,
2121
float elevation,
22-
float pixel_ratio);
22+
SkScalar dpr,
23+
const SkMatrix& ctm);
2324
static void DrawShadow(SkCanvas* canvas,
2425
const SkPath& path,
2526
SkColor color,

flow/layers/physical_shape_layer_unittests.cc

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ TEST_F(PhysicalShapeLayerTest, ElevationSimple) {
124124
// The Fuchsia system compositor handles all elevated PhysicalShapeLayers and
125125
// their shadows , so we do not do any painting there.
126126
EXPECT_EQ(layer->paint_bounds(),
127-
PhysicalShapeLayer::ComputeShadowBounds(layer_path.getBounds(),
128-
initial_elevation, 1.0f));
127+
PhysicalShapeLayer::ComputeShadowBounds(
128+
layer_path, initial_elevation, 1.0f, SkMatrix()));
129129
EXPECT_TRUE(layer->needs_painting(paint_context()));
130130
EXPECT_EQ(layer->elevation(), initial_elevation);
131131

@@ -174,8 +174,8 @@ TEST_F(PhysicalShapeLayerTest, ElevationComplex) {
174174
// there.
175175
EXPECT_EQ(layers[i]->paint_bounds(),
176176
(PhysicalShapeLayer::ComputeShadowBounds(
177-
layer_path.getBounds(), initial_elevations[i],
178-
1.0f /* pixel_ratio */)));
177+
layer_path, initial_elevations[i], 1.0f /* pixel_ratio */,
178+
SkMatrix())));
179179
EXPECT_TRUE(layers[i]->needs_painting(paint_context()));
180180
}
181181

@@ -200,6 +200,93 @@ TEST_F(PhysicalShapeLayerTest, ElevationComplex) {
200200
0, MockCanvas::DrawPathData{layer_path, layer_paint}}}));
201201
}
202202

203+
TEST_F(PhysicalShapeLayerTest, ShadowNotDependsCtm) {
204+
constexpr SkScalar elevations[] = {1, 2, 3, 4, 5, 10};
205+
constexpr SkScalar scales[] = {0.5, 1, 1.5, 2, 3, 5};
206+
constexpr SkScalar translates[] = {0, 1, -1, 0.5, 2, 10};
207+
208+
SkPath path;
209+
path.addRect(0, 0, 8, 8).close();
210+
211+
for (SkScalar elevation : elevations) {
212+
SkRect baseline_bounds = PhysicalShapeLayer::ComputeShadowBounds(
213+
path, elevation, 1.0f, SkMatrix());
214+
for (SkScalar scale : scales) {
215+
for (SkScalar translateX : translates) {
216+
for (SkScalar translateY : translates) {
217+
SkMatrix ctm;
218+
ctm.setScaleTranslate(scale, scale, translateX, translateY);
219+
SkRect bounds = PhysicalShapeLayer::ComputeShadowBounds(
220+
path, elevation, scale, ctm);
221+
EXPECT_FLOAT_EQ(bounds.fLeft, baseline_bounds.fLeft);
222+
EXPECT_FLOAT_EQ(bounds.fTop, baseline_bounds.fTop);
223+
EXPECT_FLOAT_EQ(bounds.fRight, baseline_bounds.fRight);
224+
EXPECT_FLOAT_EQ(bounds.fBottom, baseline_bounds.fBottom);
225+
}
226+
}
227+
}
228+
}
229+
}
230+
231+
static int RasterizedDifferenceInPixels(
232+
const std::function<void(SkCanvas*)>& actual_draw_function,
233+
const std::function<void(SkCanvas*)>& expected_draw_function,
234+
const SkSize& canvas_size) {
235+
sk_sp<SkSurface> actual_surface =
236+
SkSurface::MakeRasterN32Premul(canvas_size.width(), canvas_size.height());
237+
sk_sp<SkSurface> expected_surface =
238+
SkSurface::MakeRasterN32Premul(canvas_size.width(), canvas_size.height());
239+
240+
actual_surface->getCanvas()->drawColor(SK_ColorWHITE);
241+
expected_surface->getCanvas()->drawColor(SK_ColorWHITE);
242+
243+
actual_draw_function(actual_surface->getCanvas());
244+
expected_draw_function(expected_surface->getCanvas());
245+
246+
SkPixmap actual_pixels;
247+
EXPECT_TRUE(actual_surface->peekPixels(&actual_pixels));
248+
249+
SkPixmap expected_pixels;
250+
EXPECT_TRUE(expected_surface->peekPixels(&expected_pixels));
251+
252+
int different_pixels = 0;
253+
for (int y = 0; y < canvas_size.height(); y++) {
254+
const uint32_t* actual_row = actual_pixels.addr32(0, y);
255+
const uint32_t* expected_row = expected_pixels.addr32(0, y);
256+
for (int x = 0; x < canvas_size.width(); x++) {
257+
if (actual_row[x] != expected_row[x]) {
258+
different_pixels++;
259+
}
260+
}
261+
}
262+
return different_pixels;
263+
}
264+
265+
TEST_F(PhysicalShapeLayerTest, ShadowNotDependsPathSize) {
266+
constexpr SkRect test_cases[][2] = {
267+
{{20, -100, 80, 80}, {20, -1000, 80, 80}},
268+
{{20, 20, 80, 200}, {20, 20, 80, 2000}},
269+
};
270+
271+
for (const SkRect* test_case : test_cases) {
272+
EXPECT_EQ(RasterizedDifferenceInPixels(
273+
[=](SkCanvas* canvas) {
274+
SkPath path;
275+
path.addRect(test_case[0]).close();
276+
PhysicalShapeLayer::DrawShadow(canvas, path, SK_ColorBLACK,
277+
1.0f, false, 1.0f);
278+
},
279+
[=](SkCanvas* canvas) {
280+
SkPath path;
281+
path.addRect(test_case[1]).close();
282+
PhysicalShapeLayer::DrawShadow(canvas, path, SK_ColorBLACK,
283+
1.0f, false, 1.0f);
284+
},
285+
SkSize::Make(100, 100)),
286+
0);
287+
}
288+
}
289+
203290
static bool ReadbackResult(PrerollContext* context,
204291
Clip clip_behavior,
205292
std::shared_ptr<Layer> child,

0 commit comments

Comments
 (0)