• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 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 "content/browser/android/overscroll_glow.h"
6 
7 #include "cc/layers/layer.h"
8 #include "content/browser/android/edge_effect_base.h"
9 
10 using std::max;
11 using std::min;
12 
13 namespace content {
14 
15 namespace {
16 
17 const float kEpsilon = 1e-3f;
18 
IsApproxZero(float value)19 bool IsApproxZero(float value) {
20   return std::abs(value) < kEpsilon;
21 }
22 
ZeroSmallComponents(gfx::Vector2dF vector)23 gfx::Vector2dF ZeroSmallComponents(gfx::Vector2dF vector) {
24   if (IsApproxZero(vector.x()))
25     vector.set_x(0);
26   if (IsApproxZero(vector.y()))
27     vector.set_y(0);
28   return vector;
29 }
30 
ComputeTransform(OverscrollGlow::Edge edge,const gfx::SizeF & window_size,float offset)31 gfx::Transform ComputeTransform(OverscrollGlow::Edge edge,
32                                 const gfx::SizeF& window_size,
33                                 float offset) {
34   // Transforms assume the edge layers are anchored to their *top center point*.
35   switch (edge) {
36     case OverscrollGlow::EDGE_TOP:
37       return gfx::Transform(1, 0, 0, 1, 0, offset);
38     case OverscrollGlow::EDGE_LEFT:
39       return gfx::Transform(0,
40                             1,
41                             -1,
42                             0,
43                             -window_size.height() / 2.f + offset,
44                             window_size.height() / 2.f);
45     case OverscrollGlow::EDGE_BOTTOM:
46       return gfx::Transform(-1, 0, 0, -1, 0, window_size.height() + offset);
47     case OverscrollGlow::EDGE_RIGHT:
48       return gfx::Transform(
49           0,
50           -1,
51           1,
52           0,
53           -window_size.height() / 2.f + window_size.width() + offset,
54           window_size.height() / 2.f);
55     default:
56       NOTREACHED() << "Invalid edge: " << edge;
57       return gfx::Transform();
58   };
59 }
60 
ComputeSize(OverscrollGlow::Edge edge,const gfx::SizeF & window_size)61 gfx::SizeF ComputeSize(OverscrollGlow::Edge edge,
62                        const gfx::SizeF& window_size) {
63   switch (edge) {
64     case OverscrollGlow::EDGE_TOP:
65     case OverscrollGlow::EDGE_BOTTOM:
66       return window_size;
67     case OverscrollGlow::EDGE_LEFT:
68     case OverscrollGlow::EDGE_RIGHT:
69       return gfx::SizeF(window_size.height(), window_size.width());
70     default:
71       NOTREACHED() << "Invalid edge: " << edge;
72       return gfx::SizeF();
73   };
74 }
75 
76 }  // namespace
77 
OverscrollGlow(const EdgeEffectProvider & edge_effect_provider)78 OverscrollGlow::OverscrollGlow(const EdgeEffectProvider& edge_effect_provider)
79     : edge_effect_provider_(edge_effect_provider),
80       enabled_(true),
81       initialized_(false) {
82   DCHECK(!edge_effect_provider_.is_null());
83 }
84 
~OverscrollGlow()85 OverscrollGlow::~OverscrollGlow() {
86   Detach();
87 }
88 
Enable()89 void OverscrollGlow::Enable() {
90   enabled_ = true;
91 }
92 
Disable()93 void OverscrollGlow::Disable() {
94   if (!enabled_)
95     return;
96   enabled_ = false;
97   if (!enabled_ && initialized_) {
98     Detach();
99     for (size_t i = 0; i < EDGE_COUNT; ++i)
100       edge_effects_[i]->Finish();
101   }
102 }
103 
OnOverscrolled(cc::Layer * overscrolling_layer,base::TimeTicks current_time,gfx::Vector2dF accumulated_overscroll,gfx::Vector2dF overscroll_delta,gfx::Vector2dF velocity,gfx::Vector2dF displacement)104 bool OverscrollGlow::OnOverscrolled(cc::Layer* overscrolling_layer,
105                                     base::TimeTicks current_time,
106                                     gfx::Vector2dF accumulated_overscroll,
107                                     gfx::Vector2dF overscroll_delta,
108                                     gfx::Vector2dF velocity,
109                                     gfx::Vector2dF displacement) {
110   DCHECK(overscrolling_layer);
111 
112   if (!enabled_)
113     return false;
114 
115   // The size of the glow determines the relative effect of the inputs; an
116   // empty-sized effect is effectively disabled.
117   if (display_params_.size.IsEmpty())
118     return false;
119 
120   // Ignore sufficiently small values that won't meaningfuly affect animation.
121   overscroll_delta = ZeroSmallComponents(overscroll_delta);
122   if (overscroll_delta.IsZero()) {
123     if (initialized_) {
124       Release(current_time);
125       UpdateLayerAttachment(overscrolling_layer);
126     }
127     return NeedsAnimate();
128   }
129 
130   if (!InitializeIfNecessary())
131     return false;
132 
133   gfx::Vector2dF old_overscroll = accumulated_overscroll - overscroll_delta;
134   bool x_overscroll_started =
135       !IsApproxZero(overscroll_delta.x()) && IsApproxZero(old_overscroll.x());
136   bool y_overscroll_started =
137       !IsApproxZero(overscroll_delta.y()) && IsApproxZero(old_overscroll.y());
138 
139   velocity = ZeroSmallComponents(velocity);
140   if (!velocity.IsZero())
141     Absorb(current_time, velocity, x_overscroll_started, y_overscroll_started);
142   else
143     Pull(current_time, overscroll_delta, displacement);
144 
145   UpdateLayerAttachment(overscrolling_layer);
146   return NeedsAnimate();
147 }
148 
Animate(base::TimeTicks current_time)149 bool OverscrollGlow::Animate(base::TimeTicks current_time) {
150   if (!NeedsAnimate()) {
151     Detach();
152     return false;
153   }
154 
155   for (size_t i = 0; i < EDGE_COUNT; ++i) {
156     if (edge_effects_[i]->Update(current_time)) {
157       Edge edge = static_cast<Edge>(i);
158       edge_effects_[i]->ApplyToLayers(
159           ComputeSize(edge, display_params_.size),
160           ComputeTransform(
161               edge, display_params_.size, display_params_.edge_offsets[i]));
162     }
163   }
164 
165   if (!NeedsAnimate()) {
166     Detach();
167     return false;
168   }
169 
170   return true;
171 }
172 
UpdateDisplayParameters(const DisplayParameters & params)173 void OverscrollGlow::UpdateDisplayParameters(const DisplayParameters& params) {
174   display_params_ = params;
175 }
176 
NeedsAnimate() const177 bool OverscrollGlow::NeedsAnimate() const {
178   if (!enabled_ || !initialized_)
179     return false;
180   for (size_t i = 0; i < EDGE_COUNT; ++i) {
181     if (!edge_effects_[i]->IsFinished())
182       return true;
183   }
184   return false;
185 }
186 
UpdateLayerAttachment(cc::Layer * parent)187 void OverscrollGlow::UpdateLayerAttachment(cc::Layer* parent) {
188   DCHECK(parent);
189   if (!root_layer_)
190     return;
191 
192   if (!NeedsAnimate()) {
193     Detach();
194     return;
195   }
196 
197   if (root_layer_->parent() != parent)
198     parent->AddChild(root_layer_);
199 
200   for (size_t i = 0; i < EDGE_COUNT; ++i)
201     edge_effects_[i]->SetParent(root_layer_);
202 }
203 
Detach()204 void OverscrollGlow::Detach() {
205   if (root_layer_)
206     root_layer_->RemoveFromParent();
207 }
208 
InitializeIfNecessary()209 bool OverscrollGlow::InitializeIfNecessary() {
210   DCHECK(enabled_);
211   if (initialized_)
212     return true;
213 
214   DCHECK(!root_layer_);
215   root_layer_ = cc::Layer::Create();
216   for (size_t i = 0; i < EDGE_COUNT; ++i) {
217     edge_effects_[i] = edge_effect_provider_.Run();
218     DCHECK(edge_effects_[i]);
219   }
220 
221   initialized_ = true;
222   return true;
223 }
224 
Pull(base::TimeTicks current_time,const gfx::Vector2dF & overscroll_delta,const gfx::Vector2dF & overscroll_location)225 void OverscrollGlow::Pull(base::TimeTicks current_time,
226                           const gfx::Vector2dF& overscroll_delta,
227                           const gfx::Vector2dF& overscroll_location) {
228   DCHECK(enabled_ && initialized_);
229   DCHECK(!overscroll_delta.IsZero());
230   const float inv_width = 1.f / display_params_.size.width();
231   const float inv_height = 1.f / display_params_.size.height();
232 
233   gfx::Vector2dF overscroll_pull =
234       gfx::ScaleVector2d(overscroll_delta, inv_width, inv_height);
235   const float edge_pull[EDGE_COUNT] = {
236       min(overscroll_pull.y(), 0.f),  // Top
237       min(overscroll_pull.x(), 0.f),  // Left
238       max(overscroll_pull.y(), 0.f),  // Bottom
239       max(overscroll_pull.x(), 0.f)   // Right
240   };
241 
242   gfx::Vector2dF displacement =
243       gfx::ScaleVector2d(overscroll_location, inv_width, inv_height);
244   displacement.set_x(max(0.f, min(1.f, displacement.x())));
245   displacement.set_y(max(0.f, min(1.f, displacement.y())));
246   const float edge_displacement[EDGE_COUNT] = {
247       1.f - displacement.x(),  // Top
248       displacement.y(),        // Left
249       displacement.x(),        // Bottom
250       1.f - displacement.y()   // Right
251   };
252 
253   for (size_t i = 0; i < EDGE_COUNT; ++i) {
254     if (!edge_pull[i])
255       continue;
256 
257     edge_effects_[i]->Pull(
258         current_time, std::abs(edge_pull[i]), edge_displacement[i]);
259     GetOppositeEdge(i)->Release(current_time);
260   }
261 }
262 
Absorb(base::TimeTicks current_time,const gfx::Vector2dF & velocity,bool x_overscroll_started,bool y_overscroll_started)263 void OverscrollGlow::Absorb(base::TimeTicks current_time,
264                             const gfx::Vector2dF& velocity,
265                             bool x_overscroll_started,
266                             bool y_overscroll_started) {
267   DCHECK(enabled_ && initialized_);
268   DCHECK(!velocity.IsZero());
269 
270   // Only trigger on initial overscroll at a non-zero velocity
271   const float overscroll_velocities[EDGE_COUNT] = {
272       y_overscroll_started ? min(velocity.y(), 0.f) : 0,  // Top
273       x_overscroll_started ? min(velocity.x(), 0.f) : 0,  // Left
274       y_overscroll_started ? max(velocity.y(), 0.f) : 0,  // Bottom
275       x_overscroll_started ? max(velocity.x(), 0.f) : 0   // Right
276   };
277 
278   for (size_t i = 0; i < EDGE_COUNT; ++i) {
279     if (!overscroll_velocities[i])
280       continue;
281 
282     edge_effects_[i]->Absorb(current_time, std::abs(overscroll_velocities[i]));
283     GetOppositeEdge(i)->Release(current_time);
284   }
285 }
286 
Release(base::TimeTicks current_time)287 void OverscrollGlow::Release(base::TimeTicks current_time) {
288   DCHECK(initialized_);
289   for (size_t i = 0; i < EDGE_COUNT; ++i)
290     edge_effects_[i]->Release(current_time);
291 }
292 
GetOppositeEdge(int edge_index)293 EdgeEffectBase* OverscrollGlow::GetOppositeEdge(int edge_index) {
294   DCHECK(initialized_);
295   return edge_effects_[(edge_index + 2) % EDGE_COUNT].get();
296 }
297 
DisplayParameters()298 OverscrollGlow::DisplayParameters::DisplayParameters() {
299   edge_offsets[0] = edge_offsets[1] = edge_offsets[2] = edge_offsets[3] = 0.f;
300 }
301 
302 }  // namespace content
303