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

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: 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
« no previous file with comments | « ash/rotator/screen_rotation_animator.h ('k') | ash/rotator/window_rotation.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
33 namespace ash {
34
35 namespace {
36
37 // Clones all child layers of |to_clone| and adds the clones as children to
38 // |parent|. Recurses on each child of |to_clone|.
39 void CloneChildren(ui::Layer* to_clone, ui::Layer* parent) {
40 typedef std::vector<ui::Layer*> Layers;
41 // Make a copy of the children since RecreateLayer() mutates it.
42 Layers children(to_clone->children());
43 for (Layers::const_iterator i = children.begin(); i != children.end(); ++i) {
44 ui::LayerOwner* owner = (*i)->owner();
45 ui::Layer* clone = owner ? owner->RecreateLayer().release() : NULL;
46 if (clone) {
47 parent->Add(clone);
48 // RecreateLayer() moves the existing children to the new layer. Create a
49 // copy of those.
50 CloneChildren(owner->layer(), clone);
51 }
52 }
53 }
54
55 // Clones the layer tree owned by |window| and adds the cloned layer tree as a
56 // child of |window|'s layer.
57 scoped_ptr<ui::LayerTreeOwner> CloneWindowLayers(aura::Window* window) {
58 scoped_ptr<ui::LayerTreeOwner> cloned_layer_tree(
59 new ui::LayerTreeOwner(new ui::Layer()));
60 CloneChildren(window->layer(), cloned_layer_tree->root());
61
62 DCHECK(cloned_layer_tree->root());
63 DCHECK(window->layer());
64
65 // Add the cloned layer tree into the root, so it will be rendered.
66 window->layer()->Add(cloned_layer_tree->root());
67 window->layer()->StackAtTop(cloned_layer_tree->root());
68
69 return cloned_layer_tree.Pass();
70 }
71
72 // Gets the current display rotation for the display with the specified
73 // |display_id|.
74 gfx::Display::Rotation GetCurrentRotation(int64 display_id) {
75 return Shell::GetInstance()->display_manager()->GetDisplayInfo(
76 display_id).rotation();
77 }
78
79 // Returns true if the rotation between |initial_rotation| and |new_rotation| is
80 // 180 degrees.
81 bool Is180DegreeFlip(gfx::Display::Rotation initial_rotation,
82 gfx::Display::Rotation new_rotation) {
83 return (initial_rotation + 2) % 4 == new_rotation;
84 }
85
86 // A LayerAnimationObserver that will destroy the contained LayerTreeOwner when
87 // notified that a layer animation has ended or was aborted.
88 class LayerCleanupObserver : public ui::LayerAnimationObserver {
89 public:
90 explicit LayerCleanupObserver(
91 scoped_ptr<ui::LayerTreeOwner> layer_tree_owner);
92 ~LayerCleanupObserver() override;
93
94 // Get the root layer of the owned layer tree.
95 ui::Layer* GetRootLayer();
96
97 // ui::LayerAnimationObserver:
98 void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override;
99 void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override;
100 void OnLayerAnimationScheduled(ui::LayerAnimationSequence* sequence)
101 override {}
102
103 protected:
104 // ui::LayerAnimationObserver:
105 bool RequiresNotificationWhenAnimatorDestroyed() const override {
106 return true;
107 }
108 void OnAttachedToSequence(ui::LayerAnimationSequence* sequence) override;
109 void OnDetachedFromSequence(ui::LayerAnimationSequence* sequence) override;
110
111 private:
112 // The owned layer tree.
113 scoped_ptr<ui::LayerTreeOwner> layer_tree_owner_;
114
115 // The LayerAnimationSequence that |this| has been attached to. Defaults to
116 // nullptr.
117 ui::LayerAnimationSequence* sequence_;
118
119 DISALLOW_COPY_AND_ASSIGN(LayerCleanupObserver);
120 };
121
122 LayerCleanupObserver::LayerCleanupObserver(
123 scoped_ptr<ui::LayerTreeOwner> layer_tree_owner)
124 : layer_tree_owner_(layer_tree_owner.Pass()),
125 sequence_(nullptr) {
126 }
127
128 LayerCleanupObserver::~LayerCleanupObserver() {
129 // We must eplicitly detach from |sequence_| because we return true from
130 // RequiresNotificationWhenAnimatorDestroyed.
131 if (sequence_)
132 sequence_->RemoveObserver(this);
133 }
134
135 ui::Layer* LayerCleanupObserver::GetRootLayer() {
136 return layer_tree_owner_->root();
137 }
138
139 void LayerCleanupObserver::OnLayerAnimationEnded(
140 ui::LayerAnimationSequence* sequence) {
141 delete this;
142 }
143
144 void LayerCleanupObserver::OnLayerAnimationAborted(
145 ui::LayerAnimationSequence* sequence) {
146 delete this;
147 }
148
149 void LayerCleanupObserver::OnAttachedToSequence(
150 ui::LayerAnimationSequence* sequence) {
151 sequence_ = sequence;
152 }
153
154 void LayerCleanupObserver::OnDetachedFromSequence(
155 ui::LayerAnimationSequence* sequence) {
156 DCHECK(sequence == sequence_);
157 sequence_ = nullptr;
158 }
159
160 // Set the screen orientation for the given |display| to |new_rotation| and
161 // animate the change.
162 void RotateScreen(gfx::Display display,
163 gfx::Display::Rotation new_rotation,
164 base::TimeDelta duration,
165 int rotation_degrees,
166 int rotation_degree_offset,
167 gfx::Tween::Type tween_type,
168 bool should_scale) {
Jun Mukai 2015/03/03 23:36:42 Indent seems wrong. Run git cl format.
bruthig 2015/03/04 15:31:36 Done.
169 aura::Window* root_window = Shell::GetInstance()->display_controller()->
170 GetRootWindowForDisplayId(display.id());
171
172 const gfx::Rect original_screen_bounds_f = root_window->GetTargetBounds();
Jun Mukai 2015/03/03 23:36:41 What does the _f prefix mean? Did you intend to u
bruthig 2015/03/04 15:31:36 Ya. I just made both *_screen_bounds variables Re
Jun Mukai 2015/03/04 19:46:20 Ah, actually I'm okay with _f prefix itself, I won
173 // 180 degree rotations should animation clock-wise.
174 const int rotation_factor =
175 (GetCurrentRotation(display.id()) + 3) % 4 == new_rotation ? 1 : -1;
176 scoped_ptr<LayerCleanupObserver> layer_cleanup_observer(
177 new LayerCleanupObserver(CloneWindowLayers(root_window).Pass()));
178
179 Shell::GetInstance()->display_manager()->SetDisplayRotation(display.id(),
180 new_rotation);
181
182 const gfx::Rect rotated_screen_bounds = root_window->GetTargetBounds();
183 const gfx::Point pivot =
184 Is180DegreeFlip(GetCurrentRotation(display.id()), new_rotation) ?
185 gfx::Point(rotated_screen_bounds.height() / 2,
186 rotated_screen_bounds.width() / 2) :
187 gfx::Point(rotated_screen_bounds.width() / 2,
188 rotated_screen_bounds.height() / 2);
189
190 gfx::Point3F new_layer_initial_scale = gfx::Point3F(1.0f, 1.0f, 1.0f);
191 gfx::Point3F old_layer_target_scale = gfx::Point3F(1.0f, 1.0f, 1.0f);
192
193 if (should_scale) {
194 const gfx::RectF rotated_screen_bounds_f = rotated_screen_bounds;
195 new_layer_initial_scale = gfx::Point3F(
196 original_screen_bounds_f.width() / rotated_screen_bounds_f.width(),
197 original_screen_bounds_f.height() / rotated_screen_bounds_f.height(),
198 1.0f);
199
200 old_layer_target_scale = gfx::Point3F(
201 rotated_screen_bounds_f.width() / original_screen_bounds_f.width(),
202 rotated_screen_bounds_f.height() / original_screen_bounds_f.height(),
203 1.0f);
204 }
205
206 // We must animate each non-cloned child layer individually because the cloned
207 // layer was added as a child to |root_window|'s layer so that it will be
208 // rendered.
209 for (ui::Layer* child_layer : root_window->layer()->children()) {
210 // Skip the cloned layer because it has a different animation.
211 if (child_layer == layer_cleanup_observer->GetRootLayer())
212 continue;
213
214 scoped_ptr<ScreenRotationAnimation> screen_rotation(
215 new ScreenRotationAnimation(child_layer,
216 rotation_degrees * rotation_factor,
217 0 /* end_degrees */,
218 new_layer_initial_scale,
219 gfx::Point3F(1.0f, 1.0f, 1.0f),
220 pivot,
221 duration));
222
223 screen_rotation->set_tween_type(tween_type);
224 screen_rotation->set_target_opacity(1.0f);
Jun Mukai 2015/03/03 23:36:41 Why these values aren't in constructor params?
bruthig 2015/03/04 15:31:36 I thought the default values for them were reasona
Jun Mukai 2015/03/04 19:46:20 I still don't see the reason why these two fields
bruthig 2015/03/06 16:00:33 Done.
225
226 ui::LayerAnimator* animator = child_layer->GetAnimator();
227 animator->set_preemption_strategy(
228 ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
229 scoped_ptr<ui::LayerAnimationSequence> animation_sequence(
230 new ui::LayerAnimationSequence(screen_rotation.release()));
231 animator->StartAnimation(animation_sequence.release());
Jun Mukai 2015/03/03 23:36:41 Could we use ScopedLayerAnimationSettings instead
bruthig 2015/03/04 15:31:36 No I don't think it's possible to use the ScopedLa
Jun Mukai 2015/03/04 19:46:20 got it. Sorry for my poor comment.
232 }
233
234 // The old layer will also be transformed into the new orientation. We will
235 // translate it so that the old layer's center point aligns with the new
236 // orientation's center point and use that center point as the pivot for the
237 // rotation animation.
238 gfx::Transform translate_transform =
239 layer_cleanup_observer->GetRootLayer()->GetTargetTransform();
240 translate_transform.Translate(
241 (rotated_screen_bounds.width() - original_screen_bounds_f.width()) / 2,
242 (rotated_screen_bounds.height() - original_screen_bounds_f.height()) / 2);
243 layer_cleanup_observer->GetRootLayer()->SetTransform(translate_transform);
244
245 scoped_ptr<ScreenRotationAnimation> screen_rotation(
246 new ScreenRotationAnimation(layer_cleanup_observer->GetRootLayer(),
247 (rotation_degrees + rotation_degree_offset) * rotation_factor,
248 rotation_degree_offset * rotation_factor,
249 gfx::Point3F(1.0f, 1.0f, 1.0f),
250 old_layer_target_scale,
251 pivot,
252 duration));
253
254 screen_rotation->set_tween_type(tween_type);
255 screen_rotation->set_target_opacity(0.0f);
256
257 ui::LayerAnimator* animator = layer_cleanup_observer->GetRootLayer()->
258 GetAnimator();
259 animator->set_preemption_strategy(
260 ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
261 scoped_ptr<ui::LayerAnimationSequence> animation_sequence(
262 new ui::LayerAnimationSequence(screen_rotation.release()));
263 // Add an observer so that the cloned layers can be cleaned up with the
264 // animation completes/aborts.
265 animation_sequence->AddObserver(layer_cleanup_observer.release());
266 animator->StartAnimation(animation_sequence.release());
267 }
268
269 } // namespace
270
271 ScreenRotationAnimator::ScreenRotationAnimator(int64 display_id)
272 : display_(Shell::GetInstance()->display_manager()->GetDisplayForId(
273 display_id)) {
274 }
275
276 ScreenRotationAnimator::ScreenRotationAnimator(gfx::Display display)
277 : display_(display) {
278 }
279
280 ScreenRotationAnimator::~ScreenRotationAnimator() {
281 }
282
283 void ScreenRotationAnimator::Rotate(gfx::Display::Rotation new_rotation) {
284 const gfx::Display::Rotation current_rotation = GetCurrentRotation(
285 display_.id());
286
287 if (current_rotation == new_rotation)
288 return;
289
290 const std::string switch_value = base::CommandLine::ForCurrentProcess()->
291 GetSwitchValueASCII(switches::kAshEnableScreenRotationAnimation);
292
293 if ("" == switch_value) {
294 Shell::GetInstance()->display_manager()->SetDisplayRotation(display_.id(),
295 new_rotation);
296 return;
297 }
298
299 if ("partial-rotation" == switch_value ||
300 "partial-rotation-slow" == switch_value) {
301 RotateScreen(display_,
302 new_rotation,
303 base::TimeDelta::FromMilliseconds(
304 "partial-rotation" == switch_value ? 250 : 2500),
Jun Mukai 2015/03/03 23:36:41 You should declare constants for those values, lik
bruthig 2015/03/04 15:31:35 Done.
305 20 /* rotation_degrees */,
306 Is180DegreeFlip(current_rotation, new_rotation) ? 160 : 70,
Jun Mukai 2015/03/03 23:36:41 What do these 160/70 mean?
bruthig 2015/03/04 15:31:36 I attempted to clarify by adding the kPartialRotat
307 gfx::Tween::FAST_OUT_LINEAR_IN,
308 false /* should_scale */);
309 } else if ("full-rotation" == switch_value ||
310 "full-rotation-slow" == switch_value) {
311 RotateScreen(display_,
312 new_rotation,
313 base::TimeDelta::FromMilliseconds(
314 "full-rotation" == switch_value ? 250 : 2500),
315 Is180DegreeFlip(current_rotation, new_rotation) ? 180 : 90,
316 0 /* rotation_degree_offset */,
317 gfx::Tween::FAST_OUT_LINEAR_IN,
318 true /* should_scale */);
319 } else {
320 DCHECK(false);
321 }
322 }
323
324 } // namespace ash
OLDNEW
« no previous file with comments | « ash/rotator/screen_rotation_animator.h ('k') | ash/rotator/window_rotation.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698