1 // Copyright 2014 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/edge_effect_l.h" 6 7 #include "cc/layers/ui_resource_layer.h" 8 #include "ui/base/android/system_ui_resource_manager.h" 9 10 namespace content { 11 12 namespace { 13 14 // Time it will take the effect to fully recede in ms 15 const int kRecedeTimeMs = 600; 16 17 // Time it will take before a pulled glow begins receding in ms 18 const int kPullTimeMs = 167; 19 20 // Time it will take for a pulled glow to decay to partial strength before 21 // release 22 const int kPullDecayTimeMs = 2000; 23 24 const float kMaxAlpha = 0.5f; 25 26 const float kPullGlowBegin = 0.f; 27 28 // Min/max velocity that will be absorbed 29 const float kMinVelocity = 100.f; 30 const float kMaxVelocity = 10000.f; 31 32 const float kEpsilon = 0.001f; 33 34 const float kSin = 0.5f; // sin(PI / 6) 35 const float kCos = 0.866f; // cos(PI / 6); 36 37 // How much dragging should effect the height of the glow image. 38 // Number determined by user testing. 39 const float kPullDistanceAlphaGlowFactor = 0.8f; 40 41 const int kVelocityGlowFactor = 6; 42 43 const ui::SystemUIResourceManager::ResourceType kResourceType = 44 ui::SystemUIResourceManager::OVERSCROLL_GLOW_L; 45 46 template <typename T> 47 T Lerp(T a, T b, T t) { 48 return a + (b - a) * t; 49 } 50 51 template <typename T> 52 T Clamp(T value, T low, T high) { 53 return value < low ? low : (value > high ? high : value); 54 } 55 56 template <typename T> 57 T Damp(T input, T factor) { 58 T result; 59 if (factor == 1) { 60 result = 1 - (1 - input) * (1 - input); 61 } else { 62 result = 1 - std::pow(1 - input, 2 * factor); 63 } 64 return result; 65 } 66 67 } // namespace 68 69 EdgeEffectL::EdgeEffectL(ui::SystemUIResourceManager* resource_manager) 70 : resource_manager_(resource_manager), 71 glow_(cc::UIResourceLayer::Create()), 72 glow_alpha_(0), 73 glow_scale_y_(0), 74 glow_alpha_start_(0), 75 glow_alpha_finish_(0), 76 glow_scale_y_start_(0), 77 glow_scale_y_finish_(0), 78 displacement_(0.5f), 79 target_displacement_(0.5f), 80 state_(STATE_IDLE), 81 pull_distance_(0) { 82 // Prevent the provided layers from drawing until the effect is activated. 83 glow_->SetIsDrawable(false); 84 } 85 86 EdgeEffectL::~EdgeEffectL() { 87 glow_->RemoveFromParent(); 88 } 89 90 bool EdgeEffectL::IsFinished() const { 91 return state_ == STATE_IDLE; 92 } 93 94 void EdgeEffectL::Finish() { 95 glow_->SetIsDrawable(false); 96 pull_distance_ = 0; 97 state_ = STATE_IDLE; 98 } 99 100 void EdgeEffectL::Pull(base::TimeTicks current_time, 101 float delta_distance, 102 float displacement) { 103 target_displacement_ = displacement; 104 if (state_ == STATE_PULL_DECAY && current_time - start_time_ < duration_) { 105 return; 106 } 107 if (state_ != STATE_PULL) { 108 glow_scale_y_ = std::max(kPullGlowBegin, glow_scale_y_); 109 } 110 state_ = STATE_PULL; 111 112 start_time_ = current_time; 113 duration_ = base::TimeDelta::FromMilliseconds(kPullTimeMs); 114 115 float abs_delta_distance = std::abs(delta_distance); 116 pull_distance_ += delta_distance; 117 118 glow_alpha_ = glow_alpha_start_ = std::min( 119 kMaxAlpha, 120 glow_alpha_ + (abs_delta_distance * kPullDistanceAlphaGlowFactor)); 121 122 if (pull_distance_ == 0) { 123 glow_scale_y_ = glow_scale_y_start_ = 0; 124 } else { 125 float scale = 1.f - 126 1.f / std::sqrt(std::abs(pull_distance_) * bounds_.height()) - 127 0.3f; 128 glow_scale_y_ = glow_scale_y_start_ = std::max(0.f, scale) / 0.7f; 129 } 130 131 glow_alpha_finish_ = glow_alpha_; 132 glow_scale_y_finish_ = glow_scale_y_; 133 } 134 135 void EdgeEffectL::Release(base::TimeTicks current_time) { 136 pull_distance_ = 0; 137 138 if (state_ != STATE_PULL && state_ != STATE_PULL_DECAY) 139 return; 140 141 state_ = STATE_RECEDE; 142 glow_alpha_start_ = glow_alpha_; 143 glow_scale_y_start_ = glow_scale_y_; 144 145 glow_alpha_finish_ = 0.f; 146 glow_scale_y_finish_ = 0.f; 147 148 start_time_ = current_time; 149 duration_ = base::TimeDelta::FromMilliseconds(kRecedeTimeMs); 150 } 151 152 void EdgeEffectL::Absorb(base::TimeTicks current_time, float velocity) { 153 state_ = STATE_ABSORB; 154 155 velocity = Clamp(std::abs(velocity), kMinVelocity, kMaxVelocity); 156 157 start_time_ = current_time; 158 // This should never be less than 1 millisecond. 159 duration_ = base::TimeDelta::FromMilliseconds(0.15f + (velocity * 0.02f)); 160 161 // The glow depends more on the velocity, and therefore starts out 162 // nearly invisible. 163 glow_alpha_start_ = 0.3f; 164 glow_scale_y_start_ = std::max(glow_scale_y_, 0.f); 165 166 // Growth for the size of the glow should be quadratic to properly respond 167 // to a user's scrolling speed. The faster the scrolling speed, the more 168 // intense the effect should be for both the size and the saturation. 169 glow_scale_y_finish_ = 170 std::min(0.025f + (velocity * (velocity / 100) * 0.00015f) / 2.f, 1.f); 171 // Alpha should change for the glow as well as size. 172 glow_alpha_finish_ = Clamp( 173 glow_alpha_start_, velocity * kVelocityGlowFactor * .00001f, kMaxAlpha); 174 target_displacement_ = 0.5; 175 } 176 177 bool EdgeEffectL::Update(base::TimeTicks current_time) { 178 if (IsFinished()) 179 return false; 180 181 const double dt = (current_time - start_time_).InMilliseconds(); 182 const double t = std::min(dt / duration_.InMilliseconds(), 1.); 183 const float interp = static_cast<float>(Damp(t, 1.)); 184 185 glow_alpha_ = Lerp(glow_alpha_start_, glow_alpha_finish_, interp); 186 glow_scale_y_ = Lerp(glow_scale_y_start_, glow_scale_y_finish_, interp); 187 displacement_ = (displacement_ + target_displacement_) / 2.f; 188 189 if (t >= 1.f - kEpsilon) { 190 switch (state_) { 191 case STATE_ABSORB: 192 state_ = STATE_RECEDE; 193 start_time_ = current_time; 194 duration_ = base::TimeDelta::FromMilliseconds(kRecedeTimeMs); 195 196 glow_alpha_start_ = glow_alpha_; 197 glow_scale_y_start_ = glow_scale_y_; 198 199 glow_alpha_finish_ = 0.f; 200 glow_scale_y_finish_ = 0.f; 201 break; 202 case STATE_PULL: 203 state_ = STATE_PULL_DECAY; 204 start_time_ = current_time; 205 duration_ = base::TimeDelta::FromMilliseconds(kPullDecayTimeMs); 206 207 glow_alpha_start_ = glow_alpha_; 208 glow_scale_y_start_ = glow_scale_y_; 209 210 // After pull, the glow should fade to nothing. 211 glow_alpha_finish_ = 0.f; 212 glow_scale_y_finish_ = 0.f; 213 break; 214 case STATE_PULL_DECAY: 215 state_ = STATE_RECEDE; 216 break; 217 case STATE_RECEDE: 218 Finish(); 219 break; 220 default: 221 break; 222 } 223 } 224 225 bool one_last_frame = false; 226 if (state_ == STATE_RECEDE && glow_scale_y_ <= 0) { 227 Finish(); 228 one_last_frame = true; 229 } 230 231 return !IsFinished() || one_last_frame; 232 } 233 234 void EdgeEffectL::ApplyToLayers(const gfx::SizeF& size, 235 const gfx::Transform& transform) { 236 if (IsFinished()) 237 return; 238 239 // An empty window size, while meaningless, is also relatively harmless, and 240 // will simply prevent any drawing of the layers. 241 if (size.IsEmpty()) { 242 glow_->SetIsDrawable(false); 243 return; 244 } 245 246 const float r = size.width() * 0.75f / kSin; 247 const float y = kCos * r; 248 const float h = r - y; 249 const float o_r = size.height() * 0.75f / kSin; 250 const float o_y = kCos * o_r; 251 const float o_h = o_r - o_y; 252 const float base_glow_scale = h > 0.f ? std::min(o_h / h, 1.f) : 1.f; 253 bounds_ = gfx::Size(size.width(), (int)std::min(size.height(), h)); 254 gfx::Size image_bounds( 255 r, std::min(1.f, glow_scale_y_) * base_glow_scale * bounds_.height()); 256 257 glow_->SetIsDrawable(true); 258 glow_->SetUIResourceId(resource_manager_->GetUIResourceId(kResourceType)); 259 glow_->SetTransformOrigin(gfx::Point3F(bounds_.width() * 0.5f, 0, 0)); 260 glow_->SetBounds(image_bounds); 261 glow_->SetContentsOpaque(false); 262 glow_->SetOpacity(Clamp(glow_alpha_, 0.f, 1.f)); 263 264 const float displacement = Clamp(displacement_, 0.f, 1.f) - 0.5f; 265 const float displacement_offset_x = bounds_.width() * displacement * 0.5f; 266 const float image_offset_x = (bounds_.width() - image_bounds.width()) * 0.5f; 267 gfx::Transform offset_transform; 268 offset_transform.Translate(image_offset_x - displacement_offset_x, 0); 269 offset_transform.ConcatTransform(transform); 270 glow_->SetTransform(offset_transform); 271 } 272 273 void EdgeEffectL::SetParent(cc::Layer* parent) { 274 if (glow_->parent() != parent) 275 parent->AddChild(glow_); 276 glow_->SetUIResourceId(resource_manager_->GetUIResourceId(kResourceType)); 277 } 278 279 // static 280 void EdgeEffectL::PreloadResources( 281 ui::SystemUIResourceManager* resource_manager) { 282 DCHECK(resource_manager); 283 resource_manager->PreloadResource(kResourceType); 284 } 285 286 } // namespace content 287