Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(496)

Side by Side Diff: ash/rotator/screen_rotation_animator.cc

Issue 975943002: Implemented screen rotation animation experiment. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed outstanding comments from Patch set 1 & 2. Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "ash/rotator/screen_rotation_animator.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "ash/ash_switches.h"
11 #include "ash/display/display_controller.h"
12 #include "ash/display/display_info.h"
13 #include "ash/display/display_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"
33
34 namespace ash {
35
36 namespace {
37
38 // Switch value for an animation that will rotate the initial orientation's
39 // layer towards the new orientation and the new orientation's layer in to
40 // position from the initial orientation through an arc of
41 // |kPartialRotationDegrees| degrees. The initial orientation's layer will be
42 // faded out as well.
43 const char kPartialRotation[] = "partial-rotation";
44
45 // Switch value for the same animation as |kPartialRotation| but with a much
46 // longer duration.
47 const char kPartialRotationSlow[] = "partial-rotation-slow";
48
49 // Switch value for an animation that will rotate both the initial and target
50 // orientation's layers from the initial orientation into the target
51 // orientation. The initial orientation's layer will be faded out as well and an
52 // interpolated scaling factor is applied to the animation so that the initial
53 // and target layers are both the same size throughout the animation.
54 const char kFullRotation[] = "full-rotation";
55
56 // Switch value for the same animation as |kFullRotation| but with a much longer
57 // duration.
58 const char kFullRotationSlow[] = "full-rotation-slow";
59
60 // The number of degrees the partial rotation animations animate through.
61 const int kPartialRotationDegrees = 20;
62
63 // The time it takes for the normal speed rotation animations to run.
64 const int kRotationDurationInMs = 250;
65
66 // The time it takes for the slow speed rotation animations to run.
67 const int kRotationDurationSlowInMs = 2500;
68
69 // Gets the current display rotation for the display with the specified
70 // |display_id|.
71 gfx::Display::Rotation GetCurrentRotation(int64 display_id) {
72 return Shell::GetInstance()
73 ->display_manager()
74 ->GetDisplayInfo(display_id)
75 .rotation();
76 }
77
78 // Returns true if the rotation between |initial_rotation| and |new_rotation| is
79 // 180 degrees.
80 bool Is180DegreeFlip(gfx::Display::Rotation initial_rotation,
81 gfx::Display::Rotation new_rotation) {
82 return (initial_rotation + 2) % 4 == new_rotation;
83 }
84
85 // A LayerAnimationObserver that will destroy the contained LayerTreeOwner when
86 // notified that a layer animation has ended or was aborted.
87 class LayerCleanupObserver : public ui::LayerAnimationObserver {
88 public:
89 explicit LayerCleanupObserver(
90 scoped_ptr<ui::LayerTreeOwner> layer_tree_owner);
91 ~LayerCleanupObserver() override;
92
93 // Get the root layer of the owned layer tree.
94 ui::Layer* GetRootLayer();
95
96 // ui::LayerAnimationObserver:
97 void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override;
98 void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override;
99 void OnLayerAnimationScheduled(
100 ui::LayerAnimationSequence* sequence) override {}
101
102 protected:
103 // ui::LayerAnimationObserver:
104 bool RequiresNotificationWhenAnimatorDestroyed() const override {
105 return true;
106 }
107 void OnAttachedToSequence(ui::LayerAnimationSequence* sequence) override;
108 void OnDetachedFromSequence(ui::LayerAnimationSequence* sequence) override;
109
110 private:
111 // The owned layer tree.
112 scoped_ptr<ui::LayerTreeOwner> layer_tree_owner_;
113
114 // The LayerAnimationSequence that |this| has been attached to. Defaults to
115 // nullptr.
116 ui::LayerAnimationSequence* sequence_;
117
118 DISALLOW_COPY_AND_ASSIGN(LayerCleanupObserver);
119 };
120
121 LayerCleanupObserver::LayerCleanupObserver(
122 scoped_ptr<ui::LayerTreeOwner> layer_tree_owner)
123 : layer_tree_owner_(layer_tree_owner.Pass()), sequence_(nullptr) {
124 }
125
126 LayerCleanupObserver::~LayerCleanupObserver() {
127 // We must eplicitly detach from |sequence_| because we return true from
128 // RequiresNotificationWhenAnimatorDestroyed.
129 if (sequence_)
130 sequence_->RemoveObserver(this);
131 }
132
133 ui::Layer* LayerCleanupObserver::GetRootLayer() {
134 return layer_tree_owner_->root();
135 }
136
137 void LayerCleanupObserver::OnLayerAnimationEnded(
138 ui::LayerAnimationSequence* sequence) {
139 delete this;
140 }
141
142 void LayerCleanupObserver::OnLayerAnimationAborted(
143 ui::LayerAnimationSequence* sequence) {
144 delete this;
145 }
146
147 void LayerCleanupObserver::OnAttachedToSequence(
148 ui::LayerAnimationSequence* sequence) {
149 sequence_ = sequence;
150 }
151
152 void LayerCleanupObserver::OnDetachedFromSequence(
153 ui::LayerAnimationSequence* sequence) {
154 DCHECK(sequence == sequence_);
oshima 2015/03/06 20:59:32 nit: DCHECK_EQ
bruthig 2015/03/09 21:04:27 Done.
155 sequence_ = nullptr;
156 }
157
158 // Set the screen orientation for the given |display| to |new_rotation| and
159 // animate the change.
160 void RotateScreen(gfx::Display display,
161 gfx::Display::Rotation new_rotation,
162 base::TimeDelta duration,
163 int rotation_degrees,
164 int rotation_degree_offset,
165 gfx::Tween::Type tween_type,
166 bool should_scale) {
167 aura::Window* root_window =
168 Shell::GetInstance()->display_controller()->GetRootWindowForDisplayId(
169 display.id());
170
171 const gfx::Display::Rotation initial_orientation =
172 GetCurrentRotation(display.id());
173
174 const gfx::RectF original_screen_bounds = root_window->GetTargetBounds();
175 // 180 degree rotations should animate clock-wise.
176 const int rotation_factor =
177 (initial_orientation + 3) % 4 == new_rotation ? 1 : -1;
178
179 scoped_ptr<ui::LayerTreeOwner> old_layer_tree =
180 wm::RecreateLayers(root_window);
181
182 // Add the cloned layer tree in to the root, so it will be rendered.
183 root_window->layer()->Add(old_layer_tree->root());
184 root_window->layer()->StackAtTop(old_layer_tree->root());
185
186 scoped_ptr<LayerCleanupObserver> layer_cleanup_observer(
187 new LayerCleanupObserver(old_layer_tree.Pass()));
188
189 Shell::GetInstance()->display_manager()->SetDisplayRotation(display.id(),
190 new_rotation);
191
192 const gfx::RectF rotated_screen_bounds = root_window->GetTargetBounds();
193 const gfx::Point pivot = gfx::Point(rotated_screen_bounds.width() / 2,
194 rotated_screen_bounds.height() / 2);
195
196 gfx::Point3F new_layer_initial_scale = gfx::Point3F(1.0f, 1.0f, 1.0f);
197 gfx::Point3F old_layer_target_scale = gfx::Point3F(1.0f, 1.0f, 1.0f);
198
199 if (should_scale) {
200 new_layer_initial_scale = gfx::Point3F(
201 original_screen_bounds.width() / rotated_screen_bounds.width(),
202 original_screen_bounds.height() / rotated_screen_bounds.height(), 1.0f);
203
204 old_layer_target_scale = gfx::Point3F(
205 rotated_screen_bounds.width() / original_screen_bounds.width(),
206 rotated_screen_bounds.height() / original_screen_bounds.height(), 1.0f);
207 }
208
209 // We must animate each non-cloned child layer individually because the cloned
210 // layer was added as a child to |root_window|'s layer so that it will be
211 // rendered.
oshima 2015/03/06 20:59:32 instead, can't we simply apply the transform to th
bruthig 2015/03/09 21:04:27 I'm hesitant to implement it like that for a coupl
oshima 2015/03/10 22:20:38 We can actually have a layer with NOT_DRAWN under
bruthig 2015/03/11 14:40:07 Added a TODO to implement this.
212 for (ui::Layer* child_layer : root_window->layer()->children()) {
213 // Skip the cloned layer because it has a different animation.
214 if (child_layer == layer_cleanup_observer->GetRootLayer())
215 continue;
oshima 2015/03/10 22:20:38 can you add old layer after animations are applied
bruthig 2015/03/11 14:40:07 Am I correct to assume that this was an alternativ
oshima 2015/03/11 22:43:43 Yes. If you're going to change, you may leave it a
216
217 scoped_ptr<ScreenRotationAnimation> screen_rotation(
218 new ScreenRotationAnimation(
219 child_layer, rotation_degrees * rotation_factor,
220 0 /* end_degrees */, child_layer->opacity(),
221 1.0f /* target_opacity */, new_layer_initial_scale,
222 gfx::Point3F(1.0f, 1.0f, 1.0f), pivot, duration, tween_type));
223
224 ui::LayerAnimator* animator = child_layer->GetAnimator();
225 animator->set_preemption_strategy(
226 ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
227 scoped_ptr<ui::LayerAnimationSequence> animation_sequence(
228 new ui::LayerAnimationSequence(screen_rotation.release()));
229 animator->StartAnimation(animation_sequence.release());
230 }
231
232 // The old layer will also be transformed into the new orientation. We will
233 // translate it so that the old layer's center point aligns with the new
234 // orientation's center point and use that center point as the pivot for the
235 // rotation animation.
236 gfx::Transform translate_transform;
237 translate_transform.Translate(
238 (rotated_screen_bounds.width() - original_screen_bounds.width()) / 2,
239 (rotated_screen_bounds.height() - original_screen_bounds.height()) / 2);
240 layer_cleanup_observer->GetRootLayer()->SetTransform(translate_transform);
241
242 scoped_ptr<ScreenRotationAnimation> screen_rotation(
243 new ScreenRotationAnimation(
244 layer_cleanup_observer->GetRootLayer(),
245 (rotation_degrees + rotation_degree_offset) * rotation_factor,
246 rotation_degree_offset * rotation_factor,
247 layer_cleanup_observer->GetRootLayer()->opacity(),
248 0.0f /* target_opacity */, gfx::Point3F(1.0f, 1.0f, 1.0f),
249 old_layer_target_scale, pivot, duration, tween_type));
250
251 ui::LayerAnimator* animator =
252 layer_cleanup_observer->GetRootLayer()->GetAnimator();
253 animator->set_preemption_strategy(
254 ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
255 scoped_ptr<ui::LayerAnimationSequence> animation_sequence(
256 new ui::LayerAnimationSequence(screen_rotation.release()));
257 // Add an observer so that the cloned layers can be cleaned up with the
258 // animation completes/aborts.
259 animation_sequence->AddObserver(layer_cleanup_observer.release());
260 animator->StartAnimation(animation_sequence.release());
261 }
262
263 } // namespace
264
265 ScreenRotationAnimator::ScreenRotationAnimator(int64 display_id)
266 : display_(Shell::GetInstance()->display_manager()->GetDisplayForId(
267 display_id)) {
268 }
269
270 ScreenRotationAnimator::ScreenRotationAnimator(gfx::Display display)
271 : display_(display) {
272 }
273
274 ScreenRotationAnimator::~ScreenRotationAnimator() {
275 }
276
277 void ScreenRotationAnimator::Rotate(gfx::Display::Rotation new_rotation) {
278 const gfx::Display::Rotation current_rotation =
279 GetCurrentRotation(display_.id());
280
281 if (current_rotation == new_rotation)
282 return;
283
284 const std::string switch_value =
285 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
286 switches::kAshEnableScreenRotationAnimation);
287
288 if (switch_value.empty()) {
289 Shell::GetInstance()->display_manager()->SetDisplayRotation(display_.id(),
290 new_rotation);
291 return;
292 }
293
294 if (kPartialRotation == switch_value ||
295 kPartialRotationSlow == switch_value) {
296 const int durationInMs = (kPartialRotation == switch_value)
297 ? kRotationDurationInMs
298 : kRotationDurationSlowInMs;
299 const int rotation_degree_offset =
300 Is180DegreeFlip(current_rotation, new_rotation)
301 ? 180 - kPartialRotationDegrees
302 : 90 - kPartialRotationDegrees;
303
304 RotateScreen(display_, new_rotation,
305 base::TimeDelta::FromMilliseconds(durationInMs),
306 kPartialRotationDegrees, rotation_degree_offset,
307 gfx::Tween::FAST_OUT_LINEAR_IN, false /* should_scale */);
308 } else if (kFullRotation == switch_value ||
309 kFullRotationSlow == switch_value) {
310 const int durationInMs = (kFullRotation == switch_value)
311 ? kRotationDurationInMs
312 : kRotationDurationSlowInMs;
313 const int rotation_degrees =
314 Is180DegreeFlip(current_rotation, new_rotation) ? 180 : 90;
315
316 RotateScreen(display_, new_rotation,
317 base::TimeDelta::FromMilliseconds(durationInMs),
318 rotation_degrees, 0 /* rotation_degree_offset */,
319 gfx::Tween::FAST_OUT_LINEAR_IN, true /* should_scale */);
oshima 2015/03/06 20:59:32 showing stretched image sounds a bit weird. Didn'
bruthig 2015/03/09 21:04:27 I showed that implementation to some developers cl
oshima 2015/03/10 22:20:38 I tried it on my desktop and showing stretched ima
320 } else {
321 NOTREACHED();
322 }
323 }
324
325 } // namespace ash
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698