blob: df247e394f1f931d476e501e89fab08f2a9c586b [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/flow/layers/physical_shape_layer.h"
#include "flutter/flow/testing/layer_test.h"
#include "flutter/flow/testing/mock_layer.h"
#include "flutter/fml/macros.h"
#include "flutter/testing/mock_canvas.h"
namespace flutter {
namespace testing {
using PhysicalShapeLayerTest = LayerTest;
#ifndef NDEBUG
TEST_F(PhysicalShapeLayerTest, PaintingEmptyLayerDies) {
auto layer =
std::make_shared<PhysicalShapeLayer>(SK_ColorBLACK, SK_ColorBLACK,
0.0f, // elevation
SkPath(), Clip::none);
layer->Preroll(preroll_context(), SkMatrix());
EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty());
EXPECT_FALSE(layer->needs_painting());
EXPECT_FALSE(layer->needs_system_composite());
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
"needs_painting\\(\\)");
}
TEST_F(PhysicalShapeLayerTest, PaintBeforePreollDies) {
SkPath child_path;
child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f);
auto mock_layer = std::make_shared<MockLayer>(child_path, SkPaint());
auto layer =
std::make_shared<PhysicalShapeLayer>(SK_ColorBLACK, SK_ColorBLACK,
0.0f, // elevation
SkPath(), Clip::none);
layer->Add(mock_layer);
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
"needs_painting\\(\\)");
}
#endif
TEST_F(PhysicalShapeLayerTest, NonEmptyLayer) {
SkPath layer_path;
layer_path.addRect(5.0f, 6.0f, 20.5f, 21.5f);
auto layer =
std::make_shared<PhysicalShapeLayer>(SK_ColorGREEN, SK_ColorBLACK,
0.0f, // elevation
layer_path, Clip::none);
layer->Preroll(preroll_context(), SkMatrix());
EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds());
EXPECT_TRUE(layer->needs_painting());
EXPECT_FALSE(layer->needs_system_composite());
SkPaint layer_paint;
layer_paint.setColor(SK_ColorGREEN);
layer_paint.setAntiAlias(true);
layer->Paint(paint_context());
EXPECT_EQ(mock_canvas().draw_calls(),
std::vector({MockCanvas::DrawCall{
0, MockCanvas::DrawPathData{layer_path, layer_paint}}}));
}
TEST_F(PhysicalShapeLayerTest, ChildrenLargerThanPath) {
SkPath layer_path;
layer_path.addRect(5.0f, 6.0f, 20.5f, 21.5f);
SkPath child1_path;
child1_path.addRect(4, 0, 12, 12).close();
SkPath child2_path;
child2_path.addRect(3, 2, 5, 15).close();
auto child1 = std::make_shared<PhysicalShapeLayer>(SK_ColorRED, SK_ColorBLACK,
0.0f, // elevation
child1_path, Clip::none);
auto child2 =
std::make_shared<PhysicalShapeLayer>(SK_ColorBLUE, SK_ColorBLACK,
0.0f, // elevation
child2_path, Clip::none);
auto layer =
std::make_shared<PhysicalShapeLayer>(SK_ColorGREEN, SK_ColorBLACK,
0.0f, // elevation
layer_path, Clip::none);
layer->Add(child1);
layer->Add(child2);
SkRect child_paint_bounds;
layer->Preroll(preroll_context(), SkMatrix());
child_paint_bounds.join(child1->paint_bounds());
child_paint_bounds.join(child2->paint_bounds());
EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds());
EXPECT_NE(layer->paint_bounds(), child_paint_bounds);
EXPECT_TRUE(layer->needs_painting());
EXPECT_FALSE(layer->needs_system_composite());
SkPaint layer_paint;
layer_paint.setColor(SK_ColorGREEN);
layer_paint.setAntiAlias(true);
SkPaint child1_paint;
child1_paint.setColor(SK_ColorRED);
child1_paint.setAntiAlias(true);
SkPaint child2_paint;
child2_paint.setColor(SK_ColorBLUE);
child2_paint.setAntiAlias(true);
layer->Paint(paint_context());
EXPECT_EQ(
mock_canvas().draw_calls(),
std::vector({MockCanvas::DrawCall{
0, MockCanvas::DrawPathData{layer_path, layer_paint}},
MockCanvas::DrawCall{
0, MockCanvas::DrawPathData{child1_path, child1_paint}},
MockCanvas::DrawCall{0, MockCanvas::DrawPathData{
child2_path, child2_paint}}}));
}
TEST_F(PhysicalShapeLayerTest, ElevationSimple) {
constexpr float initial_elevation = 20.0f;
SkPath layer_path;
layer_path.addRect(0, 0, 8, 8).close();
auto layer = std::make_shared<PhysicalShapeLayer>(
SK_ColorGREEN, SK_ColorBLACK, initial_elevation, layer_path, Clip::none);
layer->Preroll(preroll_context(), SkMatrix());
// The Fuchsia system compositor handles all elevated PhysicalShapeLayers and
// their shadows , so we do not do any painting there.
EXPECT_EQ(layer->paint_bounds(),
PhysicalShapeLayer::ComputeShadowBounds(layer_path.getBounds(),
initial_elevation, 1.0f));
EXPECT_TRUE(layer->needs_painting());
EXPECT_FALSE(layer->needs_system_composite());
EXPECT_EQ(layer->total_elevation(), initial_elevation);
// The Fuchsia system compositor handles all elevated PhysicalShapeLayers and
// their shadows , so we do not use the direct |Paint()| path there.
#if !defined(OS_FUCHSIA)
SkPaint layer_paint;
layer_paint.setColor(SK_ColorGREEN);
layer_paint.setAntiAlias(true);
layer->Paint(paint_context());
EXPECT_EQ(
mock_canvas().draw_calls(),
std::vector(
{MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}},
MockCanvas::DrawCall{
0, MockCanvas::DrawPathData{layer_path, layer_paint}}}));
#endif
}
TEST_F(PhysicalShapeLayerTest, ElevationComplex) {
// The layer tree should look like this:
// layers[0] +1.0f = 1.0f
// | \
// | \
// | \
// | layers[2] +3.0f = 4.0f
// | |
// | layers[3] +4.0f = 8.0f
// |
// |
// layers[1] + 2.0f = 3.0f
constexpr float initial_elevations[4] = {1.0f, 2.0f, 3.0f, 4.0f};
constexpr float total_elevations[4] = {1.0f, 3.0f, 4.0f, 8.0f};
SkPath layer_path;
layer_path.addRect(0, 0, 80, 80).close();
std::shared_ptr<PhysicalShapeLayer> layers[4];
for (int i = 0; i < 4; i += 1) {
layers[i] = std::make_shared<PhysicalShapeLayer>(
SK_ColorBLACK, SK_ColorBLACK, initial_elevations[i], layer_path,
Clip::none);
}
layers[0]->Add(layers[1]);
layers[0]->Add(layers[2]);
layers[2]->Add(layers[3]);
layers[0]->Preroll(preroll_context(), SkMatrix());
for (int i = 0; i < 4; i += 1) {
// On Fuchsia, the system compositor handles all elevated
// PhysicalShapeLayers and their shadows , so we do not do any painting
// there.
EXPECT_EQ(layers[i]->paint_bounds(),
(PhysicalShapeLayer::ComputeShadowBounds(
layer_path.getBounds(), initial_elevations[i],
1.0f /* pixel_ratio */)));
EXPECT_TRUE(layers[i]->needs_painting());
EXPECT_FALSE(layers[i]->needs_system_composite());
EXPECT_EQ(layers[i]->total_elevation(), total_elevations[i]);
}
// The Fuchsia system compositor handles all elevated PhysicalShapeLayers and
// their shadows , so we do not use the direct |Paint()| path there.
#if !defined(OS_FUCHSIA)
SkPaint layer_paint;
layer_paint.setColor(SK_ColorBLACK);
layer_paint.setAntiAlias(true);
layers[0]->Paint(paint_context());
EXPECT_EQ(
mock_canvas().draw_calls(),
std::vector(
{MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}},
MockCanvas::DrawCall{
0, MockCanvas::DrawPathData{layer_path, layer_paint}},
MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}},
MockCanvas::DrawCall{
0, MockCanvas::DrawPathData{layer_path, layer_paint}},
MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}},
MockCanvas::DrawCall{
0, MockCanvas::DrawPathData{layer_path, layer_paint}},
MockCanvas::DrawCall{0, MockCanvas::DrawShadowData{layer_path}},
MockCanvas::DrawCall{
0, MockCanvas::DrawPathData{layer_path, layer_paint}}}));
#endif
}
static bool ReadbackResult(PrerollContext* context,
Clip clip_behavior,
std::shared_ptr<Layer> child,
bool before) {
const SkMatrix initial_matrix = SkMatrix();
const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
const SkPath layer_path = SkPath().addRect(layer_bounds);
auto layer =
std::make_shared<PhysicalShapeLayer>(SK_ColorGREEN, SK_ColorBLACK,
0.0f, // elevation
layer_path, clip_behavior);
if (child != nullptr) {
layer->Add(child);
}
context->surface_needs_readback = before;
layer->Preroll(context, initial_matrix);
return context->surface_needs_readback;
}
TEST_F(PhysicalShapeLayerTest, Readback) {
PrerollContext* context = preroll_context();
SkPath path;
SkPaint paint;
const Clip hard = Clip::hardEdge;
const Clip soft = Clip::antiAlias;
const Clip save_layer = Clip::antiAliasWithSaveLayer;
std::shared_ptr<MockLayer> nochild;
auto reader = std::make_shared<MockLayer>(path, paint, false, false, true);
auto nonreader = std::make_shared<MockLayer>(path, paint);
// No children, no prior readback -> no readback after
EXPECT_FALSE(ReadbackResult(context, hard, nochild, false));
EXPECT_FALSE(ReadbackResult(context, soft, nochild, false));
EXPECT_FALSE(ReadbackResult(context, save_layer, nochild, false));
// No children, prior readback -> readback after
EXPECT_TRUE(ReadbackResult(context, hard, nochild, true));
EXPECT_TRUE(ReadbackResult(context, soft, nochild, true));
EXPECT_TRUE(ReadbackResult(context, save_layer, nochild, true));
// Non readback child, no prior readback -> no readback after
EXPECT_FALSE(ReadbackResult(context, hard, nonreader, false));
EXPECT_FALSE(ReadbackResult(context, soft, nonreader, false));
EXPECT_FALSE(ReadbackResult(context, save_layer, nonreader, false));
// Non readback child, prior readback -> readback after
EXPECT_TRUE(ReadbackResult(context, hard, nonreader, true));
EXPECT_TRUE(ReadbackResult(context, soft, nonreader, true));
EXPECT_TRUE(ReadbackResult(context, save_layer, nonreader, true));
// Readback child, no prior readback -> readback after unless SaveLayer
EXPECT_TRUE(ReadbackResult(context, hard, reader, false));
EXPECT_TRUE(ReadbackResult(context, soft, reader, false));
EXPECT_FALSE(ReadbackResult(context, save_layer, reader, false));
// Readback child, prior readback -> readback after
EXPECT_TRUE(ReadbackResult(context, hard, reader, true));
EXPECT_TRUE(ReadbackResult(context, soft, reader, true));
EXPECT_TRUE(ReadbackResult(context, save_layer, reader, true));
}
} // namespace testing
} // namespace flutter