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"
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"
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
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
66 gfx::Display::Rotation
GetCurrentRotation(int64 display_id
) {
67 return Shell::GetInstance()
69 ->GetDisplayInfo(display_id
)
73 // Returns true if the rotation between |initial_rotation| and |new_rotation| is
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
{
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
{}
98 // ui::LayerAnimationObserver:
99 bool RequiresNotificationWhenAnimatorDestroyed() const override
{
102 void OnAttachedToSequence(ui::LayerAnimationSequence
* sequence
) override
;
103 void OnDetachedFromSequence(ui::LayerAnimationSequence
* sequence
) override
;
106 // Aborts the active animations of the layer, and recurses upon its child
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
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.
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
) {
142 void LayerCleanupObserver::OnLayerAnimationAborted(
143 ui::LayerAnimationSequence
* sequence
) {
147 void LayerCleanupObserver::OnAttachedToSequence(
148 ui::LayerAnimationSequence
* sequence
) {
149 sequence_
= sequence
;
152 void LayerCleanupObserver::OnDetachedFromSequence(
153 ui::LayerAnimationSequence
* sequence
) {
154 DCHECK_EQ(sequence
, sequence_
);
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
,
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::RectF 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::RectF 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
);
207 new_layer_initial_scale
= gfx::Point3F(
208 original_screen_bounds
.width() / rotated_screen_bounds
.width(),
209 original_screen_bounds
.height() / rotated_screen_bounds
.height(), 1.0f
);
211 old_layer_target_scale
= gfx::Point3F(
212 rotated_screen_bounds
.width() / original_screen_bounds
.width(),
213 rotated_screen_bounds
.height() / original_screen_bounds
.height(), 1.0f
);
216 // We must animate each non-cloned child layer individually because the cloned
217 // layer was added as a child to |root_window|'s layer so that it will be
219 // TODO(bruthig): Add a NOT_DRAWN layer in between the root_window's layer and
220 // its current children so that we only need to initiate two
221 // LayerAnimationSequences. One for the new layers and one for the old layer.
222 for (ui::Layer
* child_layer
: root_window
->layer()->children()) {
223 // Skip the cloned layer because it has a different animation.
224 if (child_layer
== layer_cleanup_observer
->GetRootLayer())
227 scoped_ptr
<ScreenRotationAnimation
> screen_rotation(
228 new ScreenRotationAnimation(
229 child_layer
, rotation_degrees
* rotation_factor
,
230 0 /* end_degrees */, child_layer
->opacity(),
231 1.0f
/* target_opacity */, new_layer_initial_scale
,
232 gfx::Point3F(1.0f
, 1.0f
, 1.0f
), pivot
, duration
, tween_type
));
234 ui::LayerAnimator
* animator
= child_layer
->GetAnimator();
235 animator
->set_preemption_strategy(
236 ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS
);
237 scoped_ptr
<ui::LayerAnimationSequence
> animation_sequence(
238 new ui::LayerAnimationSequence(screen_rotation
.release()));
239 animator
->StartAnimation(animation_sequence
.release());
242 // The old layer will also be transformed into the new orientation. We will
243 // translate it so that the old layer's center point aligns with the new
244 // orientation's center point and use that center point as the pivot for the
245 // rotation animation.
246 gfx::Transform translate_transform
;
247 translate_transform
.Translate(
248 (rotated_screen_bounds
.width() - original_screen_bounds
.width()) / 2,
249 (rotated_screen_bounds
.height() - original_screen_bounds
.height()) / 2);
250 layer_cleanup_observer
->GetRootLayer()->SetTransform(translate_transform
);
252 scoped_ptr
<ScreenRotationAnimation
> screen_rotation(
253 new ScreenRotationAnimation(
254 layer_cleanup_observer
->GetRootLayer(),
255 (rotation_degrees
+ rotation_degree_offset
) * rotation_factor
,
256 rotation_degree_offset
* rotation_factor
,
257 layer_cleanup_observer
->GetRootLayer()->opacity(),
258 0.0f
/* target_opacity */, gfx::Point3F(1.0f
, 1.0f
, 1.0f
),
259 old_layer_target_scale
, pivot
, duration
, tween_type
));
261 ui::LayerAnimator
* animator
=
262 layer_cleanup_observer
->GetRootLayer()->GetAnimator();
263 animator
->set_preemption_strategy(
264 ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS
);
265 scoped_ptr
<ui::LayerAnimationSequence
> animation_sequence(
266 new ui::LayerAnimationSequence(screen_rotation
.release()));
267 // Add an observer so that the cloned layers can be cleaned up with the
268 // animation completes/aborts.
269 animation_sequence
->AddObserver(layer_cleanup_observer
.release());
270 animator
->StartAnimation(animation_sequence
.release());
275 ScreenRotationAnimator::ScreenRotationAnimator(int64 display_id
)
276 : display_id_(display_id
) {
279 ScreenRotationAnimator::~ScreenRotationAnimator() {
282 bool ScreenRotationAnimator::CanAnimate() const {
283 return Shell::GetInstance()
285 ->GetDisplayForId(display_id_
)
289 void ScreenRotationAnimator::Rotate(gfx::Display::Rotation new_rotation
,
290 gfx::Display::RotationSource source
) {
291 const gfx::Display::Rotation current_rotation
=
292 GetCurrentRotation(display_id_
);
294 if (current_rotation
== new_rotation
)
297 const std::string switch_value
=
298 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
299 switches::kAshEnableScreenRotationAnimation
);
301 if (switch_value
== kRotationAnimation_None
) {
302 Shell::GetInstance()->display_manager()->SetDisplayRotation(
303 display_id_
, new_rotation
, source
);
304 } else if (kRotationAnimation_Default
== switch_value
||
305 kRotationAnimation_Partial
== switch_value
) {
306 const int rotation_degree_offset
=
307 Is180DegreeFlip(current_rotation
, new_rotation
)
308 ? 180 - kPartialRotationDegrees
309 : 90 - kPartialRotationDegrees
;
311 RotateScreen(display_id_
, new_rotation
, source
,
312 base::TimeDelta::FromMilliseconds(kRotationDurationInMs
),
313 kPartialRotationDegrees
, rotation_degree_offset
,
314 gfx::Tween::FAST_OUT_LINEAR_IN
, false /* should_scale */);
315 } else if (kRotationAnimation_Full
== switch_value
) {
316 const int rotation_degrees
=
317 Is180DegreeFlip(current_rotation
, new_rotation
) ? 180 : 90;
319 RotateScreen(display_id_
, new_rotation
, source
,
320 base::TimeDelta::FromMilliseconds(kRotationDurationInMs
),
321 rotation_degrees
, 0 /* rotation_degree_offset */,
322 gfx::Tween::FAST_OUT_LINEAR_IN
, true /* should_scale */);