edge_effect.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// found in the LICENSE file.
490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "content/browser/android/edge_effect.h"
690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "cc/layers/layer.h"
890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "ui/gfx/screen.h"
990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
1090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)namespace content {
1190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
1290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)namespace {
1390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
1490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)enum State {
1590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  STATE_IDLE = 0,
1690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  STATE_PULL,
1790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  STATE_ABSORB,
1890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  STATE_RECEDE,
1990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  STATE_PULL_DECAY
2090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)};
2190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
2290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Time it will take the effect to fully recede in ms
2390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const int kRecedeTime = 1000;
2490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
2590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Time it will take before a pulled glow begins receding in ms
2690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const int kPullTime = 167;
2790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
2890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Time it will take in ms for a pulled glow to decay before release
2990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const int kPullDecayTime = 1000;
3090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
3190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const float kMaxAlpha = 1.f;
3290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const float kHeldEdgeScaleY = .5f;
3390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
3490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const float kMaxGlowHeight = 4.f;
3590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
3690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Note: The Android version computes the aspect ratio from the source texture;
3790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// because we use rescaled images, this is precomputed from the original Android
3890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// textures.
3990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const float kGlowImageAspectRatioInverse = 0.25f;
4090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
4190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const float kPullGlowBegin = 1.f;
4290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const float kPullEdgeBegin = 0.6f;
4390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
4490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Minimum velocity that will be absorbed
4590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const float kMinVelocity = 100.f;
4690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
4790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const float kEpsilon = 0.001f;
4890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
4990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// How much dragging should effect the height of the edge image.
5090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Number determined by user testing.
5190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const int kPullDistanceEdgeFactor = 7;
5290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
5390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// How much dragging should effect the height of the glow image.
5490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Number determined by user testing.
5590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const int kPullDistanceGlowFactor = 7;
5690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const float kPullDistanceAlphaGlowFactor = 1.1f;
5790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
5890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const int kVelocityEdgeFactor = 8;
5990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const int kVelocityGlowFactor = 16;
6090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
6190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)template <typename T>
6290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)T Lerp(T a, T b, T t) {
6390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  return a + (b - a) * t;
6490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
6590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
6690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)template <typename T>
6790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)T Clamp(T value, T low, T high) {
6890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)   return value < low ? low : (value > high ? high : value);
6990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
7090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
7190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)template <typename T>
7290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)T Damp(T input, T factor) {
7390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  T result;
7490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (factor == 1) {
7590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    result = 1 - (1 - input) * (1 - input);
7690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  } else {
7790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    result = 1 - std::pow(1 - input, 2 * factor);
7890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
7990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  return result;
8090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
8190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
8290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)gfx::Transform ComputeTransform(EdgeEffect::Edge edge,
8390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                gfx::SizeF size, int height) {
8490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  switch (edge) {
8590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    default:
8690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    case EdgeEffect::EDGE_TOP:
8790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      return gfx::Transform(1, 0, 0, 1, 0, 0);
8890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    case EdgeEffect::EDGE_LEFT:
8990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      return gfx::Transform(0, 1, -1, 0,
9090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                            (-size.width() + height) / 2 ,
9190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                            (size.width() - height) / 2);
9290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    case EdgeEffect::EDGE_BOTTOM:
9390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      return gfx::Transform(-1, 0, 0, -1, 0, size.height() - height);
9490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    case EdgeEffect::EDGE_RIGHT:
9590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      return gfx::Transform(0, -1, 1, 0,
9690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                            (-size.width() - height) / 2 + size.height(),
9790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                            (size.width() - height) / 2);
9890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  };
9990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
10090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
10190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void DisableLayer(cc::Layer* layer) {
10290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DCHECK(layer);
10390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  layer->SetIsDrawable(false);
10490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  layer->SetTransform(gfx::Transform());
10590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  layer->SetOpacity(1.f);
10690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
10790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
10890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void UpdateLayer(cc::Layer* layer,
10990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 EdgeEffect::Edge edge,
11090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 gfx::SizeF size,
11190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 int height,
11290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 float opacity) {
11390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DCHECK(layer);
11490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  layer->SetIsDrawable(true);
11590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  layer->SetTransform(ComputeTransform(edge, size, height));
11690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  layer->SetBounds(gfx::Size(size.width(), height));
11790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  layer->SetOpacity(Clamp(opacity, 0.f, 1.f));
11890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
11990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
12090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} // namespace
12190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
12290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)EdgeEffect::EdgeEffect(scoped_refptr<cc::Layer> edge,
12390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                       scoped_refptr<cc::Layer> glow)
12490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  : edge_(edge)
12590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  , glow_(glow)
12690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  , edge_alpha_(0)
12790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  , edge_scale_y_(0)
12890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  , glow_alpha_(0)
12990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  , glow_scale_y_(0)
13090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  , edge_alpha_start_(0)
13190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  , edge_alpha_finish_(0)
13290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  , edge_scale_y_start_(0)
13390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  , edge_scale_y_finish_(0)
13490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  , glow_alpha_start_(0)
13590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  , glow_alpha_finish_(0)
13690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  , glow_scale_y_start_(0)
13790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  , glow_scale_y_finish_(0)
13890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  , state_(STATE_IDLE)
13990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  , pull_distance_(0)
14090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  , dpi_scale_(1) {
14190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Prevent the provided layers from drawing until the effect is activated.
14290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DisableLayer(edge_);
14390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DisableLayer(glow_);
14490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
14590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  dpi_scale_ =
14690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().device_scale_factor();
14790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
14890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
14990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)EdgeEffect::~EdgeEffect() { }
15090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
15190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)bool EdgeEffect::IsFinished() const {
15290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  return state_ == STATE_IDLE;
15390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
15490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
15590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void EdgeEffect::Finish() {
15690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DisableLayer(edge_);
15790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  DisableLayer(glow_);
15890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  pull_distance_ = 0;
15990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  state_ = STATE_IDLE;
16090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
16190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
16290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void EdgeEffect::Pull(base::TimeTicks current_time, float delta_distance) {
16390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (state_ == STATE_PULL_DECAY && current_time - start_time_ < duration_) {
16490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return;
16590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
16690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (state_ != STATE_PULL) {
16790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    glow_scale_y_ = kPullGlowBegin;
16890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
16990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  state_ = STATE_PULL;
17090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
17190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  start_time_ = current_time;
17290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  duration_ = base::TimeDelta::FromMilliseconds(kPullTime);
17390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
17490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  delta_distance *= dpi_scale_;
17590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  float abs_delta_distance = std::abs(delta_distance);
17690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  pull_distance_ += delta_distance;
17790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  float distance = std::abs(pull_distance_);
17890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
17990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  edge_alpha_ = edge_alpha_start_ = Clamp(distance, kPullEdgeBegin, kMaxAlpha);
18090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  edge_scale_y_ = edge_scale_y_start_
18190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      = Clamp(distance * kPullDistanceEdgeFactor, kHeldEdgeScaleY, 1.f);
18290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
18390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  glow_alpha_ = glow_alpha_start_ =
18490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      std::min(kMaxAlpha,
18590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)               glow_alpha_ + abs_delta_distance * kPullDistanceAlphaGlowFactor);
18690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
18790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  float glow_change = abs_delta_distance;
18890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (delta_distance > 0 && pull_distance_ < 0)
18990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    glow_change = -glow_change;
19090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (pull_distance_ == 0)
19190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    glow_scale_y_ = 0;
19290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
19390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Do not allow glow to get larger than kMaxGlowHeight.
19490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  glow_scale_y_ = glow_scale_y_start_ =
19590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      Clamp(glow_scale_y_ + glow_change * kPullDistanceGlowFactor,
19690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            0.f, kMaxGlowHeight);
19790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
19890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  edge_alpha_finish_ = edge_alpha_;
19990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  edge_scale_y_finish_ = edge_scale_y_;
20090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  glow_alpha_finish_ = glow_alpha_;
20190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  glow_scale_y_finish_ = glow_scale_y_;
20290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
20390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
20490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void EdgeEffect::Release(base::TimeTicks current_time) {
20590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  pull_distance_ = 0;
20690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
20790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (state_ != STATE_PULL && state_ != STATE_PULL_DECAY)
20890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return;
20990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
21090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  state_ = STATE_RECEDE;
21190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  edge_alpha_start_ = edge_alpha_;
21290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  edge_scale_y_start_ = edge_scale_y_;
21390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  glow_alpha_start_ = glow_alpha_;
21490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  glow_scale_y_start_ = glow_scale_y_;
21590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
21690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  edge_alpha_finish_ = 0.f;
21790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  edge_scale_y_finish_ = 0.f;
21890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  glow_alpha_finish_ = 0.f;
21990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  glow_scale_y_finish_ = 0.f;
22090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
22190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  start_time_ = current_time;
22290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  duration_ = base::TimeDelta::FromMilliseconds(kRecedeTime);
22390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
22490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
22590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void EdgeEffect::Absorb(base::TimeTicks current_time, float velocity) {
22690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  state_ = STATE_ABSORB;
22790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  velocity = dpi_scale_ * std::max(kMinVelocity, std::abs(velocity));
22890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
22990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  start_time_ = current_time;
23090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // This should never be less than 1 millisecond.
23190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  duration_ = base::TimeDelta::FromMilliseconds(0.1f + (velocity * 0.03f));
23290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
23390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // The edge should always be at least partially visible, regardless
23490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // of velocity.
23590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  edge_alpha_start_ = 0.f;
23690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  edge_scale_y_ = edge_scale_y_start_ = 0.f;
23790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // The glow depends more on the velocity, and therefore starts out
23890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // nearly invisible.
23990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  glow_alpha_start_ = 0.5f;
24090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  glow_scale_y_start_ = 0.f;
24190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
24290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Factor the velocity by 8. Testing on device shows this works best to
24390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // reflect the strength of the user's scrolling.
24490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  edge_alpha_finish_ = Clamp(velocity * kVelocityEdgeFactor, 0.f, 1.f);
24590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Edge should never get larger than the size of its asset.
24690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  edge_scale_y_finish_ = Clamp(velocity * kVelocityEdgeFactor,
24790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                               kHeldEdgeScaleY, 1.f);
24890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
24990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Growth for the size of the glow should be quadratic to properly
25090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // respond
25190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // to a user's scrolling speed. The faster the scrolling speed, the more
25290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // intense the effect should be for both the size and the saturation.
25390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  glow_scale_y_finish_ =
25490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      std::min(0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f);
25590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Alpha should change for the glow as well as size.
25690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  glow_alpha_finish_ =
25790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      Clamp(glow_alpha_start_,
25890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            velocity * kVelocityGlowFactor * .00001f, kMaxAlpha);
25990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
26090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
26190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)bool EdgeEffect::Update(base::TimeTicks current_time) {
26290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (IsFinished())
26390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return false;
26490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
26590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  const double dt = (current_time - start_time_).InMilliseconds();
26690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  const double t = std::min(dt / duration_.InMilliseconds(), 1.);
26790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  const float interp = static_cast<float>(Damp(t, 1.));
26890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
26990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  edge_alpha_ = Lerp(edge_alpha_start_, edge_alpha_finish_, interp);
27090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  edge_scale_y_ = Lerp(edge_scale_y_start_, edge_scale_y_finish_, interp);
27190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  glow_alpha_ = Lerp(glow_alpha_start_, glow_alpha_finish_, interp);
27290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  glow_scale_y_ = Lerp(glow_scale_y_start_, glow_scale_y_finish_, interp);
27390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
27490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (t >= 1.f - kEpsilon) {
27590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    switch (state_) {
27690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      case STATE_ABSORB:
27790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        state_ = STATE_RECEDE;
27890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        start_time_ = current_time;
27990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        duration_ = base::TimeDelta::FromMilliseconds(kRecedeTime);
28090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
28190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        edge_alpha_start_ = edge_alpha_;
28290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        edge_scale_y_start_ = edge_scale_y_;
28390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        glow_alpha_start_ = glow_alpha_;
28490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        glow_scale_y_start_ = glow_scale_y_;
28590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
28690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        // After absorb, the glow and edge should fade to nothing.
28790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        edge_alpha_finish_ = 0.f;
28890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        edge_scale_y_finish_ = 0.f;
28990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        glow_alpha_finish_ = 0.f;
29090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        glow_scale_y_finish_ = 0.f;
29190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        break;
29290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      case STATE_PULL:
29390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        state_ = STATE_PULL_DECAY;
29490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        start_time_ = current_time;
29590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        duration_ = base::TimeDelta::FromMilliseconds(kPullDecayTime);
29690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
29790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        edge_alpha_start_ = edge_alpha_;
29890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        edge_scale_y_start_ = edge_scale_y_;
29990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        glow_alpha_start_ = glow_alpha_;
30090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        glow_scale_y_start_ = glow_scale_y_;
30190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
30290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        // After pull, the glow and edge should fade to nothing.
30390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        edge_alpha_finish_ = 0.f;
30490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        edge_scale_y_finish_ = 0.f;
30590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        glow_alpha_finish_ = 0.f;
30690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        glow_scale_y_finish_ = 0.f;
30790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        break;
30890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      case STATE_PULL_DECAY:
30990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        {
31090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          // When receding, we want edge to decrease more slowly
31190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          // than the glow.
31290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          float factor = glow_scale_y_finish_ != 0 ?
31390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)              1 / (glow_scale_y_finish_ * glow_scale_y_finish_) :
31490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)              std::numeric_limits<float>::max();
31590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          edge_scale_y_ = edge_scale_y_start_ +
31690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)              (edge_scale_y_finish_ - edge_scale_y_start_) * interp * factor;
31790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          state_ = STATE_RECEDE;
31890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        }
31990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        break;
32090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      case STATE_RECEDE:
32190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        Finish();
32290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        break;
32390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      default:
32490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        break;
32590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    }
32690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
32790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
32890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (state_ == STATE_RECEDE && glow_scale_y_ <= 0 && edge_scale_y_ <= 0)
32990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    Finish();
33090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
33190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  return !IsFinished();
33290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
33390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
33490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void EdgeEffect::ApplyToLayers(gfx::SizeF size, Edge edge) {
33590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (IsFinished())
33690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return;
33790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
33890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // An empty effect size, while meaningless, is also relatively harmless, and
33990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // will simply prevent any drawing of the layers.
34090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (size.IsEmpty()) {
34190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    DisableLayer(edge_);
34290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    DisableLayer(glow_);
34390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return;
34490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
34590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
34690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  float dummy_scale_x, dummy_scale_y;
34790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
34890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Glow
34990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  gfx::Size glow_image_bounds;
35090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  glow_->CalculateContentsScale(1.f, 1.f, 1.f, false,
35190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                &dummy_scale_x, &dummy_scale_y,
35290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                &glow_image_bounds);
35390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  const int glow_height = glow_image_bounds.height();
35490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  const int glow_bottom = static_cast<int>(std::min(
35590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      glow_height * glow_scale_y_ * kGlowImageAspectRatioInverse * 0.6f,
35690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      glow_height * kMaxGlowHeight) * dpi_scale_ + 0.5f);
35790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  UpdateLayer(glow_, edge, size, glow_bottom, glow_alpha_);
35890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
35990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Edge
36090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  gfx::Size edge_image_bounds;
36190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  edge_->CalculateContentsScale(1.f, 1.f, 1.f, false,
36290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                &dummy_scale_x, &dummy_scale_y,
36390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                &edge_image_bounds);
36490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  const int edge_height = edge_image_bounds.height();
36590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  const int edge_bottom = static_cast<int>(
36690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      edge_height * edge_scale_y_ * dpi_scale_);
36790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  UpdateLayer(edge_, edge, size, edge_bottom, edge_alpha_);
36890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
36990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
37090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)} // namespace content
371