| // Copyright (c) 2012 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 "base/basictypes.h" |
| #include "base/bind.h" |
| #include "base/compiler_specific.h" |
| #include "base/file_util.h" |
| #include "base/files/file_path.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/path_service.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "cc/layers/delegated_frame_provider.h" |
| #include "cc/layers/delegated_frame_resource_collection.h" |
| #include "cc/layers/layer.h" |
| #include "cc/output/delegated_frame_data.h" |
| #include "cc/test/pixel_test_utils.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/compositor/compositor_observer.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/compositor/layer_animation_sequence.h" |
| #include "ui/compositor/layer_animator.h" |
| #include "ui/compositor/test/context_factories_for_test.h" |
| #include "ui/compositor/test/test_compositor_host.h" |
| #include "ui/compositor/test/test_layers.h" |
| #include "ui/gfx/canvas.h" |
| #include "ui/gfx/codec/png_codec.h" |
| #include "ui/gfx/gfx_paths.h" |
| #include "ui/gfx/skia_util.h" |
| |
| using cc::MatchesPNGFile; |
| |
| namespace ui { |
| |
| namespace { |
| |
| // There are three test classes in here that configure the Compositor and |
| // Layer's slightly differently: |
| // - LayerWithNullDelegateTest uses NullLayerDelegate as the LayerDelegate. This |
| // is typically the base class you want to use. |
| // - LayerWithDelegateTest uses LayerDelegate on the delegates. |
| // - LayerWithRealCompositorTest when a real compositor is required for testing. |
| // - Slow because they bring up a window and run the real compositor. This |
| // is typically not what you want. |
| |
| class ColoredLayer : public Layer, public LayerDelegate { |
| public: |
| explicit ColoredLayer(SkColor color) |
| : Layer(LAYER_TEXTURED), |
| color_(color) { |
| set_delegate(this); |
| } |
| |
| virtual ~ColoredLayer() { } |
| |
| // Overridden from LayerDelegate: |
| virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE { |
| canvas->DrawColor(color_); |
| } |
| |
| virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE { |
| } |
| |
| virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE { |
| return base::Closure(); |
| } |
| |
| private: |
| SkColor color_; |
| }; |
| |
| class LayerWithRealCompositorTest : public testing::Test { |
| public: |
| LayerWithRealCompositorTest() { |
| if (PathService::Get(gfx::DIR_TEST_DATA, &test_data_directory_)) { |
| test_data_directory_ = test_data_directory_.AppendASCII("compositor"); |
| } else { |
| LOG(ERROR) << "Could not open test data directory."; |
| } |
| } |
| virtual ~LayerWithRealCompositorTest() {} |
| |
| // Overridden from testing::Test: |
| virtual void SetUp() OVERRIDE { |
| bool allow_test_contexts = false; |
| InitializeContextFactoryForTests(allow_test_contexts); |
| Compositor::Initialize(); |
| |
| const gfx::Rect host_bounds(10, 10, 500, 500); |
| window_.reset(TestCompositorHost::Create(host_bounds)); |
| window_->Show(); |
| } |
| |
| virtual void TearDown() OVERRIDE { |
| window_.reset(); |
| TerminateContextFactoryForTests(); |
| Compositor::Terminate(); |
| } |
| |
| Compositor* GetCompositor() { |
| return window_->GetCompositor(); |
| } |
| |
| Layer* CreateLayer(LayerType type) { |
| return new Layer(type); |
| } |
| |
| Layer* CreateColorLayer(SkColor color, const gfx::Rect& bounds) { |
| Layer* layer = new ColoredLayer(color); |
| layer->SetBounds(bounds); |
| return layer; |
| } |
| |
| Layer* CreateNoTextureLayer(const gfx::Rect& bounds) { |
| Layer* layer = CreateLayer(LAYER_NOT_DRAWN); |
| layer->SetBounds(bounds); |
| return layer; |
| } |
| |
| void DrawTree(Layer* root) { |
| GetCompositor()->SetRootLayer(root); |
| GetCompositor()->ScheduleDraw(); |
| WaitForDraw(); |
| } |
| |
| bool ReadPixels(SkBitmap* bitmap) { |
| return GetCompositor()->ReadPixels(bitmap, |
| gfx::Rect(GetCompositor()->size())); |
| } |
| |
| void WaitForDraw() { |
| ui::DrawWaiterForTest::Wait(GetCompositor()); |
| } |
| |
| void WaitForCommit() { |
| ui::DrawWaiterForTest::WaitForCommit(GetCompositor()); |
| } |
| |
| // Invalidates the entire contents of the layer. |
| void SchedulePaintForLayer(Layer* layer) { |
| layer->SchedulePaint( |
| gfx::Rect(0, 0, layer->bounds().width(), layer->bounds().height())); |
| } |
| |
| const base::FilePath& test_data_directory() const { |
| return test_data_directory_; |
| } |
| |
| private: |
| scoped_ptr<TestCompositorHost> window_; |
| |
| // The root directory for test files. |
| base::FilePath test_data_directory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(LayerWithRealCompositorTest); |
| }; |
| |
| // LayerDelegate that paints colors to the layer. |
| class TestLayerDelegate : public LayerDelegate { |
| public: |
| explicit TestLayerDelegate() { reset(); } |
| virtual ~TestLayerDelegate() {} |
| |
| void AddColor(SkColor color) { |
| colors_.push_back(color); |
| } |
| |
| const gfx::Size& paint_size() const { return paint_size_; } |
| int color_index() const { return color_index_; } |
| |
| std::string ToScaleString() const { |
| return base::StringPrintf("%.1f %.1f", scale_x_, scale_y_); |
| } |
| |
| float device_scale_factor() const { |
| return device_scale_factor_; |
| } |
| |
| // Overridden from LayerDelegate: |
| virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE { |
| gfx::ImageSkiaRep contents = canvas->ExtractImageRep(); |
| paint_size_ = gfx::Size(contents.pixel_width(), contents.pixel_height()); |
| canvas->FillRect(gfx::Rect(paint_size_), colors_[color_index_]); |
| color_index_ = (color_index_ + 1) % static_cast<int>(colors_.size()); |
| const SkMatrix& matrix = canvas->sk_canvas()->getTotalMatrix(); |
| scale_x_ = matrix.getScaleX(); |
| scale_y_ = matrix.getScaleY(); |
| } |
| |
| virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE { |
| device_scale_factor_ = device_scale_factor; |
| } |
| |
| virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE { |
| return base::Closure(); |
| } |
| |
| void reset() { |
| color_index_ = 0; |
| paint_size_.SetSize(0, 0); |
| scale_x_ = scale_y_ = 0.0f; |
| device_scale_factor_ = 0.0f; |
| } |
| |
| private: |
| std::vector<SkColor> colors_; |
| int color_index_; |
| gfx::Size paint_size_; |
| float scale_x_; |
| float scale_y_; |
| float device_scale_factor_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestLayerDelegate); |
| }; |
| |
| // LayerDelegate that verifies that a layer was asked to update its canvas. |
| class DrawTreeLayerDelegate : public LayerDelegate { |
| public: |
| DrawTreeLayerDelegate() : painted_(false) {} |
| virtual ~DrawTreeLayerDelegate() {} |
| |
| void Reset() { |
| painted_ = false; |
| } |
| |
| bool painted() const { return painted_; } |
| |
| private: |
| // Overridden from LayerDelegate: |
| virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE { |
| painted_ = true; |
| } |
| virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE { |
| } |
| virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE { |
| return base::Closure(); |
| } |
| |
| bool painted_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DrawTreeLayerDelegate); |
| }; |
| |
| // The simplest possible layer delegate. Does nothing. |
| class NullLayerDelegate : public LayerDelegate { |
| public: |
| NullLayerDelegate() {} |
| virtual ~NullLayerDelegate() {} |
| |
| private: |
| // Overridden from LayerDelegate: |
| virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE { |
| } |
| virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE { |
| } |
| virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE { |
| return base::Closure(); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(NullLayerDelegate); |
| }; |
| |
| // Remembers if it has been notified. |
| class TestCompositorObserver : public CompositorObserver { |
| public: |
| TestCompositorObserver() |
| : committed_(false), started_(false), ended_(false), aborted_(false) {} |
| |
| bool committed() const { return committed_; } |
| bool notified() const { return started_ && ended_; } |
| bool aborted() const { return aborted_; } |
| |
| void Reset() { |
| committed_ = false; |
| started_ = false; |
| ended_ = false; |
| aborted_ = false; |
| } |
| |
| private: |
| virtual void OnCompositingDidCommit(Compositor* compositor) OVERRIDE { |
| committed_ = true; |
| } |
| |
| virtual void OnCompositingStarted(Compositor* compositor, |
| base::TimeTicks start_time) OVERRIDE { |
| started_ = true; |
| } |
| |
| virtual void OnCompositingEnded(Compositor* compositor) OVERRIDE { |
| ended_ = true; |
| } |
| |
| virtual void OnCompositingAborted(Compositor* compositor) OVERRIDE { |
| aborted_ = true; |
| } |
| |
| virtual void OnCompositingLockStateChanged(Compositor* compositor) OVERRIDE { |
| } |
| |
| virtual void OnUpdateVSyncParameters(Compositor* compositor, |
| base::TimeTicks timebase, |
| base::TimeDelta interval) OVERRIDE { |
| } |
| |
| bool committed_; |
| bool started_; |
| bool ended_; |
| bool aborted_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestCompositorObserver); |
| }; |
| |
| } // namespace |
| |
| TEST_F(LayerWithRealCompositorTest, Draw) { |
| scoped_ptr<Layer> layer(CreateColorLayer(SK_ColorRED, |
| gfx::Rect(20, 20, 50, 50))); |
| DrawTree(layer.get()); |
| } |
| |
| // Create this hierarchy: |
| // L1 - red |
| // +-- L2 - blue |
| // | +-- L3 - yellow |
| // +-- L4 - magenta |
| // |
| TEST_F(LayerWithRealCompositorTest, Hierarchy) { |
| scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorRED, |
| gfx::Rect(20, 20, 400, 400))); |
| scoped_ptr<Layer> l2(CreateColorLayer(SK_ColorBLUE, |
| gfx::Rect(10, 10, 350, 350))); |
| scoped_ptr<Layer> l3(CreateColorLayer(SK_ColorYELLOW, |
| gfx::Rect(5, 5, 25, 25))); |
| scoped_ptr<Layer> l4(CreateColorLayer(SK_ColorMAGENTA, |
| gfx::Rect(300, 300, 100, 100))); |
| |
| l1->Add(l2.get()); |
| l1->Add(l4.get()); |
| l2->Add(l3.get()); |
| |
| DrawTree(l1.get()); |
| } |
| |
| class LayerWithDelegateTest : public testing::Test { |
| public: |
| LayerWithDelegateTest() {} |
| virtual ~LayerWithDelegateTest() {} |
| |
| // Overridden from testing::Test: |
| virtual void SetUp() OVERRIDE { |
| bool allow_test_contexts = true; |
| InitializeContextFactoryForTests(allow_test_contexts); |
| Compositor::Initialize(); |
| compositor_.reset(new Compositor(gfx::kNullAcceleratedWidget)); |
| compositor_->SetScaleAndSize(1.0f, gfx::Size(1000, 1000)); |
| } |
| |
| virtual void TearDown() OVERRIDE { |
| compositor_.reset(); |
| TerminateContextFactoryForTests(); |
| Compositor::Terminate(); |
| } |
| |
| Compositor* compositor() { return compositor_.get(); } |
| |
| virtual Layer* CreateLayer(LayerType type) { |
| return new Layer(type); |
| } |
| |
| Layer* CreateColorLayer(SkColor color, const gfx::Rect& bounds) { |
| Layer* layer = new ColoredLayer(color); |
| layer->SetBounds(bounds); |
| return layer; |
| } |
| |
| virtual Layer* CreateNoTextureLayer(const gfx::Rect& bounds) { |
| Layer* layer = CreateLayer(LAYER_NOT_DRAWN); |
| layer->SetBounds(bounds); |
| return layer; |
| } |
| |
| void DrawTree(Layer* root) { |
| compositor()->SetRootLayer(root); |
| Draw(); |
| } |
| |
| // Invalidates the entire contents of the layer. |
| void SchedulePaintForLayer(Layer* layer) { |
| layer->SchedulePaint( |
| gfx::Rect(0, 0, layer->bounds().width(), layer->bounds().height())); |
| } |
| |
| // Invokes DrawTree on the compositor. |
| void Draw() { |
| compositor()->ScheduleDraw(); |
| WaitForDraw(); |
| } |
| |
| void WaitForDraw() { |
| DrawWaiterForTest::Wait(compositor()); |
| } |
| |
| void WaitForCommit() { |
| DrawWaiterForTest::WaitForCommit(compositor()); |
| } |
| |
| private: |
| scoped_ptr<Compositor> compositor_; |
| |
| DISALLOW_COPY_AND_ASSIGN(LayerWithDelegateTest); |
| }; |
| |
| // L1 |
| // +-- L2 |
| TEST_F(LayerWithDelegateTest, ConvertPointToLayer_Simple) { |
| scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorRED, |
| gfx::Rect(20, 20, 400, 400))); |
| scoped_ptr<Layer> l2(CreateColorLayer(SK_ColorBLUE, |
| gfx::Rect(10, 10, 350, 350))); |
| l1->Add(l2.get()); |
| DrawTree(l1.get()); |
| |
| gfx::Point point1_in_l2_coords(5, 5); |
| Layer::ConvertPointToLayer(l2.get(), l1.get(), &point1_in_l2_coords); |
| gfx::Point point1_in_l1_coords(15, 15); |
| EXPECT_EQ(point1_in_l1_coords, point1_in_l2_coords); |
| |
| gfx::Point point2_in_l1_coords(5, 5); |
| Layer::ConvertPointToLayer(l1.get(), l2.get(), &point2_in_l1_coords); |
| gfx::Point point2_in_l2_coords(-5, -5); |
| EXPECT_EQ(point2_in_l2_coords, point2_in_l1_coords); |
| } |
| |
| // L1 |
| // +-- L2 |
| // +-- L3 |
| TEST_F(LayerWithDelegateTest, ConvertPointToLayer_Medium) { |
| scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorRED, |
| gfx::Rect(20, 20, 400, 400))); |
| scoped_ptr<Layer> l2(CreateColorLayer(SK_ColorBLUE, |
| gfx::Rect(10, 10, 350, 350))); |
| scoped_ptr<Layer> l3(CreateColorLayer(SK_ColorYELLOW, |
| gfx::Rect(10, 10, 100, 100))); |
| l1->Add(l2.get()); |
| l2->Add(l3.get()); |
| DrawTree(l1.get()); |
| |
| gfx::Point point1_in_l3_coords(5, 5); |
| Layer::ConvertPointToLayer(l3.get(), l1.get(), &point1_in_l3_coords); |
| gfx::Point point1_in_l1_coords(25, 25); |
| EXPECT_EQ(point1_in_l1_coords, point1_in_l3_coords); |
| |
| gfx::Point point2_in_l1_coords(5, 5); |
| Layer::ConvertPointToLayer(l1.get(), l3.get(), &point2_in_l1_coords); |
| gfx::Point point2_in_l3_coords(-15, -15); |
| EXPECT_EQ(point2_in_l3_coords, point2_in_l1_coords); |
| } |
| |
| TEST_F(LayerWithRealCompositorTest, Delegate) { |
| scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorBLACK, |
| gfx::Rect(20, 20, 400, 400))); |
| GetCompositor()->SetRootLayer(l1.get()); |
| WaitForDraw(); |
| |
| TestLayerDelegate delegate; |
| l1->set_delegate(&delegate); |
| delegate.AddColor(SK_ColorWHITE); |
| delegate.AddColor(SK_ColorYELLOW); |
| delegate.AddColor(SK_ColorGREEN); |
| |
| l1->SchedulePaint(gfx::Rect(0, 0, 400, 400)); |
| WaitForDraw(); |
| |
| EXPECT_EQ(delegate.color_index(), 1); |
| EXPECT_EQ(delegate.paint_size(), l1->bounds().size()); |
| |
| l1->SchedulePaint(gfx::Rect(10, 10, 200, 200)); |
| WaitForDraw(); |
| EXPECT_EQ(delegate.color_index(), 2); |
| EXPECT_EQ(delegate.paint_size(), gfx::Size(200, 200)); |
| |
| l1->SchedulePaint(gfx::Rect(5, 5, 50, 50)); |
| WaitForDraw(); |
| EXPECT_EQ(delegate.color_index(), 0); |
| EXPECT_EQ(delegate.paint_size(), gfx::Size(50, 50)); |
| } |
| |
| TEST_F(LayerWithRealCompositorTest, DrawTree) { |
| scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorRED, |
| gfx::Rect(20, 20, 400, 400))); |
| scoped_ptr<Layer> l2(CreateColorLayer(SK_ColorBLUE, |
| gfx::Rect(10, 10, 350, 350))); |
| scoped_ptr<Layer> l3(CreateColorLayer(SK_ColorYELLOW, |
| gfx::Rect(10, 10, 100, 100))); |
| l1->Add(l2.get()); |
| l2->Add(l3.get()); |
| |
| GetCompositor()->SetRootLayer(l1.get()); |
| WaitForDraw(); |
| |
| DrawTreeLayerDelegate d1; |
| l1->set_delegate(&d1); |
| DrawTreeLayerDelegate d2; |
| l2->set_delegate(&d2); |
| DrawTreeLayerDelegate d3; |
| l3->set_delegate(&d3); |
| |
| l2->SchedulePaint(gfx::Rect(5, 5, 5, 5)); |
| WaitForDraw(); |
| EXPECT_FALSE(d1.painted()); |
| EXPECT_TRUE(d2.painted()); |
| EXPECT_FALSE(d3.painted()); |
| } |
| |
| // Tests no-texture Layers. |
| // Create this hierarchy: |
| // L1 - red |
| // +-- L2 - NO TEXTURE |
| // | +-- L3 - yellow |
| // +-- L4 - magenta |
| // |
| TEST_F(LayerWithRealCompositorTest, HierarchyNoTexture) { |
| scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorRED, |
| gfx::Rect(20, 20, 400, 400))); |
| scoped_ptr<Layer> l2(CreateNoTextureLayer(gfx::Rect(10, 10, 350, 350))); |
| scoped_ptr<Layer> l3(CreateColorLayer(SK_ColorYELLOW, |
| gfx::Rect(5, 5, 25, 25))); |
| scoped_ptr<Layer> l4(CreateColorLayer(SK_ColorMAGENTA, |
| gfx::Rect(300, 300, 100, 100))); |
| |
| l1->Add(l2.get()); |
| l1->Add(l4.get()); |
| l2->Add(l3.get()); |
| |
| GetCompositor()->SetRootLayer(l1.get()); |
| WaitForDraw(); |
| |
| DrawTreeLayerDelegate d2; |
| l2->set_delegate(&d2); |
| DrawTreeLayerDelegate d3; |
| l3->set_delegate(&d3); |
| |
| l2->SchedulePaint(gfx::Rect(5, 5, 5, 5)); |
| l3->SchedulePaint(gfx::Rect(5, 5, 5, 5)); |
| WaitForDraw(); |
| |
| // |d2| should not have received a paint notification since it has no texture. |
| EXPECT_FALSE(d2.painted()); |
| // |d3| should have received a paint notification. |
| EXPECT_TRUE(d3.painted()); |
| } |
| |
| class LayerWithNullDelegateTest : public LayerWithDelegateTest { |
| public: |
| LayerWithNullDelegateTest() {} |
| virtual ~LayerWithNullDelegateTest() {} |
| |
| virtual void SetUp() OVERRIDE { |
| LayerWithDelegateTest::SetUp(); |
| default_layer_delegate_.reset(new NullLayerDelegate()); |
| } |
| |
| virtual Layer* CreateLayer(LayerType type) OVERRIDE { |
| Layer* layer = new Layer(type); |
| layer->set_delegate(default_layer_delegate_.get()); |
| return layer; |
| } |
| |
| Layer* CreateTextureRootLayer(const gfx::Rect& bounds) { |
| Layer* layer = CreateTextureLayer(bounds); |
| compositor()->SetRootLayer(layer); |
| return layer; |
| } |
| |
| Layer* CreateTextureLayer(const gfx::Rect& bounds) { |
| Layer* layer = CreateLayer(LAYER_TEXTURED); |
| layer->SetBounds(bounds); |
| return layer; |
| } |
| |
| virtual Layer* CreateNoTextureLayer(const gfx::Rect& bounds) OVERRIDE { |
| Layer* layer = CreateLayer(LAYER_NOT_DRAWN); |
| layer->SetBounds(bounds); |
| return layer; |
| } |
| |
| private: |
| scoped_ptr<NullLayerDelegate> default_layer_delegate_; |
| |
| DISALLOW_COPY_AND_ASSIGN(LayerWithNullDelegateTest); |
| }; |
| |
| class FakeTexture : public Texture { |
| public: |
| FakeTexture(bool flipped, const gfx::Size& size, float device_scale_factor) |
| : Texture(flipped, size, device_scale_factor) {} |
| |
| virtual unsigned int PrepareTexture() OVERRIDE { return 0; } |
| |
| protected: |
| virtual ~FakeTexture() {} |
| }; |
| |
| TEST_F(LayerWithNullDelegateTest, SwitchLayerPreservesCCLayerState) { |
| scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorRED, |
| gfx::Rect(20, 20, 400, 400))); |
| l1->SetFillsBoundsOpaquely(true); |
| l1->SetForceRenderSurface(true); |
| l1->SetVisible(false); |
| |
| EXPECT_EQ(gfx::PointF().ToString(), |
| l1->cc_layer()->anchor_point().ToString()); |
| EXPECT_TRUE(l1->cc_layer()->DrawsContent()); |
| EXPECT_TRUE(l1->cc_layer()->contents_opaque()); |
| EXPECT_TRUE(l1->cc_layer()->force_render_surface()); |
| EXPECT_TRUE(l1->cc_layer()->hide_layer_and_subtree()); |
| |
| cc::Layer* before_layer = l1->cc_layer(); |
| |
| scoped_refptr<Texture> texture = |
| new FakeTexture(false, gfx::Size(10, 10), 1.f); |
| l1->SetExternalTexture(texture.get()); |
| |
| EXPECT_NE(before_layer, l1->cc_layer()); |
| |
| EXPECT_EQ(gfx::PointF().ToString(), |
| l1->cc_layer()->anchor_point().ToString()); |
| EXPECT_TRUE(l1->cc_layer()->DrawsContent()); |
| EXPECT_TRUE(l1->cc_layer()->contents_opaque()); |
| EXPECT_TRUE(l1->cc_layer()->force_render_surface()); |
| EXPECT_TRUE(l1->cc_layer()->hide_layer_and_subtree()); |
| } |
| |
| // Various visibile/drawn assertions. |
| TEST_F(LayerWithNullDelegateTest, Visibility) { |
| scoped_ptr<Layer> l1(new Layer(LAYER_TEXTURED)); |
| scoped_ptr<Layer> l2(new Layer(LAYER_TEXTURED)); |
| scoped_ptr<Layer> l3(new Layer(LAYER_TEXTURED)); |
| l1->Add(l2.get()); |
| l2->Add(l3.get()); |
| |
| NullLayerDelegate delegate; |
| l1->set_delegate(&delegate); |
| l2->set_delegate(&delegate); |
| l3->set_delegate(&delegate); |
| |
| // Layers should initially be drawn. |
| EXPECT_TRUE(l1->IsDrawn()); |
| EXPECT_TRUE(l2->IsDrawn()); |
| EXPECT_TRUE(l3->IsDrawn()); |
| EXPECT_FALSE(l1->cc_layer()->hide_layer_and_subtree()); |
| EXPECT_FALSE(l2->cc_layer()->hide_layer_and_subtree()); |
| EXPECT_FALSE(l3->cc_layer()->hide_layer_and_subtree()); |
| |
| compositor()->SetRootLayer(l1.get()); |
| |
| Draw(); |
| |
| l1->SetVisible(false); |
| EXPECT_FALSE(l1->IsDrawn()); |
| EXPECT_FALSE(l2->IsDrawn()); |
| EXPECT_FALSE(l3->IsDrawn()); |
| EXPECT_TRUE(l1->cc_layer()->hide_layer_and_subtree()); |
| EXPECT_FALSE(l2->cc_layer()->hide_layer_and_subtree()); |
| EXPECT_FALSE(l3->cc_layer()->hide_layer_and_subtree()); |
| |
| l3->SetVisible(false); |
| EXPECT_FALSE(l1->IsDrawn()); |
| EXPECT_FALSE(l2->IsDrawn()); |
| EXPECT_FALSE(l3->IsDrawn()); |
| EXPECT_TRUE(l1->cc_layer()->hide_layer_and_subtree()); |
| EXPECT_FALSE(l2->cc_layer()->hide_layer_and_subtree()); |
| EXPECT_TRUE(l3->cc_layer()->hide_layer_and_subtree()); |
| |
| l1->SetVisible(true); |
| EXPECT_TRUE(l1->IsDrawn()); |
| EXPECT_TRUE(l2->IsDrawn()); |
| EXPECT_FALSE(l3->IsDrawn()); |
| EXPECT_FALSE(l1->cc_layer()->hide_layer_and_subtree()); |
| EXPECT_FALSE(l2->cc_layer()->hide_layer_and_subtree()); |
| EXPECT_TRUE(l3->cc_layer()->hide_layer_and_subtree()); |
| } |
| |
| // Checks that stacking-related methods behave as advertised. |
| TEST_F(LayerWithNullDelegateTest, Stacking) { |
| scoped_ptr<Layer> root(new Layer(LAYER_NOT_DRAWN)); |
| scoped_ptr<Layer> l1(new Layer(LAYER_TEXTURED)); |
| scoped_ptr<Layer> l2(new Layer(LAYER_TEXTURED)); |
| scoped_ptr<Layer> l3(new Layer(LAYER_TEXTURED)); |
| l1->set_name("1"); |
| l2->set_name("2"); |
| l3->set_name("3"); |
| root->Add(l3.get()); |
| root->Add(l2.get()); |
| root->Add(l1.get()); |
| |
| // Layers' children are stored in bottom-to-top order. |
| EXPECT_EQ("3 2 1", test::ChildLayerNamesAsString(*root.get())); |
| |
| root->StackAtTop(l3.get()); |
| EXPECT_EQ("2 1 3", test::ChildLayerNamesAsString(*root.get())); |
| |
| root->StackAtTop(l1.get()); |
| EXPECT_EQ("2 3 1", test::ChildLayerNamesAsString(*root.get())); |
| |
| root->StackAtTop(l1.get()); |
| EXPECT_EQ("2 3 1", test::ChildLayerNamesAsString(*root.get())); |
| |
| root->StackAbove(l2.get(), l3.get()); |
| EXPECT_EQ("3 2 1", test::ChildLayerNamesAsString(*root.get())); |
| |
| root->StackAbove(l1.get(), l3.get()); |
| EXPECT_EQ("3 1 2", test::ChildLayerNamesAsString(*root.get())); |
| |
| root->StackAbove(l2.get(), l1.get()); |
| EXPECT_EQ("3 1 2", test::ChildLayerNamesAsString(*root.get())); |
| |
| root->StackAtBottom(l2.get()); |
| EXPECT_EQ("2 3 1", test::ChildLayerNamesAsString(*root.get())); |
| |
| root->StackAtBottom(l3.get()); |
| EXPECT_EQ("3 2 1", test::ChildLayerNamesAsString(*root.get())); |
| |
| root->StackAtBottom(l3.get()); |
| EXPECT_EQ("3 2 1", test::ChildLayerNamesAsString(*root.get())); |
| |
| root->StackBelow(l2.get(), l3.get()); |
| EXPECT_EQ("2 3 1", test::ChildLayerNamesAsString(*root.get())); |
| |
| root->StackBelow(l1.get(), l3.get()); |
| EXPECT_EQ("2 1 3", test::ChildLayerNamesAsString(*root.get())); |
| |
| root->StackBelow(l3.get(), l2.get()); |
| EXPECT_EQ("3 2 1", test::ChildLayerNamesAsString(*root.get())); |
| |
| root->StackBelow(l3.get(), l2.get()); |
| EXPECT_EQ("3 2 1", test::ChildLayerNamesAsString(*root.get())); |
| |
| root->StackBelow(l3.get(), l1.get()); |
| EXPECT_EQ("2 3 1", test::ChildLayerNamesAsString(*root.get())); |
| } |
| |
| // Verifies SetBounds triggers the appropriate painting/drawing. |
| TEST_F(LayerWithNullDelegateTest, SetBoundsSchedulesPaint) { |
| scoped_ptr<Layer> l1(CreateTextureLayer(gfx::Rect(0, 0, 200, 200))); |
| compositor()->SetRootLayer(l1.get()); |
| |
| Draw(); |
| |
| l1->SetBounds(gfx::Rect(5, 5, 200, 200)); |
| |
| // The CompositorDelegate (us) should have been told to draw for a move. |
| WaitForDraw(); |
| |
| l1->SetBounds(gfx::Rect(5, 5, 100, 100)); |
| |
| // The CompositorDelegate (us) should have been told to draw for a resize. |
| WaitForDraw(); |
| } |
| |
| // Checks that pixels are actually drawn to the screen with a read back. |
| TEST_F(LayerWithRealCompositorTest, DrawPixels) { |
| gfx::Size viewport_size = GetCompositor()->size(); |
| |
| // The window should be some non-trivial size but may not be exactly |
| // 500x500 on all platforms/bots. |
| EXPECT_GE(viewport_size.width(), 200); |
| EXPECT_GE(viewport_size.height(), 200); |
| |
| int blue_height = 10; |
| |
| scoped_ptr<Layer> layer( |
| CreateColorLayer(SK_ColorRED, gfx::Rect(viewport_size))); |
| scoped_ptr<Layer> layer2( |
| CreateColorLayer(SK_ColorBLUE, |
| gfx::Rect(0, 0, viewport_size.width(), blue_height))); |
| |
| layer->Add(layer2.get()); |
| |
| DrawTree(layer.get()); |
| |
| SkBitmap bitmap; |
| ASSERT_TRUE(GetCompositor()->ReadPixels(&bitmap, gfx::Rect(viewport_size))); |
| ASSERT_FALSE(bitmap.empty()); |
| |
| SkAutoLockPixels lock(bitmap); |
| for (int x = 0; x < viewport_size.width(); x++) { |
| for (int y = 0; y < viewport_size.height(); y++) { |
| SkColor actual_color = bitmap.getColor(x, y); |
| SkColor expected_color = y < blue_height ? SK_ColorBLUE : SK_ColorRED; |
| EXPECT_EQ(expected_color, actual_color) |
| << "Pixel error at x=" << x << " y=" << y << "; " |
| << "actual RGBA=(" |
| << SkColorGetR(actual_color) << "," |
| << SkColorGetG(actual_color) << "," |
| << SkColorGetB(actual_color) << "," |
| << SkColorGetA(actual_color) << "); " |
| << "expected RGBA=(" |
| << SkColorGetR(expected_color) << "," |
| << SkColorGetG(expected_color) << "," |
| << SkColorGetB(expected_color) << "," |
| << SkColorGetA(expected_color) << ")"; |
| } |
| } |
| } |
| |
| // Checks the logic around Compositor::SetRootLayer and Layer::SetCompositor. |
| TEST_F(LayerWithRealCompositorTest, SetRootLayer) { |
| Compositor* compositor = GetCompositor(); |
| scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorRED, |
| gfx::Rect(20, 20, 400, 400))); |
| scoped_ptr<Layer> l2(CreateColorLayer(SK_ColorBLUE, |
| gfx::Rect(10, 10, 350, 350))); |
| |
| EXPECT_EQ(NULL, l1->GetCompositor()); |
| EXPECT_EQ(NULL, l2->GetCompositor()); |
| |
| compositor->SetRootLayer(l1.get()); |
| EXPECT_EQ(compositor, l1->GetCompositor()); |
| |
| l1->Add(l2.get()); |
| EXPECT_EQ(compositor, l2->GetCompositor()); |
| |
| l1->Remove(l2.get()); |
| EXPECT_EQ(NULL, l2->GetCompositor()); |
| |
| l1->Add(l2.get()); |
| EXPECT_EQ(compositor, l2->GetCompositor()); |
| |
| compositor->SetRootLayer(NULL); |
| EXPECT_EQ(NULL, l1->GetCompositor()); |
| EXPECT_EQ(NULL, l2->GetCompositor()); |
| } |
| |
| // Checks that compositor observers are notified when: |
| // - DrawTree is called, |
| // - After ScheduleDraw is called, or |
| // - Whenever SetBounds, SetOpacity or SetTransform are called. |
| // TODO(vollick): could be reorganized into compositor_unittest.cc |
| TEST_F(LayerWithRealCompositorTest, CompositorObservers) { |
| scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorRED, |
| gfx::Rect(20, 20, 400, 400))); |
| scoped_ptr<Layer> l2(CreateColorLayer(SK_ColorBLUE, |
| gfx::Rect(10, 10, 350, 350))); |
| l1->Add(l2.get()); |
| TestCompositorObserver observer; |
| GetCompositor()->AddObserver(&observer); |
| |
| // Explicitly called DrawTree should cause the observers to be notified. |
| // NOTE: this call to DrawTree sets l1 to be the compositor's root layer. |
| DrawTree(l1.get()); |
| EXPECT_TRUE(observer.notified()); |
| |
| // ScheduleDraw without any visible change should cause a commit. |
| observer.Reset(); |
| l1->ScheduleDraw(); |
| WaitForCommit(); |
| EXPECT_TRUE(observer.committed()); |
| |
| // Moving, but not resizing, a layer should alert the observers. |
| observer.Reset(); |
| l2->SetBounds(gfx::Rect(0, 0, 350, 350)); |
| WaitForDraw(); |
| EXPECT_TRUE(observer.notified()); |
| |
| // So should resizing a layer. |
| observer.Reset(); |
| l2->SetBounds(gfx::Rect(0, 0, 400, 400)); |
| WaitForDraw(); |
| EXPECT_TRUE(observer.notified()); |
| |
| // Opacity changes should alert the observers. |
| observer.Reset(); |
| l2->SetOpacity(0.5f); |
| WaitForDraw(); |
| EXPECT_TRUE(observer.notified()); |
| |
| // So should setting the opacity back. |
| observer.Reset(); |
| l2->SetOpacity(1.0f); |
| WaitForDraw(); |
| EXPECT_TRUE(observer.notified()); |
| |
| // Setting the transform of a layer should alert the observers. |
| observer.Reset(); |
| gfx::Transform transform; |
| transform.Translate(200.0, 200.0); |
| transform.Rotate(90.0); |
| transform.Translate(-200.0, -200.0); |
| l2->SetTransform(transform); |
| WaitForDraw(); |
| EXPECT_TRUE(observer.notified()); |
| |
| // A change resulting in an aborted swap buffer should alert the observer |
| // and also signal an abort. |
| observer.Reset(); |
| l2->SetOpacity(0.1f); |
| GetCompositor()->DidAbortSwapBuffers(); |
| WaitForDraw(); |
| EXPECT_TRUE(observer.notified()); |
| EXPECT_TRUE(observer.aborted()); |
| |
| GetCompositor()->RemoveObserver(&observer); |
| |
| // Opacity changes should no longer alert the removed observer. |
| observer.Reset(); |
| l2->SetOpacity(0.5f); |
| WaitForDraw(); |
| |
| EXPECT_FALSE(observer.notified()); |
| } |
| |
| // Checks that modifying the hierarchy correctly affects final composite. |
| TEST_F(LayerWithRealCompositorTest, ModifyHierarchy) { |
| GetCompositor()->SetScaleAndSize(1.0f, gfx::Size(50, 50)); |
| |
| // l0 |
| // +-l11 |
| // | +-l21 |
| // +-l12 |
| scoped_ptr<Layer> l0(CreateColorLayer(SK_ColorRED, |
| gfx::Rect(0, 0, 50, 50))); |
| scoped_ptr<Layer> l11(CreateColorLayer(SK_ColorGREEN, |
| gfx::Rect(0, 0, 25, 25))); |
| scoped_ptr<Layer> l21(CreateColorLayer(SK_ColorMAGENTA, |
| gfx::Rect(0, 0, 15, 15))); |
| scoped_ptr<Layer> l12(CreateColorLayer(SK_ColorBLUE, |
| gfx::Rect(10, 10, 25, 25))); |
| |
| base::FilePath ref_img1 = |
| test_data_directory().AppendASCII("ModifyHierarchy1.png"); |
| base::FilePath ref_img2 = |
| test_data_directory().AppendASCII("ModifyHierarchy2.png"); |
| SkBitmap bitmap; |
| |
| l0->Add(l11.get()); |
| l11->Add(l21.get()); |
| l0->Add(l12.get()); |
| DrawTree(l0.get()); |
| ASSERT_TRUE(ReadPixels(&bitmap)); |
| ASSERT_FALSE(bitmap.empty()); |
| // WritePNGFile(bitmap, ref_img1); |
| EXPECT_TRUE(MatchesPNGFile(bitmap, ref_img1, cc::ExactPixelComparator(true))); |
| |
| l0->StackAtTop(l11.get()); |
| DrawTree(l0.get()); |
| ASSERT_TRUE(ReadPixels(&bitmap)); |
| ASSERT_FALSE(bitmap.empty()); |
| // WritePNGFile(bitmap, ref_img2); |
| EXPECT_TRUE(MatchesPNGFile(bitmap, ref_img2, cc::ExactPixelComparator(true))); |
| |
| // should restore to original configuration |
| l0->StackAbove(l12.get(), l11.get()); |
| DrawTree(l0.get()); |
| ASSERT_TRUE(ReadPixels(&bitmap)); |
| ASSERT_FALSE(bitmap.empty()); |
| EXPECT_TRUE(MatchesPNGFile(bitmap, ref_img1, cc::ExactPixelComparator(true))); |
| |
| // l11 back to front |
| l0->StackAtTop(l11.get()); |
| DrawTree(l0.get()); |
| ASSERT_TRUE(ReadPixels(&bitmap)); |
| ASSERT_FALSE(bitmap.empty()); |
| EXPECT_TRUE(MatchesPNGFile(bitmap, ref_img2, cc::ExactPixelComparator(true))); |
| |
| // should restore to original configuration |
| l0->StackAbove(l12.get(), l11.get()); |
| DrawTree(l0.get()); |
| ASSERT_TRUE(ReadPixels(&bitmap)); |
| ASSERT_FALSE(bitmap.empty()); |
| EXPECT_TRUE(MatchesPNGFile(bitmap, ref_img1, cc::ExactPixelComparator(true))); |
| |
| // l11 back to front |
| l0->StackAbove(l11.get(), l12.get()); |
| DrawTree(l0.get()); |
| ASSERT_TRUE(ReadPixels(&bitmap)); |
| ASSERT_FALSE(bitmap.empty()); |
| EXPECT_TRUE(MatchesPNGFile(bitmap, ref_img2, cc::ExactPixelComparator(true))); |
| } |
| |
| // Opacity is rendered correctly. |
| // Checks that modifying the hierarchy correctly affects final composite. |
| TEST_F(LayerWithRealCompositorTest, Opacity) { |
| GetCompositor()->SetScaleAndSize(1.0f, gfx::Size(50, 50)); |
| |
| // l0 |
| // +-l11 |
| scoped_ptr<Layer> l0(CreateColorLayer(SK_ColorRED, |
| gfx::Rect(0, 0, 50, 50))); |
| scoped_ptr<Layer> l11(CreateColorLayer(SK_ColorGREEN, |
| gfx::Rect(0, 0, 25, 25))); |
| |
| base::FilePath ref_img = test_data_directory().AppendASCII("Opacity.png"); |
| |
| l11->SetOpacity(0.75); |
| l0->Add(l11.get()); |
| DrawTree(l0.get()); |
| SkBitmap bitmap; |
| ASSERT_TRUE(ReadPixels(&bitmap)); |
| ASSERT_FALSE(bitmap.empty()); |
| // WritePNGFile(bitmap, ref_img); |
| EXPECT_TRUE(MatchesPNGFile(bitmap, ref_img, cc::ExactPixelComparator(true))); |
| } |
| |
| namespace { |
| |
| class SchedulePaintLayerDelegate : public LayerDelegate { |
| public: |
| SchedulePaintLayerDelegate() : paint_count_(0), layer_(NULL) {} |
| |
| virtual ~SchedulePaintLayerDelegate() {} |
| |
| void set_layer(Layer* layer) { |
| layer_ = layer; |
| layer_->set_delegate(this); |
| } |
| |
| void SetSchedulePaintRect(const gfx::Rect& rect) { |
| schedule_paint_rect_ = rect; |
| } |
| |
| int GetPaintCountAndClear() { |
| int value = paint_count_; |
| paint_count_ = 0; |
| return value; |
| } |
| |
| const gfx::RectF& last_clip_rect() const { return last_clip_rect_; } |
| |
| private: |
| // Overridden from LayerDelegate: |
| virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE { |
| paint_count_++; |
| if (!schedule_paint_rect_.IsEmpty()) { |
| layer_->SchedulePaint(schedule_paint_rect_); |
| schedule_paint_rect_ = gfx::Rect(); |
| } |
| SkRect sk_clip_rect; |
| if (canvas->sk_canvas()->getClipBounds(&sk_clip_rect)) |
| last_clip_rect_ = gfx::SkRectToRectF(sk_clip_rect); |
| } |
| |
| virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE { |
| } |
| |
| virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE { |
| return base::Closure(); |
| } |
| |
| int paint_count_; |
| Layer* layer_; |
| gfx::Rect schedule_paint_rect_; |
| gfx::RectF last_clip_rect_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SchedulePaintLayerDelegate); |
| }; |
| |
| } // namespace |
| |
| // Verifies that if SchedulePaint is invoked during painting the layer is still |
| // marked dirty. |
| TEST_F(LayerWithDelegateTest, SchedulePaintFromOnPaintLayer) { |
| scoped_ptr<Layer> root(CreateColorLayer(SK_ColorRED, |
| gfx::Rect(0, 0, 500, 500))); |
| SchedulePaintLayerDelegate child_delegate; |
| scoped_ptr<Layer> child(CreateColorLayer(SK_ColorBLUE, |
| gfx::Rect(0, 0, 200, 200))); |
| child_delegate.set_layer(child.get()); |
| |
| root->Add(child.get()); |
| |
| SchedulePaintForLayer(root.get()); |
| DrawTree(root.get()); |
| child->SchedulePaint(gfx::Rect(0, 0, 20, 20)); |
| EXPECT_EQ(1, child_delegate.GetPaintCountAndClear()); |
| |
| // Set a rect so that when OnPaintLayer() is invoked SchedulePaint is invoked |
| // again. |
| child_delegate.SetSchedulePaintRect(gfx::Rect(10, 10, 30, 30)); |
| WaitForCommit(); |
| EXPECT_EQ(1, child_delegate.GetPaintCountAndClear()); |
| |
| // Because SchedulePaint() was invoked from OnPaintLayer() |child| should |
| // still need to be painted. |
| WaitForCommit(); |
| EXPECT_EQ(1, child_delegate.GetPaintCountAndClear()); |
| EXPECT_TRUE(child_delegate.last_clip_rect().Contains( |
| gfx::Rect(10, 10, 30, 30))); |
| } |
| |
| TEST_F(LayerWithRealCompositorTest, ScaleUpDown) { |
| scoped_ptr<Layer> root(CreateColorLayer(SK_ColorWHITE, |
| gfx::Rect(10, 20, 200, 220))); |
| TestLayerDelegate root_delegate; |
| root_delegate.AddColor(SK_ColorWHITE); |
| root->set_delegate(&root_delegate); |
| |
| scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorWHITE, |
| gfx::Rect(10, 20, 140, 180))); |
| TestLayerDelegate l1_delegate; |
| l1_delegate.AddColor(SK_ColorWHITE); |
| l1->set_delegate(&l1_delegate); |
| |
| GetCompositor()->SetScaleAndSize(1.0f, gfx::Size(500, 500)); |
| GetCompositor()->SetRootLayer(root.get()); |
| root->Add(l1.get()); |
| WaitForDraw(); |
| |
| EXPECT_EQ("10,20 200x220", root->bounds().ToString()); |
| EXPECT_EQ("10,20 140x180", l1->bounds().ToString()); |
| gfx::Size size_in_pixel = root->cc_layer()->bounds(); |
| EXPECT_EQ("200x220", size_in_pixel.ToString()); |
| size_in_pixel = l1->cc_layer()->bounds(); |
| EXPECT_EQ("140x180", size_in_pixel.ToString()); |
| // No scale change, so no scale notification. |
| EXPECT_EQ(0.0f, root_delegate.device_scale_factor()); |
| EXPECT_EQ(0.0f, l1_delegate.device_scale_factor()); |
| |
| EXPECT_EQ("200x220", root_delegate.paint_size().ToString()); |
| EXPECT_EQ("140x180", l1_delegate.paint_size().ToString()); |
| |
| // Scale up to 2.0. Changing scale doesn't change the bounds in DIP. |
| GetCompositor()->SetScaleAndSize(2.0f, gfx::Size(500, 500)); |
| EXPECT_EQ("10,20 200x220", root->bounds().ToString()); |
| EXPECT_EQ("10,20 140x180", l1->bounds().ToString()); |
| // Pixel size must have been scaled up. |
| size_in_pixel = root->cc_layer()->bounds(); |
| EXPECT_EQ("400x440", size_in_pixel.ToString()); |
| size_in_pixel = l1->cc_layer()->bounds(); |
| EXPECT_EQ("280x360", size_in_pixel.ToString()); |
| // New scale factor must have been notified. |
| EXPECT_EQ(2.0f, root_delegate.device_scale_factor()); |
| EXPECT_EQ(2.0f, l1_delegate.device_scale_factor()); |
| |
| // Canvas size must have been scaled down up. |
| WaitForDraw(); |
| EXPECT_EQ("400x440", root_delegate.paint_size().ToString()); |
| EXPECT_EQ("2.0 2.0", root_delegate.ToScaleString()); |
| EXPECT_EQ("280x360", l1_delegate.paint_size().ToString()); |
| EXPECT_EQ("2.0 2.0", l1_delegate.ToScaleString()); |
| |
| // Scale down back to 1.0f. |
| GetCompositor()->SetScaleAndSize(1.0f, gfx::Size(500, 500)); |
| EXPECT_EQ("10,20 200x220", root->bounds().ToString()); |
| EXPECT_EQ("10,20 140x180", l1->bounds().ToString()); |
| // Pixel size must have been scaled down. |
| size_in_pixel = root->cc_layer()->bounds(); |
| EXPECT_EQ("200x220", size_in_pixel.ToString()); |
| size_in_pixel = l1->cc_layer()->bounds(); |
| EXPECT_EQ("140x180", size_in_pixel.ToString()); |
| // New scale factor must have been notified. |
| EXPECT_EQ(1.0f, root_delegate.device_scale_factor()); |
| EXPECT_EQ(1.0f, l1_delegate.device_scale_factor()); |
| |
| // Canvas size must have been scaled down too. |
| WaitForDraw(); |
| EXPECT_EQ("200x220", root_delegate.paint_size().ToString()); |
| EXPECT_EQ("1.0 1.0", root_delegate.ToScaleString()); |
| EXPECT_EQ("140x180", l1_delegate.paint_size().ToString()); |
| EXPECT_EQ("1.0 1.0", l1_delegate.ToScaleString()); |
| |
| root_delegate.reset(); |
| l1_delegate.reset(); |
| // Just changing the size shouldn't notify the scale change nor |
| // trigger repaint. |
| GetCompositor()->SetScaleAndSize(1.0f, gfx::Size(1000, 1000)); |
| // No scale change, so no scale notification. |
| EXPECT_EQ(0.0f, root_delegate.device_scale_factor()); |
| EXPECT_EQ(0.0f, l1_delegate.device_scale_factor()); |
| WaitForDraw(); |
| EXPECT_EQ("0x0", root_delegate.paint_size().ToString()); |
| EXPECT_EQ("0.0 0.0", root_delegate.ToScaleString()); |
| EXPECT_EQ("0x0", l1_delegate.paint_size().ToString()); |
| EXPECT_EQ("0.0 0.0", l1_delegate.ToScaleString()); |
| } |
| |
| TEST_F(LayerWithRealCompositorTest, ScaleReparent) { |
| scoped_ptr<Layer> root(CreateColorLayer(SK_ColorWHITE, |
| gfx::Rect(10, 20, 200, 220))); |
| scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorWHITE, |
| gfx::Rect(10, 20, 140, 180))); |
| TestLayerDelegate l1_delegate; |
| l1_delegate.AddColor(SK_ColorWHITE); |
| l1->set_delegate(&l1_delegate); |
| |
| GetCompositor()->SetScaleAndSize(1.0f, gfx::Size(500, 500)); |
| GetCompositor()->SetRootLayer(root.get()); |
| WaitForDraw(); |
| |
| root->Add(l1.get()); |
| EXPECT_EQ("10,20 140x180", l1->bounds().ToString()); |
| gfx::Size size_in_pixel = l1->cc_layer()->bounds(); |
| EXPECT_EQ("140x180", size_in_pixel.ToString()); |
| EXPECT_EQ(0.0f, l1_delegate.device_scale_factor()); |
| |
| WaitForDraw(); |
| EXPECT_EQ("140x180", l1_delegate.paint_size().ToString()); |
| EXPECT_EQ("1.0 1.0", l1_delegate.ToScaleString()); |
| |
| // Remove l1 from root and change the scale. |
| root->Remove(l1.get()); |
| EXPECT_EQ(NULL, l1->parent()); |
| EXPECT_EQ(NULL, l1->GetCompositor()); |
| GetCompositor()->SetScaleAndSize(2.0f, gfx::Size(500, 500)); |
| // Sanity check on root and l1. |
| EXPECT_EQ("10,20 200x220", root->bounds().ToString()); |
| size_in_pixel = l1->cc_layer()->bounds(); |
| EXPECT_EQ("140x180", size_in_pixel.ToString()); |
| |
| |
| root->Add(l1.get()); |
| EXPECT_EQ("10,20 140x180", l1->bounds().ToString()); |
| size_in_pixel = l1->cc_layer()->bounds(); |
| EXPECT_EQ("280x360", size_in_pixel.ToString()); |
| EXPECT_EQ(2.0f, l1_delegate.device_scale_factor()); |
| WaitForDraw(); |
| EXPECT_EQ("280x360", l1_delegate.paint_size().ToString()); |
| EXPECT_EQ("2.0 2.0", l1_delegate.ToScaleString()); |
| } |
| |
| // Tests layer::set_scale_content(false). |
| TEST_F(LayerWithRealCompositorTest, NoScaleCanvas) { |
| scoped_ptr<Layer> root(CreateColorLayer(SK_ColorWHITE, |
| gfx::Rect(10, 20, 200, 220))); |
| scoped_ptr<Layer> l1(CreateColorLayer(SK_ColorWHITE, |
| gfx::Rect(10, 20, 140, 180))); |
| l1->set_scale_content(false); |
| root->Add(l1.get()); |
| TestLayerDelegate l1_delegate; |
| l1_delegate.AddColor(SK_ColorWHITE); |
| l1->set_delegate(&l1_delegate); |
| |
| GetCompositor()->SetScaleAndSize(2.0f, gfx::Size(500, 500)); |
| GetCompositor()->SetRootLayer(root.get()); |
| // Scale factor change is notified regardless of scale_content flag. |
| EXPECT_EQ(2.0f, l1_delegate.device_scale_factor()); |
| |
| WaitForDraw(); |
| EXPECT_EQ("280x360", l1_delegate.paint_size().ToString()); |
| EXPECT_EQ("1.0 1.0", l1_delegate.ToScaleString()); |
| } |
| |
| // Verifies that when changing bounds on a layer that is invisible, and then |
| // made visible, the right thing happens: |
| // - if just a move, then no painting should happen. |
| // - if a resize, the layer should be repainted. |
| TEST_F(LayerWithDelegateTest, SetBoundsWhenInvisible) { |
| scoped_ptr<Layer> root(CreateNoTextureLayer(gfx::Rect(0, 0, 1000, 1000))); |
| |
| scoped_ptr<Layer> child(CreateLayer(LAYER_TEXTURED)); |
| child->SetBounds(gfx::Rect(0, 0, 500, 500)); |
| DrawTreeLayerDelegate delegate; |
| child->set_delegate(&delegate); |
| root->Add(child.get()); |
| |
| // Paint once for initial damage. |
| child->SetVisible(true); |
| DrawTree(root.get()); |
| |
| // Reset into invisible state. |
| child->SetVisible(false); |
| DrawTree(root.get()); |
| delegate.Reset(); |
| |
| // Move layer. |
| child->SetBounds(gfx::Rect(200, 200, 500, 500)); |
| child->SetVisible(true); |
| DrawTree(root.get()); |
| EXPECT_FALSE(delegate.painted()); |
| |
| // Reset into invisible state. |
| child->SetVisible(false); |
| DrawTree(root.get()); |
| delegate.Reset(); |
| |
| // Resize layer. |
| child->SetBounds(gfx::Rect(200, 200, 400, 400)); |
| child->SetVisible(true); |
| DrawTree(root.get()); |
| EXPECT_TRUE(delegate.painted()); |
| } |
| |
| static scoped_ptr<cc::DelegatedFrameData> MakeFrameData(gfx::Size size) { |
| scoped_ptr<cc::DelegatedFrameData> frame_data(new cc::DelegatedFrameData); |
| scoped_ptr<cc::RenderPass> render_pass(cc::RenderPass::Create()); |
| render_pass->SetNew(cc::RenderPass::Id(1, 1), |
| gfx::Rect(size), |
| gfx::RectF(), |
| gfx::Transform()); |
| frame_data->render_pass_list.push_back(render_pass.Pass()); |
| return frame_data.Pass(); |
| } |
| |
| TEST_F(LayerWithDelegateTest, DelegatedLayer) { |
| scoped_ptr<Layer> root(CreateNoTextureLayer(gfx::Rect(0, 0, 1000, 1000))); |
| |
| scoped_ptr<Layer> child(CreateLayer(LAYER_TEXTURED)); |
| |
| child->SetBounds(gfx::Rect(0, 0, 10, 10)); |
| child->SetVisible(true); |
| root->Add(child.get()); |
| DrawTree(root.get()); |
| |
| scoped_refptr<cc::DelegatedFrameResourceCollection> resource_collection = |
| new cc::DelegatedFrameResourceCollection; |
| scoped_refptr<cc::DelegatedFrameProvider> frame_provider; |
| |
| // Content matches layer size. |
| frame_provider = new cc::DelegatedFrameProvider( |
| resource_collection.get(), MakeFrameData(gfx::Size(10, 10))); |
| child->SetShowDelegatedContent(frame_provider, gfx::Size(10, 10)); |
| EXPECT_EQ(child->cc_layer()->bounds().ToString(), |
| gfx::Size(10, 10).ToString()); |
| |
| // Content larger than layer. |
| child->SetBounds(gfx::Rect(0, 0, 5, 5)); |
| EXPECT_EQ(child->cc_layer()->bounds().ToString(), |
| gfx::Size(5, 5).ToString()); |
| |
| // Content smaller than layer. |
| child->SetBounds(gfx::Rect(0, 0, 10, 10)); |
| frame_provider = new cc::DelegatedFrameProvider( |
| resource_collection.get(), MakeFrameData(gfx::Size(5, 5))); |
| child->SetShowDelegatedContent(frame_provider, gfx::Size(5, 5)); |
| EXPECT_EQ(child->cc_layer()->bounds().ToString(), gfx::Size(5, 5).ToString()); |
| |
| // Hi-DPI content on low-DPI layer. |
| frame_provider = new cc::DelegatedFrameProvider( |
| resource_collection.get(), MakeFrameData(gfx::Size(20, 20))); |
| child->SetShowDelegatedContent(frame_provider, gfx::Size(10, 10)); |
| EXPECT_EQ(child->cc_layer()->bounds().ToString(), |
| gfx::Size(10, 10).ToString()); |
| |
| // Hi-DPI content on hi-DPI layer. |
| compositor()->SetScaleAndSize(2.f, gfx::Size(1000, 1000)); |
| EXPECT_EQ(child->cc_layer()->bounds().ToString(), |
| gfx::Size(20, 20).ToString()); |
| |
| // Low-DPI content on hi-DPI layer. |
| frame_provider = new cc::DelegatedFrameProvider( |
| resource_collection.get(), MakeFrameData(gfx::Size(10, 10))); |
| child->SetShowDelegatedContent(frame_provider, gfx::Size(10, 10)); |
| EXPECT_EQ(child->cc_layer()->bounds().ToString(), |
| gfx::Size(20, 20).ToString()); |
| } |
| |
| TEST_F(LayerWithDelegateTest, ExternalContent) { |
| scoped_ptr<Layer> root(CreateNoTextureLayer(gfx::Rect(0, 0, 1000, 1000))); |
| scoped_ptr<Layer> child(CreateLayer(LAYER_TEXTURED)); |
| |
| child->SetBounds(gfx::Rect(0, 0, 10, 10)); |
| child->SetVisible(true); |
| root->Add(child.get()); |
| |
| // The layer is already showing painted content, so the cc layer won't change. |
| scoped_refptr<cc::Layer> before = child->cc_layer(); |
| child->SetShowPaintedContent(); |
| EXPECT_TRUE(child->cc_layer()); |
| EXPECT_EQ(before, child->cc_layer()); |
| |
| scoped_refptr<cc::DelegatedFrameResourceCollection> resource_collection = |
| new cc::DelegatedFrameResourceCollection; |
| scoped_refptr<cc::DelegatedFrameProvider> frame_provider = |
| new cc::DelegatedFrameProvider(resource_collection.get(), |
| MakeFrameData(gfx::Size(10, 10))); |
| |
| // Showing delegated content changes the underlying cc layer. |
| before = child->cc_layer(); |
| child->SetShowDelegatedContent(frame_provider, gfx::Size(10, 10)); |
| EXPECT_TRUE(child->cc_layer()); |
| EXPECT_NE(before, child->cc_layer()); |
| |
| // Changing to painted content should change the underlying cc layer. |
| before = child->cc_layer(); |
| child->SetShowPaintedContent(); |
| EXPECT_TRUE(child->cc_layer()); |
| EXPECT_NE(before, child->cc_layer()); |
| } |
| |
| // Tests Layer::AddThreadedAnimation and Layer::RemoveThreadedAnimation. |
| TEST_F(LayerWithRealCompositorTest, AddRemoveThreadedAnimations) { |
| scoped_ptr<Layer> root(CreateLayer(LAYER_TEXTURED)); |
| scoped_ptr<Layer> l1(CreateLayer(LAYER_TEXTURED)); |
| scoped_ptr<Layer> l2(CreateLayer(LAYER_TEXTURED)); |
| |
| l1->SetAnimator(LayerAnimator::CreateImplicitAnimator()); |
| l2->SetAnimator(LayerAnimator::CreateImplicitAnimator()); |
| |
| EXPECT_FALSE(l1->HasPendingThreadedAnimations()); |
| |
| // Trigger a threaded animation. |
| l1->SetOpacity(0.5f); |
| |
| EXPECT_TRUE(l1->HasPendingThreadedAnimations()); |
| |
| // Ensure we can remove a pending threaded animation. |
| l1->GetAnimator()->StopAnimating(); |
| |
| EXPECT_FALSE(l1->HasPendingThreadedAnimations()); |
| |
| // Trigger another threaded animation. |
| l1->SetOpacity(0.2f); |
| |
| EXPECT_TRUE(l1->HasPendingThreadedAnimations()); |
| |
| root->Add(l1.get()); |
| GetCompositor()->SetRootLayer(root.get()); |
| |
| // Now that l1 is part of a tree, it should have dispatched the pending |
| // animation. |
| EXPECT_FALSE(l1->HasPendingThreadedAnimations()); |
| |
| // Ensure that l1 no longer holds on to animations. |
| l1->SetOpacity(0.1f); |
| EXPECT_FALSE(l1->HasPendingThreadedAnimations()); |
| |
| // Ensure that adding a layer to an existing tree causes its pending |
| // animations to get dispatched. |
| l2->SetOpacity(0.5f); |
| EXPECT_TRUE(l2->HasPendingThreadedAnimations()); |
| |
| l1->Add(l2.get()); |
| EXPECT_FALSE(l2->HasPendingThreadedAnimations()); |
| } |
| |
| // Tests that in-progress threaded animations complete when a Layer's |
| // cc::Layer changes. |
| TEST_F(LayerWithRealCompositorTest, SwitchCCLayerAnimations) { |
| scoped_ptr<Layer> root(CreateLayer(LAYER_TEXTURED)); |
| scoped_ptr<Layer> l1(CreateLayer(LAYER_TEXTURED)); |
| GetCompositor()->SetRootLayer(root.get()); |
| root->Add(l1.get()); |
| |
| l1->SetAnimator(LayerAnimator::CreateImplicitAnimator()); |
| |
| EXPECT_FLOAT_EQ(l1->opacity(), 1.0f); |
| |
| // Trigger a threaded animation. |
| l1->SetOpacity(0.5f); |
| |
| // Change l1's cc::Layer. |
| l1->SwitchCCLayerForTest(); |
| |
| // Ensure that the opacity animation completed. |
| EXPECT_FLOAT_EQ(l1->opacity(), 0.5f); |
| } |
| |
| } // namespace ui |