Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / content / browser / android / edge_effect.cc
bloba23dff89e3c1a3f3d2bbfe6a0ad936687a4bc938
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/edge_effect.h"
7 #include "cc/layers/layer.h"
9 namespace content {
11 namespace {
13 enum State {
14 STATE_IDLE = 0,
15 STATE_PULL,
16 STATE_ABSORB,
17 STATE_RECEDE,
18 STATE_PULL_DECAY
21 // Time it will take the effect to fully recede in ms
22 const int kRecedeTime = 1000;
24 // Time it will take before a pulled glow begins receding in ms
25 const int kPullTime = 167;
27 // Time it will take in ms for a pulled glow to decay before release
28 const int kPullDecayTime = 1000;
30 const float kMaxAlpha = 1.f;
31 const float kHeldEdgeScaleY = .5f;
33 const float kMaxGlowHeight = 4.f;
35 const float kPullGlowBegin = 1.f;
36 const float kPullEdgeBegin = 0.6f;
38 // Min/max velocity that will be absorbed
39 const float kMinVelocity = 100.f;
40 const float kMaxVelocity = 10000.f;
42 const float kEpsilon = 0.001f;
44 const float kGlowHeightToWidthRatio = 0.25f;
46 // How much dragging should effect the height of the edge image.
47 // Number determined by user testing.
48 const int kPullDistanceEdgeFactor = 7;
50 // How much dragging should effect the height of the glow image.
51 // Number determined by user testing.
52 const int kPullDistanceGlowFactor = 7;
53 const float kPullDistanceAlphaGlowFactor = 1.1f;
55 const int kVelocityEdgeFactor = 8;
56 const int kVelocityGlowFactor = 12;
58 template <typename T>
59 T Lerp(T a, T b, T t) {
60 return a + (b - a) * t;
63 template <typename T>
64 T Clamp(T value, T low, T high) {
65 return value < low ? low : (value > high ? high : value);
68 template <typename T>
69 T Damp(T input, T factor) {
70 T result;
71 if (factor == 1) {
72 result = 1 - (1 - input) * (1 - input);
73 } else {
74 result = 1 - std::pow(1 - input, 2 * factor);
76 return result;
79 gfx::Transform ComputeTransform(EdgeEffect::Edge edge,
80 const gfx::SizeF& window_size,
81 int offset,
82 int height) {
83 // Edge effects that require rotation are translated to the center about which
84 // the layer should be rotated to align with the corresponding edge.
85 switch (edge) {
86 case EdgeEffect::EDGE_TOP:
87 return gfx::Transform(1, 0, 0, 1, 0, offset);
88 case EdgeEffect::EDGE_LEFT:
89 return gfx::Transform(0, 1, -1, 0,
90 (-window_size.height() + height) / 2.f + offset,
91 (window_size.height() - height) / 2.f);
92 case EdgeEffect::EDGE_BOTTOM:
93 return gfx::Transform(-1, 0, 0, -1,
94 0, window_size.height() - height + offset);
95 case EdgeEffect::EDGE_RIGHT:
96 return gfx::Transform(0, -1, 1, 0,
97 (-window_size.height() - height) / 2.f + window_size.width() + offset,
98 (window_size.height() - height) / 2.f);
99 default:
100 NOTREACHED() << "Invalid edge: " << edge;
101 return gfx::Transform();
105 gfx::Size ComputeBounds(EdgeEffect::Edge edge,
106 const gfx::SizeF& window_size,
107 int height) {
108 switch (edge) {
109 case EdgeEffect::EDGE_TOP:
110 case EdgeEffect::EDGE_BOTTOM:
111 return gfx::Size(window_size.width(), height);
112 case EdgeEffect::EDGE_LEFT:
113 case EdgeEffect::EDGE_RIGHT:
114 return gfx::Size(window_size.height(), height);
115 default:
116 NOTREACHED() << "Invalid edge: " << edge;
117 return gfx::Size();
121 void DisableLayer(cc::Layer* layer) {
122 DCHECK(layer);
123 layer->SetIsDrawable(false);
124 layer->SetTransform(gfx::Transform());
125 layer->SetOpacity(1.f);
128 void UpdateLayer(cc::Layer* layer,
129 EdgeEffect::Edge edge,
130 const gfx::SizeF& window_size,
131 int offset,
132 int height,
133 float opacity) {
134 DCHECK(layer);
135 layer->SetIsDrawable(true);
136 layer->SetTransform(ComputeTransform(edge, window_size, offset, height));
137 layer->SetBounds(ComputeBounds(edge, window_size, height));
138 layer->SetOpacity(Clamp(opacity, 0.f, 1.f));
141 } // namespace
143 EdgeEffect::EdgeEffect(scoped_refptr<cc::Layer> edge,
144 scoped_refptr<cc::Layer> glow)
145 : edge_(edge)
146 , glow_(glow)
147 , edge_alpha_(0)
148 , edge_scale_y_(0)
149 , glow_alpha_(0)
150 , glow_scale_y_(0)
151 , edge_alpha_start_(0)
152 , edge_alpha_finish_(0)
153 , edge_scale_y_start_(0)
154 , edge_scale_y_finish_(0)
155 , glow_alpha_start_(0)
156 , glow_alpha_finish_(0)
157 , glow_scale_y_start_(0)
158 , glow_scale_y_finish_(0)
159 , state_(STATE_IDLE)
160 , pull_distance_(0) {
161 // Prevent the provided layers from drawing until the effect is activated.
162 DisableLayer(edge_.get());
163 DisableLayer(glow_.get());
166 EdgeEffect::~EdgeEffect() { }
168 bool EdgeEffect::IsFinished() const {
169 return state_ == STATE_IDLE;
172 void EdgeEffect::Finish() {
173 DisableLayer(edge_.get());
174 DisableLayer(glow_.get());
175 pull_distance_ = 0;
176 state_ = STATE_IDLE;
179 void EdgeEffect::Pull(base::TimeTicks current_time, float delta_distance) {
180 if (state_ == STATE_PULL_DECAY && current_time - start_time_ < duration_) {
181 return;
183 if (state_ != STATE_PULL) {
184 glow_scale_y_ = kPullGlowBegin;
186 state_ = STATE_PULL;
188 start_time_ = current_time;
189 duration_ = base::TimeDelta::FromMilliseconds(kPullTime);
191 float abs_delta_distance = std::abs(delta_distance);
192 pull_distance_ += delta_distance;
193 float distance = std::abs(pull_distance_);
195 edge_alpha_ = edge_alpha_start_ = Clamp(distance, kPullEdgeBegin, kMaxAlpha);
196 edge_scale_y_ = edge_scale_y_start_
197 = Clamp(distance * kPullDistanceEdgeFactor, kHeldEdgeScaleY, 1.f);
199 glow_alpha_ = glow_alpha_start_ =
200 std::min(kMaxAlpha,
201 glow_alpha_ + abs_delta_distance * kPullDistanceAlphaGlowFactor);
203 float glow_change = abs_delta_distance;
204 if (delta_distance > 0 && pull_distance_ < 0)
205 glow_change = -glow_change;
206 if (pull_distance_ == 0)
207 glow_scale_y_ = 0;
209 // Do not allow glow to get larger than kMaxGlowHeight.
210 glow_scale_y_ = glow_scale_y_start_ =
211 Clamp(glow_scale_y_ + glow_change * kPullDistanceGlowFactor,
212 0.f, kMaxGlowHeight);
214 edge_alpha_finish_ = edge_alpha_;
215 edge_scale_y_finish_ = edge_scale_y_;
216 glow_alpha_finish_ = glow_alpha_;
217 glow_scale_y_finish_ = glow_scale_y_;
220 void EdgeEffect::Release(base::TimeTicks current_time) {
221 pull_distance_ = 0;
223 if (state_ != STATE_PULL && state_ != STATE_PULL_DECAY)
224 return;
226 state_ = STATE_RECEDE;
227 edge_alpha_start_ = edge_alpha_;
228 edge_scale_y_start_ = edge_scale_y_;
229 glow_alpha_start_ = glow_alpha_;
230 glow_scale_y_start_ = glow_scale_y_;
232 edge_alpha_finish_ = 0.f;
233 edge_scale_y_finish_ = 0.f;
234 glow_alpha_finish_ = 0.f;
235 glow_scale_y_finish_ = 0.f;
237 start_time_ = current_time;
238 duration_ = base::TimeDelta::FromMilliseconds(kRecedeTime);
241 void EdgeEffect::Absorb(base::TimeTicks current_time, float velocity) {
242 state_ = STATE_ABSORB;
243 velocity = Clamp(std::abs(velocity), kMinVelocity, kMaxVelocity);
245 start_time_ = current_time;
246 // This should never be less than 1 millisecond.
247 duration_ = base::TimeDelta::FromMilliseconds(0.15f + (velocity * 0.02f));
249 // The edge should always be at least partially visible, regardless
250 // of velocity.
251 edge_alpha_start_ = 0.f;
252 edge_scale_y_ = edge_scale_y_start_ = 0.f;
253 // The glow depends more on the velocity, and therefore starts out
254 // nearly invisible.
255 glow_alpha_start_ = 0.3f;
256 glow_scale_y_start_ = 0.f;
258 // Factor the velocity by 8. Testing on device shows this works best to
259 // reflect the strength of the user's scrolling.
260 edge_alpha_finish_ = Clamp(velocity * kVelocityEdgeFactor, 0.f, 1.f);
261 // Edge should never get larger than the size of its asset.
262 edge_scale_y_finish_ = Clamp(velocity * kVelocityEdgeFactor,
263 kHeldEdgeScaleY, 1.f);
265 // Growth for the size of the glow should be quadratic to properly
266 // respond
267 // to a user's scrolling speed. The faster the scrolling speed, the more
268 // intense the effect should be for both the size and the saturation.
269 glow_scale_y_finish_ = std::min(
270 0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f);
271 // Alpha should change for the glow as well as size.
272 glow_alpha_finish_ = Clamp(glow_alpha_start_,
273 velocity * kVelocityGlowFactor * .00001f,
274 kMaxAlpha);
277 bool EdgeEffect::Update(base::TimeTicks current_time) {
278 if (IsFinished())
279 return false;
281 const double dt = (current_time - start_time_).InMilliseconds();
282 const double t = std::min(dt / duration_.InMilliseconds(), 1.);
283 const float interp = static_cast<float>(Damp(t, 1.));
285 edge_alpha_ = Lerp(edge_alpha_start_, edge_alpha_finish_, interp);
286 edge_scale_y_ = Lerp(edge_scale_y_start_, edge_scale_y_finish_, interp);
287 glow_alpha_ = Lerp(glow_alpha_start_, glow_alpha_finish_, interp);
288 glow_scale_y_ = Lerp(glow_scale_y_start_, glow_scale_y_finish_, interp);
290 if (t >= 1.f - kEpsilon) {
291 switch (state_) {
292 case STATE_ABSORB:
293 state_ = STATE_RECEDE;
294 start_time_ = current_time;
295 duration_ = base::TimeDelta::FromMilliseconds(kRecedeTime);
297 edge_alpha_start_ = edge_alpha_;
298 edge_scale_y_start_ = edge_scale_y_;
299 glow_alpha_start_ = glow_alpha_;
300 glow_scale_y_start_ = glow_scale_y_;
302 // After absorb, the glow and edge should fade to nothing.
303 edge_alpha_finish_ = 0.f;
304 edge_scale_y_finish_ = 0.f;
305 glow_alpha_finish_ = 0.f;
306 glow_scale_y_finish_ = 0.f;
307 break;
308 case STATE_PULL:
309 state_ = STATE_PULL_DECAY;
310 start_time_ = current_time;
311 duration_ = base::TimeDelta::FromMilliseconds(kPullDecayTime);
313 edge_alpha_start_ = edge_alpha_;
314 edge_scale_y_start_ = edge_scale_y_;
315 glow_alpha_start_ = glow_alpha_;
316 glow_scale_y_start_ = glow_scale_y_;
318 // After pull, the glow and edge should fade to nothing.
319 edge_alpha_finish_ = 0.f;
320 edge_scale_y_finish_ = 0.f;
321 glow_alpha_finish_ = 0.f;
322 glow_scale_y_finish_ = 0.f;
323 break;
324 case STATE_PULL_DECAY: {
325 // When receding, we want edge to decrease more slowly
326 // than the glow.
327 const float factor = glow_scale_y_finish_ != 0 ?
328 1 / (glow_scale_y_finish_ * glow_scale_y_finish_) :
329 std::numeric_limits<float>::max();
330 edge_scale_y_ = edge_scale_y_start_ +
331 (edge_scale_y_finish_ - edge_scale_y_start_) * interp * factor;
332 state_ = STATE_RECEDE;
333 } break;
334 case STATE_RECEDE:
335 Finish();
336 break;
337 default:
338 break;
342 if (state_ == STATE_RECEDE && glow_scale_y_ <= 0 && edge_scale_y_ <= 0)
343 Finish();
345 return !IsFinished();
348 void EdgeEffect::ApplyToLayers(gfx::SizeF window_size,
349 Edge edge,
350 float edge_height,
351 float glow_height,
352 float offset) {
353 if (IsFinished())
354 return;
356 // An empty window size, while meaningless, is also relatively harmless, and
357 // will simply prevent any drawing of the layers.
358 if (window_size.IsEmpty()) {
359 DisableLayer(edge_.get());
360 DisableLayer(glow_.get());
361 return;
364 // Glow
365 const int scaled_glow_height = static_cast<int>(
366 std::min(glow_height * glow_scale_y_ * kGlowHeightToWidthRatio * 0.6f,
367 glow_height * kMaxGlowHeight) + 0.5f);
368 UpdateLayer(
369 glow_.get(), edge, window_size, offset, scaled_glow_height, glow_alpha_);
371 // Edge
372 const int scaled_edge_height = static_cast<int>(edge_height * edge_scale_y_);
373 UpdateLayer(
374 edge_.get(), edge, window_size, offset, scaled_edge_height, edge_alpha_);
377 } // namespace content