Permission message rules: Each rule must have >= 1 required permissions
[chromium-blink-merge.git] / ash / rotator / screen_rotation_animator.cc
blob677ecba8774bfefb8d5fe33081a9619592989446
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ash/rotator/screen_rotation_animator.h"
7 #include <string>
8 #include <vector>
10 #include "ash/ash_switches.h"
11 #include "ash/display/display_info.h"
12 #include "ash/display/display_manager.h"
13 #include "ash/display/window_tree_host_manager.h"
14 #include "ash/rotator/screen_rotation_animation.h"
15 #include "ash/shell.h"
16 #include "base/command_line.h"
17 #include "base/time/time.h"
18 #include "ui/aura/window.h"
19 #include "ui/compositor/layer.h"
20 #include "ui/compositor/layer_animation_observer.h"
21 #include "ui/compositor/layer_animation_sequence.h"
22 #include "ui/compositor/layer_animator.h"
23 #include "ui/compositor/layer_owner.h"
24 #include "ui/compositor/layer_tree_owner.h"
25 #include "ui/gfx/animation/tween.h"
26 #include "ui/gfx/display.h"
27 #include "ui/gfx/geometry/point.h"
28 #include "ui/gfx/geometry/rect.h"
29 #include "ui/gfx/geometry/rect_f.h"
30 #include "ui/gfx/transform.h"
31 #include "ui/gfx/transform_util.h"
32 #include "ui/wm/core/window_util.h"
34 namespace ash {
36 namespace {
38 // Switch value for the default animation.
39 const char kRotationAnimation_Default[] = "";
41 // Switch value for no animation.
42 const char kRotationAnimation_None[] = "none";
44 // Switch value for an animation that will rotate the initial orientation's
45 // layer towards the new orientation and the new orientation's layer in to
46 // position from the initial orientation through an arc of
47 // |kPartialRotationDegrees| degrees. The initial orientation's layer will be
48 // faded out as well.
49 const char kRotationAnimation_Partial[] = "partial-rotation";
51 // Switch value for an animation that will rotate both the initial and target
52 // orientation's layers from the initial orientation into the target
53 // orientation. The initial orientation's layer will be faded out as well and an
54 // interpolated scaling factor is applied to the animation so that the initial
55 // and target layers are both the same size throughout the animation.
56 const char kRotationAnimation_Full[] = "full-rotation";
58 // The number of degrees the partial rotation animations animate through.
59 const int kPartialRotationDegrees = 20;
61 // The time it takes for the rotation animations to run.
62 const int kRotationDurationInMs = 250;
64 // Gets the current display rotation for the display with the specified
65 // |display_id|.
66 gfx::Display::Rotation GetCurrentRotation(int64 display_id) {
67 return Shell::GetInstance()
68 ->display_manager()
69 ->GetDisplayInfo(display_id)
70 .GetActiveRotation();
73 // Returns true if the rotation between |initial_rotation| and |new_rotation| is
74 // 180 degrees.
75 bool Is180DegreeFlip(gfx::Display::Rotation initial_rotation,
76 gfx::Display::Rotation new_rotation) {
77 return (initial_rotation + 2) % 4 == new_rotation;
80 // A LayerAnimationObserver that will destroy the contained LayerTreeOwner when
81 // notified that a layer animation has ended or was aborted.
82 class LayerCleanupObserver : public ui::LayerAnimationObserver {
83 public:
84 explicit LayerCleanupObserver(
85 scoped_ptr<ui::LayerTreeOwner> layer_tree_owner);
86 ~LayerCleanupObserver() override;
88 // Get the root layer of the owned layer tree.
89 ui::Layer* GetRootLayer();
91 // ui::LayerAnimationObserver:
92 void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override;
93 void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override;
94 void OnLayerAnimationScheduled(
95 ui::LayerAnimationSequence* sequence) override {}
97 protected:
98 // ui::LayerAnimationObserver:
99 bool RequiresNotificationWhenAnimatorDestroyed() const override {
100 return true;
102 void OnAttachedToSequence(ui::LayerAnimationSequence* sequence) override;
103 void OnDetachedFromSequence(ui::LayerAnimationSequence* sequence) override;
105 private:
106 // Aborts the active animations of the layer, and recurses upon its child
107 // layers.
108 void AbortAnimations(ui::Layer* layer);
110 // The owned layer tree.
111 scoped_ptr<ui::LayerTreeOwner> layer_tree_owner_;
113 // The LayerAnimationSequence that |this| has been attached to. Defaults to
114 // nullptr.
115 ui::LayerAnimationSequence* sequence_;
117 DISALLOW_COPY_AND_ASSIGN(LayerCleanupObserver);
120 LayerCleanupObserver::LayerCleanupObserver(
121 scoped_ptr<ui::LayerTreeOwner> layer_tree_owner)
122 : layer_tree_owner_(layer_tree_owner.Pass()), sequence_(nullptr) {
125 LayerCleanupObserver::~LayerCleanupObserver() {
126 // We must eplicitly detach from |sequence_| because we return true from
127 // RequiresNotificationWhenAnimatorDestroyed.
128 if (sequence_)
129 sequence_->RemoveObserver(this);
130 AbortAnimations(layer_tree_owner_->root());
133 ui::Layer* LayerCleanupObserver::GetRootLayer() {
134 return layer_tree_owner_->root();
137 void LayerCleanupObserver::OnLayerAnimationEnded(
138 ui::LayerAnimationSequence* sequence) {
139 delete this;
142 void LayerCleanupObserver::OnLayerAnimationAborted(
143 ui::LayerAnimationSequence* sequence) {
144 delete this;
147 void LayerCleanupObserver::OnAttachedToSequence(
148 ui::LayerAnimationSequence* sequence) {
149 sequence_ = sequence;
152 void LayerCleanupObserver::OnDetachedFromSequence(
153 ui::LayerAnimationSequence* sequence) {
154 DCHECK_EQ(sequence, sequence_);
155 sequence_ = nullptr;
158 void LayerCleanupObserver::AbortAnimations(ui::Layer* layer) {
159 for (ui::Layer* child_layer : layer->children())
160 AbortAnimations(child_layer);
161 layer->GetAnimator()->AbortAllAnimations();
164 // Set the screen orientation for the given |display| to |new_rotation| and
165 // animate the change.
166 void RotateScreen(int64 display_id,
167 gfx::Display::Rotation new_rotation,
168 gfx::Display::RotationSource source,
169 base::TimeDelta duration,
170 int rotation_degrees,
171 int rotation_degree_offset,
172 gfx::Tween::Type tween_type,
173 bool should_scale) {
174 aura::Window* root_window = Shell::GetInstance()
175 ->window_tree_host_manager()
176 ->GetRootWindowForDisplayId(display_id);
178 const gfx::Display::Rotation initial_orientation =
179 GetCurrentRotation(display_id);
181 const gfx::Rect original_screen_bounds = root_window->GetTargetBounds();
182 // 180 degree rotations should animate clock-wise.
183 const int rotation_factor =
184 (initial_orientation + 3) % 4 == new_rotation ? 1 : -1;
186 scoped_ptr<ui::LayerTreeOwner> old_layer_tree =
187 wm::RecreateLayers(root_window);
189 // Add the cloned layer tree in to the root, so it will be rendered.
190 root_window->layer()->Add(old_layer_tree->root());
191 root_window->layer()->StackAtTop(old_layer_tree->root());
193 scoped_ptr<LayerCleanupObserver> layer_cleanup_observer(
194 new LayerCleanupObserver(old_layer_tree.Pass()));
196 Shell::GetInstance()->display_manager()->SetDisplayRotation(
197 display_id, new_rotation, source);
199 const gfx::Rect rotated_screen_bounds = root_window->GetTargetBounds();
200 const gfx::Point pivot = gfx::Point(rotated_screen_bounds.width() / 2,
201 rotated_screen_bounds.height() / 2);
203 gfx::Point3F new_layer_initial_scale = gfx::Point3F(1.0f, 1.0f, 1.0f);
204 gfx::Point3F old_layer_target_scale = gfx::Point3F(1.0f, 1.0f, 1.0f);
206 if (should_scale) {
207 new_layer_initial_scale =
208 gfx::Point3F(static_cast<float>(original_screen_bounds.width()) /
209 rotated_screen_bounds.width(),
210 static_cast<float>(original_screen_bounds.height()) /
211 rotated_screen_bounds.height(),
212 1.0f);
214 old_layer_target_scale =
215 gfx::Point3F(static_cast<float>(rotated_screen_bounds.width()) /
216 original_screen_bounds.width(),
217 static_cast<float>(rotated_screen_bounds.height()) /
218 original_screen_bounds.height(),
219 1.0f);
222 // We must animate each non-cloned child layer individually because the cloned
223 // layer was added as a child to |root_window|'s layer so that it will be
224 // rendered.
225 // TODO(bruthig): Add a NOT_DRAWN layer in between the root_window's layer and
226 // its current children so that we only need to initiate two
227 // LayerAnimationSequences. One for the new layers and one for the old layer.
228 for (ui::Layer* child_layer : root_window->layer()->children()) {
229 // Skip the cloned layer because it has a different animation.
230 if (child_layer == layer_cleanup_observer->GetRootLayer())
231 continue;
233 scoped_ptr<ScreenRotationAnimation> screen_rotation(
234 new ScreenRotationAnimation(
235 child_layer, rotation_degrees * rotation_factor,
236 0 /* end_degrees */, child_layer->opacity(),
237 1.0f /* target_opacity */, new_layer_initial_scale,
238 gfx::Point3F(1.0f, 1.0f, 1.0f), pivot, duration, tween_type));
240 ui::LayerAnimator* animator = child_layer->GetAnimator();
241 animator->set_preemption_strategy(
242 ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
243 scoped_ptr<ui::LayerAnimationSequence> animation_sequence(
244 new ui::LayerAnimationSequence(screen_rotation.release()));
245 animator->StartAnimation(animation_sequence.release());
248 // The old layer will also be transformed into the new orientation. We will
249 // translate it so that the old layer's center point aligns with the new
250 // orientation's center point and use that center point as the pivot for the
251 // rotation animation.
252 gfx::Transform translate_transform;
253 translate_transform.Translate(
254 (rotated_screen_bounds.width() - original_screen_bounds.width()) / 2,
255 (rotated_screen_bounds.height() - original_screen_bounds.height()) / 2);
256 layer_cleanup_observer->GetRootLayer()->SetTransform(translate_transform);
258 scoped_ptr<ScreenRotationAnimation> screen_rotation(
259 new ScreenRotationAnimation(
260 layer_cleanup_observer->GetRootLayer(),
261 (rotation_degrees + rotation_degree_offset) * rotation_factor,
262 rotation_degree_offset * rotation_factor,
263 layer_cleanup_observer->GetRootLayer()->opacity(),
264 0.0f /* target_opacity */, gfx::Point3F(1.0f, 1.0f, 1.0f),
265 old_layer_target_scale, pivot, duration, tween_type));
267 ui::LayerAnimator* animator =
268 layer_cleanup_observer->GetRootLayer()->GetAnimator();
269 animator->set_preemption_strategy(
270 ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
271 scoped_ptr<ui::LayerAnimationSequence> animation_sequence(
272 new ui::LayerAnimationSequence(screen_rotation.release()));
273 // Add an observer so that the cloned layers can be cleaned up with the
274 // animation completes/aborts.
275 animation_sequence->AddObserver(layer_cleanup_observer.release());
276 animator->StartAnimation(animation_sequence.release());
279 } // namespace
281 ScreenRotationAnimator::ScreenRotationAnimator(int64 display_id)
282 : display_id_(display_id) {
285 ScreenRotationAnimator::~ScreenRotationAnimator() {
288 bool ScreenRotationAnimator::CanAnimate() const {
289 return Shell::GetInstance()
290 ->display_manager()
291 ->GetDisplayForId(display_id_)
292 .is_valid();
295 void ScreenRotationAnimator::Rotate(gfx::Display::Rotation new_rotation,
296 gfx::Display::RotationSource source) {
297 const gfx::Display::Rotation current_rotation =
298 GetCurrentRotation(display_id_);
300 if (current_rotation == new_rotation)
301 return;
303 const std::string switch_value =
304 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
305 switches::kAshEnableScreenRotationAnimation);
307 if (switch_value == kRotationAnimation_None) {
308 Shell::GetInstance()->display_manager()->SetDisplayRotation(
309 display_id_, new_rotation, source);
310 } else if (kRotationAnimation_Default == switch_value ||
311 kRotationAnimation_Partial == switch_value) {
312 const int rotation_degree_offset =
313 Is180DegreeFlip(current_rotation, new_rotation)
314 ? 180 - kPartialRotationDegrees
315 : 90 - kPartialRotationDegrees;
317 RotateScreen(display_id_, new_rotation, source,
318 base::TimeDelta::FromMilliseconds(kRotationDurationInMs),
319 kPartialRotationDegrees, rotation_degree_offset,
320 gfx::Tween::FAST_OUT_LINEAR_IN, false /* should_scale */);
321 } else if (kRotationAnimation_Full == switch_value) {
322 const int rotation_degrees =
323 Is180DegreeFlip(current_rotation, new_rotation) ? 180 : 90;
325 RotateScreen(display_id_, new_rotation, source,
326 base::TimeDelta::FromMilliseconds(kRotationDurationInMs),
327 rotation_degrees, 0 /* rotation_degree_offset */,
328 gfx::Tween::FAST_OUT_LINEAR_IN, true /* should_scale */);
329 } else {
330 NOTREACHED();
334 } // namespace ash