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.
5 #include "content/browser/android/overscroll_glow.h"
7 #include "cc/layers/layer.h"
8 #include "content/browser/android/edge_effect_base.h"
9 #include "content/public/browser/android/compositor.h"
18 const float kEpsilon
= 1e-3f
;
20 bool IsApproxZero(float value
) {
21 return std::abs(value
) < kEpsilon
;
24 gfx::Vector2dF
ZeroSmallComponents(gfx::Vector2dF vector
) {
25 if (IsApproxZero(vector
.x()))
27 if (IsApproxZero(vector
.y()))
32 gfx::Transform
ComputeTransform(OverscrollGlow::Edge edge
,
33 const gfx::SizeF
& window_size
,
35 // Transforms assume the edge layers are anchored to their *top center point*.
37 case OverscrollGlow::EDGE_TOP
:
38 return gfx::Transform(1, 0, 0, 1, 0, offset
);
39 case OverscrollGlow::EDGE_LEFT
:
40 return gfx::Transform(0,
44 -window_size
.height() / 2.f
+ offset
,
45 window_size
.height() / 2.f
);
46 case OverscrollGlow::EDGE_BOTTOM
:
47 return gfx::Transform(-1, 0, 0, -1, 0, window_size
.height() + offset
);
48 case OverscrollGlow::EDGE_RIGHT
:
49 return gfx::Transform(
54 -window_size
.height() / 2.f
+ window_size
.width() + offset
,
55 window_size
.height() / 2.f
);
57 NOTREACHED() << "Invalid edge: " << edge
;
58 return gfx::Transform();
62 gfx::SizeF
ComputeSize(OverscrollGlow::Edge edge
,
63 const gfx::SizeF
& window_size
) {
65 case OverscrollGlow::EDGE_TOP
:
66 case OverscrollGlow::EDGE_BOTTOM
:
68 case OverscrollGlow::EDGE_LEFT
:
69 case OverscrollGlow::EDGE_RIGHT
:
70 return gfx::SizeF(window_size
.height(), window_size
.width());
72 NOTREACHED() << "Invalid edge: " << edge
;
79 OverscrollGlow::OverscrollGlow(OverscrollGlowClient
* client
)
83 allow_horizontal_overscroll_(true),
84 allow_vertical_overscroll_(true) {
88 OverscrollGlow::~OverscrollGlow() {
92 void OverscrollGlow::Reset() {
96 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
)
97 edge_effects_
[i
]->Finish();
100 bool OverscrollGlow::IsActive() const {
103 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
104 if (!edge_effects_
[i
]->IsFinished())
110 float OverscrollGlow::GetVisibleAlpha() const {
112 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
113 if (!edge_effects_
[i
]->IsFinished())
114 max_alpha
= std::max(max_alpha
, edge_effects_
[i
]->GetAlpha());
116 return std::min(max_alpha
, 1.f
);
119 bool OverscrollGlow::OnOverscrolled(base::TimeTicks current_time
,
120 const gfx::Vector2dF
& accumulated_overscroll
,
121 gfx::Vector2dF overscroll_delta
,
122 gfx::Vector2dF velocity
,
123 const gfx::Vector2dF
& overscroll_location
) {
124 // The size of the glow determines the relative effect of the inputs; an
125 // empty-sized effect is effectively disabled.
126 if (viewport_size_
.IsEmpty())
129 if (!allow_horizontal_overscroll_
) {
130 overscroll_delta
.set_x(0);
133 if (!allow_vertical_overscroll_
) {
134 overscroll_delta
.set_y(0);
138 // Ignore sufficiently small values that won't meaningfuly affect animation.
139 overscroll_delta
= ZeroSmallComponents(overscroll_delta
);
140 if (overscroll_delta
.IsZero()) {
142 Release(current_time
);
143 return CheckNeedsAnimate();
146 if (!InitializeIfNecessary())
149 gfx::Vector2dF old_overscroll
= accumulated_overscroll
- overscroll_delta
;
150 bool x_overscroll_started
=
151 !IsApproxZero(overscroll_delta
.x()) && IsApproxZero(old_overscroll
.x());
152 bool y_overscroll_started
=
153 !IsApproxZero(overscroll_delta
.y()) && IsApproxZero(old_overscroll
.y());
155 velocity
= ZeroSmallComponents(velocity
);
156 if (!velocity
.IsZero())
157 Absorb(current_time
, velocity
, x_overscroll_started
, y_overscroll_started
);
159 Pull(current_time
, overscroll_delta
, overscroll_location
);
161 return CheckNeedsAnimate();
164 bool OverscrollGlow::Animate(base::TimeTicks current_time
,
165 cc::Layer
* parent_layer
) {
166 DCHECK(parent_layer
);
167 if (!CheckNeedsAnimate())
170 UpdateLayerAttachment(parent_layer
);
172 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
173 if (edge_effects_
[i
]->Update(current_time
)) {
174 Edge edge
= static_cast<Edge
>(i
);
175 edge_effects_
[i
]->ApplyToLayers(
176 ComputeSize(edge
, viewport_size_
),
177 ComputeTransform(edge
, viewport_size_
, edge_offsets_
[i
]));
181 return CheckNeedsAnimate();
184 void OverscrollGlow::OnFrameUpdated(
185 const gfx::SizeF
& viewport_size
,
186 const gfx::SizeF
& content_size
,
187 const gfx::Vector2dF
& content_scroll_offset
) {
188 viewport_size_
= viewport_size
;
189 edge_offsets_
[OverscrollGlow::EDGE_TOP
] = -content_scroll_offset
.y();
190 edge_offsets_
[OverscrollGlow::EDGE_LEFT
] = -content_scroll_offset
.x();
191 edge_offsets_
[OverscrollGlow::EDGE_BOTTOM
] = content_size
.height() -
192 content_scroll_offset
.y() -
193 viewport_size
.height();
194 edge_offsets_
[OverscrollGlow::EDGE_RIGHT
] =
195 content_size
.width() - content_scroll_offset
.x() - viewport_size
.width();
197 // Only allow overscroll on scrollable axes, matching platform behavior.
198 allow_horizontal_overscroll_
=
199 std::ceil(viewport_size_
.width()) < std::floor(content_size
.width());
200 allow_vertical_overscroll_
=
201 std::ceil(viewport_size_
.height()) < std::floor(content_size
.height());
204 bool OverscrollGlow::CheckNeedsAnimate() {
217 void OverscrollGlow::UpdateLayerAttachment(cc::Layer
* parent
) {
219 if (!root_layer_
.get())
222 if (!CheckNeedsAnimate())
225 if (root_layer_
->parent() != parent
)
226 parent
->AddChild(root_layer_
);
228 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
)
229 edge_effects_
[i
]->SetParent(root_layer_
.get());
232 void OverscrollGlow::Detach() {
233 if (root_layer_
.get())
234 root_layer_
->RemoveFromParent();
237 bool OverscrollGlow::InitializeIfNecessary() {
241 DCHECK(!root_layer_
.get());
242 root_layer_
= cc::Layer::Create(Compositor::LayerSettings());
243 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
244 edge_effects_
[i
] = client_
->CreateEdgeEffect();
245 DCHECK(edge_effects_
[i
]);
252 void OverscrollGlow::Pull(base::TimeTicks current_time
,
253 const gfx::Vector2dF
& overscroll_delta
,
254 const gfx::Vector2dF
& overscroll_location
) {
255 DCHECK(initialized_
);
256 DCHECK(!overscroll_delta
.IsZero());
257 DCHECK(!viewport_size_
.IsEmpty());
258 const float inv_width
= 1.f
/ viewport_size_
.width();
259 const float inv_height
= 1.f
/ viewport_size_
.height();
261 gfx::Vector2dF overscroll_pull
=
262 gfx::ScaleVector2d(overscroll_delta
, inv_width
, inv_height
);
263 const float edge_pull
[EDGE_COUNT
] = {
264 min(overscroll_pull
.y(), 0.f
), // Top
265 min(overscroll_pull
.x(), 0.f
), // Left
266 max(overscroll_pull
.y(), 0.f
), // Bottom
267 max(overscroll_pull
.x(), 0.f
) // Right
270 gfx::Vector2dF displacement
=
271 gfx::ScaleVector2d(overscroll_location
, inv_width
, inv_height
);
272 displacement
.set_x(max(0.f
, min(1.f
, displacement
.x())));
273 displacement
.set_y(max(0.f
, min(1.f
, displacement
.y())));
274 const float edge_displacement
[EDGE_COUNT
] = {
275 1.f
- displacement
.x(), // Top
276 displacement
.y(), // Left
277 displacement
.x(), // Bottom
278 1.f
- displacement
.y() // Right
281 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
285 edge_effects_
[i
]->Pull(
286 current_time
, std::abs(edge_pull
[i
]), edge_displacement
[i
]);
287 GetOppositeEdge(i
)->Release(current_time
);
291 void OverscrollGlow::Absorb(base::TimeTicks current_time
,
292 const gfx::Vector2dF
& velocity
,
293 bool x_overscroll_started
,
294 bool y_overscroll_started
) {
295 DCHECK(initialized_
);
296 DCHECK(!velocity
.IsZero());
298 // Only trigger on initial overscroll at a non-zero velocity
299 const float overscroll_velocities
[EDGE_COUNT
] = {
300 y_overscroll_started
? min(velocity
.y(), 0.f
) : 0, // Top
301 x_overscroll_started
? min(velocity
.x(), 0.f
) : 0, // Left
302 y_overscroll_started
? max(velocity
.y(), 0.f
) : 0, // Bottom
303 x_overscroll_started
? max(velocity
.x(), 0.f
) : 0 // Right
306 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
) {
307 if (!overscroll_velocities
[i
])
310 edge_effects_
[i
]->Absorb(current_time
, std::abs(overscroll_velocities
[i
]));
311 GetOppositeEdge(i
)->Release(current_time
);
315 void OverscrollGlow::Release(base::TimeTicks current_time
) {
316 DCHECK(initialized_
);
317 for (size_t i
= 0; i
< EDGE_COUNT
; ++i
)
318 edge_effects_
[i
]->Release(current_time
);
321 EdgeEffectBase
* OverscrollGlow::GetOppositeEdge(int edge_index
) {
322 DCHECK(initialized_
);
323 return edge_effects_
[(edge_index
+ 2) % EDGE_COUNT
].get();
326 } // namespace content