| // Copyright 2018 The Chromium 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 "third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.h" |
| |
| #include "base/optional.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/renderer/platform/graphics/paint/display_item.h" |
| #include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h" |
| #include "third_party/blink/renderer/platform/testing/fake_display_item_client.h" |
| #include "third_party/blink/renderer/platform/testing/paint_property_test_helpers.h" |
| |
| namespace blink { |
| |
| class ChunkToLayerMapperTest : public testing::Test { |
| protected: |
| static PaintChunk Chunk(const PropertyTreeState& state) { |
| DEFINE_STATIC_LOCAL(FakeDisplayItemClient, fake_client, ()); |
| DEFINE_STATIC_LOCAL( |
| base::Optional<PaintChunk::Id>, id, |
| (PaintChunk::Id(fake_client, DisplayItem::kDrawingFirst))); |
| PaintChunk chunk(0, 0, *id, state); |
| return chunk; |
| } |
| |
| // A state containing arbitrary values which should not affect test results |
| // if the state is used as a layer state. |
| PropertyTreeState LayerState() { |
| if (!layer_transform_) { |
| layer_transform_ = |
| CreateTransform(t0(), TransformationMatrix().Translate(123, 456), |
| FloatPoint3D(1, 2, 3)); |
| layer_clip_ = CreateClip(c0(), layer_transform_.get(), |
| FloatRoundedRect(12, 34, 56, 78)); |
| layer_effect_ = EffectPaintPropertyNode::Create( |
| e0(), EffectPaintPropertyNode::State{ |
| layer_transform_.get(), layer_clip_.get(), |
| kColorFilterLuminanceToAlpha, CompositorFilterOperations(), |
| 0.789f, CompositorFilterOperations(), SkBlendMode::kSrcIn}); |
| } |
| return PropertyTreeState(layer_transform_.get(), layer_clip_.get(), |
| layer_effect_.get()); |
| } |
| |
| bool HasFilterThatMovesPixels(const ChunkToLayerMapper& mapper) { |
| return mapper.has_filter_that_moves_pixels_; |
| } |
| |
| scoped_refptr<TransformPaintPropertyNode> layer_transform_; |
| scoped_refptr<ClipPaintPropertyNode> layer_clip_; |
| scoped_refptr<EffectPaintPropertyNode> layer_effect_; |
| }; |
| |
| TEST_F(ChunkToLayerMapperTest, OneChunkUsingLayerState) { |
| ChunkToLayerMapper mapper(LayerState(), gfx::Vector2dF(10, 20)); |
| auto chunk = Chunk(LayerState()); |
| mapper.SwitchToChunk(chunk); |
| EXPECT_FALSE(HasFilterThatMovesPixels(mapper)); |
| EXPECT_EQ(TransformationMatrix().Translate(-10, -20), mapper.Transform()); |
| EXPECT_EQ(FloatClipRect(), mapper.ClipRect()); |
| EXPECT_EQ(IntRect(20, 10, 88, 99), |
| mapper.MapVisualRect(FloatRect(30, 30, 88, 99))); |
| EXPECT_EQ(IntRect(20, 10, 88, 99), |
| mapper.MapVisualRect(FloatRect(30.2f, 30.7f, 87.3f, 98.1f))); |
| EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect())); |
| } |
| |
| TEST_F(ChunkToLayerMapperTest, TwoChunkUsingLayerState) { |
| ChunkToLayerMapper mapper(LayerState(), gfx::Vector2dF(10, 20)); |
| auto chunk1 = Chunk(LayerState()); |
| auto chunk2 = Chunk(LayerState()); |
| |
| mapper.SwitchToChunk(chunk1); |
| EXPECT_FALSE(HasFilterThatMovesPixels(mapper)); |
| EXPECT_EQ(TransformationMatrix().Translate(-10, -20), mapper.Transform()); |
| EXPECT_EQ(FloatClipRect(), mapper.ClipRect()); |
| EXPECT_EQ(IntRect(20, 10, 88, 99), |
| mapper.MapVisualRect(FloatRect(30, 30, 88, 99))); |
| EXPECT_EQ(IntRect(20, 10, 88, 99), |
| mapper.MapVisualRect(FloatRect(30.2f, 30.7f, 87.3f, 98.1f))); |
| EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect())); |
| |
| mapper.SwitchToChunk(chunk2); |
| EXPECT_FALSE(HasFilterThatMovesPixels(mapper)); |
| EXPECT_EQ(TransformationMatrix().Translate(-10, -20), mapper.Transform()); |
| EXPECT_EQ(FloatClipRect(), mapper.ClipRect()); |
| EXPECT_EQ(IntRect(20, 10, 88, 99), |
| mapper.MapVisualRect(FloatRect(30, 30, 88, 99))); |
| EXPECT_EQ(IntRect(20, 10, 88, 99), |
| mapper.MapVisualRect(FloatRect(30.2f, 30.7f, 87.3f, 98.1f))); |
| EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect())); |
| } |
| |
| TEST_F(ChunkToLayerMapperTest, TwoChunkSameState) { |
| ChunkToLayerMapper mapper(LayerState(), gfx::Vector2dF(10, 20)); |
| auto transform = CreateTransform(*LayerState().Transform(), |
| TransformationMatrix().Scale(2)); |
| auto clip = CreateClip(*LayerState().Clip(), LayerState().Transform(), |
| FloatRoundedRect(10, 10, 100, 100)); |
| auto* effect = LayerState().Effect(); |
| auto chunk1 = Chunk(PropertyTreeState(transform.get(), clip.get(), effect)); |
| auto chunk2 = Chunk(PropertyTreeState(transform.get(), clip.get(), effect)); |
| |
| mapper.SwitchToChunk(chunk1); |
| EXPECT_FALSE(HasFilterThatMovesPixels(mapper)); |
| EXPECT_EQ(TransformationMatrix().Translate(-10, -20).Scale(2), |
| mapper.Transform()); |
| EXPECT_EQ(FloatRect(0, -10, 100, 100), mapper.ClipRect().Rect()); |
| EXPECT_TRUE(mapper.ClipRect().IsTight()); |
| EXPECT_EQ(IntRect(50, 40, 50, 50), |
| mapper.MapVisualRect(FloatRect(30, 30, 88, 99))); |
| EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect())); |
| |
| mapper.SwitchToChunk(chunk2); |
| EXPECT_FALSE(HasFilterThatMovesPixels(mapper)); |
| EXPECT_EQ(TransformationMatrix().Translate(-10, -20).Scale(2), |
| mapper.Transform()); |
| EXPECT_EQ(FloatRect(0, -10, 100, 100), mapper.ClipRect().Rect()); |
| EXPECT_TRUE(mapper.ClipRect().IsTight()); |
| EXPECT_EQ(IntRect(50, 40, 50, 50), |
| mapper.MapVisualRect(FloatRect(30, 30, 88, 99))); |
| EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect())); |
| } |
| |
| TEST_F(ChunkToLayerMapperTest, TwoChunkDifferentState) { |
| ChunkToLayerMapper mapper(LayerState(), gfx::Vector2dF(10, 20)); |
| auto transform1 = CreateTransform(*LayerState().Transform(), |
| TransformationMatrix().Scale(2)); |
| auto clip1 = CreateClip(*LayerState().Clip(), LayerState().Transform(), |
| FloatRoundedRect(10, 10, 100, 100)); |
| auto* effect = LayerState().Effect(); |
| auto chunk1 = Chunk(PropertyTreeState(transform1.get(), clip1.get(), effect)); |
| |
| auto transform2 = |
| CreateTransform(*transform1, TransformationMatrix().Translate(20, 30)); |
| auto clip2 = CreateClip(*LayerState().Clip(), transform2.get(), |
| FloatRoundedRect(0, 0, 20, 20)); |
| auto chunk2 = Chunk(PropertyTreeState(transform2.get(), clip2.get(), effect)); |
| |
| mapper.SwitchToChunk(chunk1); |
| EXPECT_FALSE(HasFilterThatMovesPixels(mapper)); |
| EXPECT_EQ(TransformationMatrix().Translate(-10, -20).Scale(2), |
| mapper.Transform()); |
| EXPECT_EQ(FloatRect(0, -10, 100, 100), mapper.ClipRect().Rect()); |
| EXPECT_TRUE(mapper.ClipRect().IsTight()); |
| EXPECT_EQ(IntRect(50, 40, 50, 50), |
| mapper.MapVisualRect(FloatRect(30, 30, 88, 99))); |
| EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect())); |
| |
| mapper.SwitchToChunk(chunk2); |
| EXPECT_FALSE(HasFilterThatMovesPixels(mapper)); |
| EXPECT_EQ( |
| TransformationMatrix().Translate(-10, -20).Scale(2).Translate(20, 30), |
| mapper.Transform()); |
| EXPECT_EQ(FloatRect(30, 40, 40, 40), mapper.ClipRect().Rect()); |
| EXPECT_FALSE(mapper.ClipRect().IsTight()); |
| EXPECT_EQ(IntRect(30, 40, 40, 40), |
| mapper.MapVisualRect(FloatRect(0, 0, 200, 200))); |
| EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect())); |
| } |
| |
| TEST_F(ChunkToLayerMapperTest, SlowPath) { |
| ChunkToLayerMapper mapper(LayerState(), gfx::Vector2dF(10, 20)); |
| auto chunk1 = Chunk(LayerState()); |
| |
| // Chunk2 has a blur filter. Should use the slow path. |
| CompositorFilterOperations filter2; |
| filter2.AppendBlurFilter(20); |
| auto effect2 = CreateFilterEffect(*LayerState().Effect(), std::move(filter2)); |
| auto chunk2 = Chunk(PropertyTreeState(LayerState().Transform(), |
| LayerState().Clip(), effect2.get())); |
| |
| // Chunk3 has a different effect which inherits from chunk2's effect. |
| // Should use the slow path. |
| auto effect3 = CreateOpacityEffect(*effect2, 1.f); |
| auto chunk3 = Chunk(PropertyTreeState(LayerState().Transform(), |
| LayerState().Clip(), effect3.get())); |
| |
| // Chunk4 has an opacity filter effect which inherits from the layer's effect. |
| // Should use the fast path. |
| CompositorFilterOperations filter4; |
| filter4.AppendOpacityFilter(0.5); |
| auto effect4 = CreateFilterEffect(*LayerState().Effect(), std::move(filter4)); |
| auto chunk4 = Chunk(PropertyTreeState(LayerState().Transform(), |
| LayerState().Clip(), effect4.get())); |
| |
| // Chunk5 goes back to the layer state. |
| auto chunk5 = Chunk(LayerState()); |
| |
| mapper.SwitchToChunk(chunk1); |
| EXPECT_FALSE(HasFilterThatMovesPixels(mapper)); |
| EXPECT_EQ(TransformationMatrix().Translate(-10, -20), mapper.Transform()); |
| EXPECT_EQ(FloatClipRect(), mapper.ClipRect()); |
| |
| mapper.SwitchToChunk(chunk2); |
| EXPECT_TRUE(HasFilterThatMovesPixels(mapper)); |
| EXPECT_EQ(TransformationMatrix().Translate(-10, -20), mapper.Transform()); |
| EXPECT_TRUE(mapper.ClipRect().IsInfinite()); |
| EXPECT_EQ(IntRect(-40, -50, 208, 219), |
| mapper.MapVisualRect(FloatRect(30, 30, 88, 99))); |
| EXPECT_EQ(IntRect(-40, -50, 208, 219), |
| mapper.MapVisualRect(FloatRect(30.2f, 30.7f, 87.3f, 98.1f))); |
| EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect())); |
| |
| mapper.SwitchToChunk(chunk3); |
| EXPECT_TRUE(HasFilterThatMovesPixels(mapper)); |
| EXPECT_EQ(TransformationMatrix().Translate(-10, -20), mapper.Transform()); |
| EXPECT_TRUE(mapper.ClipRect().IsInfinite()); |
| EXPECT_EQ(IntRect(-40, -50, 208, 219), |
| mapper.MapVisualRect(FloatRect(30, 30, 88, 99))); |
| EXPECT_EQ(IntRect(-40, -50, 208, 219), |
| mapper.MapVisualRect(FloatRect(30.2f, 30.7f, 87.3f, 98.1f))); |
| EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect())); |
| |
| mapper.SwitchToChunk(chunk4); |
| EXPECT_FALSE(HasFilterThatMovesPixels(mapper)); |
| EXPECT_EQ(TransformationMatrix().Translate(-10, -20), mapper.Transform()); |
| EXPECT_EQ(FloatClipRect(), mapper.ClipRect()); |
| |
| mapper.SwitchToChunk(chunk5); |
| EXPECT_FALSE(HasFilterThatMovesPixels(mapper)); |
| EXPECT_EQ(TransformationMatrix().Translate(-10, -20), mapper.Transform()); |
| EXPECT_EQ(FloatClipRect(), mapper.ClipRect()); |
| } |
| |
| TEST_F(ChunkToLayerMapperTest, SwitchToSiblingEffect) { |
| auto effect1 = CreateOpacityEffect(*LayerState().Effect(), 0.5f); |
| auto chunk1 = Chunk(PropertyTreeState(LayerState().Transform(), |
| LayerState().Clip(), effect1.get())); |
| auto effect2 = CreateOpacityEffect(*LayerState().Effect(), 0.5f); |
| auto chunk2 = Chunk(PropertyTreeState(LayerState().Transform(), |
| LayerState().Clip(), effect2.get())); |
| |
| ChunkToLayerMapper mapper(chunk1.properties.GetPropertyTreeState(), |
| gfx::Vector2dF(10, 20)); |
| mapper.SwitchToChunk(chunk2); |
| EXPECT_FALSE(HasFilterThatMovesPixels(mapper)); |
| } |
| |
| } // namespace blink |