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 "base/debug/trace_event.h" 8 #include "base/lazy_instance.h" 9 #include "base/threading/worker_pool.h" 10 #include "cc/layers/image_layer.h" 11 #include "content/browser/android/edge_effect.h" 12 #include "ui/gfx/android/java_bitmap.h" 13 14 using std::max; 15 using std::min; 16 17 namespace content { 18 19 namespace { 20 21 const float kEpsilon = 1e-3f; 22 23 class OverscrollResources { 24 public: 25 OverscrollResources() { 26 TRACE_EVENT0("browser", "OverscrollResources::Create"); 27 edge_bitmap_ = 28 gfx::CreateSkBitmapFromResource("android:drawable/overscroll_edge", 29 gfx::Size(128, 12)); 30 glow_bitmap_ = 31 gfx::CreateSkBitmapFromResource("android:drawable/overscroll_glow", 32 gfx::Size(128, 64)); 33 } 34 35 const SkBitmap& edge_bitmap() { return edge_bitmap_; } 36 const SkBitmap& glow_bitmap() { return glow_bitmap_; } 37 38 private: 39 SkBitmap edge_bitmap_; 40 SkBitmap glow_bitmap_; 41 42 DISALLOW_COPY_AND_ASSIGN(OverscrollResources); 43 }; 44 45 // Leaky to allow access from a worker thread. 46 base::LazyInstance<OverscrollResources>::Leaky g_overscroll_resources = 47 LAZY_INSTANCE_INITIALIZER; 48 49 scoped_refptr<cc::Layer> CreateImageLayer(const SkBitmap& bitmap) { 50 scoped_refptr<cc::ImageLayer> layer = cc::ImageLayer::Create(); 51 layer->SetBitmap(bitmap); 52 return layer; 53 } 54 55 bool IsApproxZero(float value) { 56 return std::abs(value) < kEpsilon; 57 } 58 59 gfx::Vector2dF ZeroSmallComponents(gfx::Vector2dF vector) { 60 if (IsApproxZero(vector.x())) 61 vector.set_x(0); 62 if (IsApproxZero(vector.y())) 63 vector.set_y(0); 64 return vector; 65 } 66 67 // Force loading of any necessary resources. This function is thread-safe. 68 void EnsureResources() { 69 g_overscroll_resources.Get(); 70 } 71 72 } // namespace 73 74 scoped_ptr<OverscrollGlow> OverscrollGlow::Create(bool enabled) { 75 // Don't block the main thread with effect resource loading during creation. 76 // Effect instantiation is deferred until the effect overscrolls, in which 77 // case the main thread may block until the resource has loaded. 78 if (enabled && g_overscroll_resources == NULL) 79 base::WorkerPool::PostTask(FROM_HERE, base::Bind(EnsureResources), true); 80 81 return make_scoped_ptr(new OverscrollGlow(enabled)); 82 } 83 84 OverscrollGlow::OverscrollGlow(bool enabled) 85 : enabled_(enabled), 86 initialized_(false), 87 horizontal_overscroll_enabled_(true), 88 vertical_overscroll_enabled_(true) {} 89 90 OverscrollGlow::~OverscrollGlow() { 91 Detach(); 92 } 93 94 void OverscrollGlow::Enable() { 95 enabled_ = true; 96 } 97 98 void OverscrollGlow::Disable() { 99 if (!enabled_) 100 return; 101 enabled_ = false; 102 if (!enabled_ && initialized_) { 103 Detach(); 104 for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) 105 edge_effects_[i]->Finish(); 106 } 107 } 108 109 bool OverscrollGlow::OnOverscrolled(cc::Layer* overscrolling_layer, 110 base::TimeTicks current_time, 111 gfx::Vector2dF overscroll, 112 gfx::Vector2dF velocity) { 113 DCHECK(overscrolling_layer); 114 115 if (!enabled_) 116 return false; 117 118 // The size of the glow determines the relative effect of the inputs; an 119 // empty-sized effect is effectively disabled. 120 if (size_.IsEmpty()) 121 return false; 122 123 if (!horizontal_overscroll_enabled_) { 124 overscroll.set_x(0); 125 velocity.set_x(0); 126 } 127 if (!vertical_overscroll_enabled_) { 128 overscroll.set_y(0); 129 velocity.set_y(0); 130 } 131 132 // Ignore sufficiently small values that won't meaningfuly affect animation. 133 overscroll = ZeroSmallComponents(overscroll); 134 velocity = ZeroSmallComponents(velocity); 135 136 if (overscroll.IsZero()) { 137 if (initialized_) { 138 Release(current_time); 139 UpdateLayerAttachment(overscrolling_layer); 140 } 141 return NeedsAnimate(); 142 } 143 144 if (!InitializeIfNecessary()) 145 return false; 146 147 if (!velocity.IsZero()) { 148 // Release effects if scrolling has changed directions. 149 if (velocity.x() * old_velocity_.x() < 0) 150 ReleaseAxis(AXIS_X, current_time); 151 if (velocity.y() * old_velocity_.y() < 0) 152 ReleaseAxis(AXIS_Y, current_time); 153 154 Absorb(current_time, velocity, overscroll, old_overscroll_); 155 } else { 156 // Release effects when overscroll accumulation violates monotonicity. 157 if (overscroll.x() * old_overscroll_.x() < 0 || 158 std::abs(overscroll.x()) < std::abs(old_overscroll_.x())) 159 ReleaseAxis(AXIS_X, current_time); 160 if (overscroll.y() * old_overscroll_.y() < 0 || 161 std::abs(overscroll.y()) < std::abs(old_overscroll_.y())) 162 ReleaseAxis(AXIS_Y, current_time); 163 164 Pull(current_time, overscroll - old_overscroll_); 165 } 166 167 old_velocity_ = velocity; 168 old_overscroll_ = overscroll; 169 170 UpdateLayerAttachment(overscrolling_layer); 171 return NeedsAnimate(); 172 } 173 174 bool OverscrollGlow::Animate(base::TimeTicks current_time) { 175 if (!NeedsAnimate()) { 176 Detach(); 177 return false; 178 } 179 180 const gfx::SizeF sizes[EdgeEffect::EDGE_COUNT] = { 181 size_, gfx::SizeF(size_.height(), size_.width()), 182 size_, gfx::SizeF(size_.height(), size_.width()) 183 }; 184 185 for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) { 186 if (edge_effects_[i]->Update(current_time)) { 187 edge_effects_[i]->ApplyToLayers(sizes[i], 188 static_cast<EdgeEffect::Edge>(i)); 189 } 190 } 191 192 if (!NeedsAnimate()) { 193 Detach(); 194 return false; 195 } 196 197 return true; 198 } 199 200 bool OverscrollGlow::NeedsAnimate() const { 201 if (!enabled_ || !initialized_) 202 return false; 203 for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) { 204 if (!edge_effects_[i]->IsFinished()) 205 return true; 206 } 207 return false; 208 } 209 210 void OverscrollGlow::UpdateLayerAttachment(cc::Layer* parent) { 211 DCHECK(parent); 212 if (!root_layer_) 213 return; 214 215 if (!NeedsAnimate()) { 216 Detach(); 217 return; 218 } 219 220 if (root_layer_->parent() != parent) 221 parent->AddChild(root_layer_); 222 } 223 224 void OverscrollGlow::Detach() { 225 if (root_layer_) 226 root_layer_->RemoveFromParent(); 227 } 228 229 bool OverscrollGlow::InitializeIfNecessary() { 230 DCHECK(enabled_); 231 if (initialized_) 232 return true; 233 234 const SkBitmap& edge = g_overscroll_resources.Get().edge_bitmap(); 235 const SkBitmap& glow = g_overscroll_resources.Get().glow_bitmap(); 236 if (edge.isNull() || glow.isNull()) { 237 Disable(); 238 return false; 239 } 240 241 DCHECK(!root_layer_); 242 root_layer_ = cc::Layer::Create(); 243 for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) { 244 scoped_refptr<cc::Layer> edge_layer = CreateImageLayer(edge); 245 scoped_refptr<cc::Layer> glow_layer = CreateImageLayer(glow); 246 root_layer_->AddChild(edge_layer); 247 root_layer_->AddChild(glow_layer); 248 edge_effects_[i] = make_scoped_ptr(new EdgeEffect(edge_layer, glow_layer)); 249 } 250 251 initialized_ = true; 252 return true; 253 } 254 255 void OverscrollGlow::Pull(base::TimeTicks current_time, 256 gfx::Vector2dF overscroll_delta) { 257 DCHECK(enabled_ && initialized_); 258 overscroll_delta = ZeroSmallComponents(overscroll_delta); 259 if (overscroll_delta.IsZero()) 260 return; 261 262 gfx::Vector2dF overscroll_pull = gfx::ScaleVector2d(overscroll_delta, 263 1.f / size_.width(), 264 1.f / size_.height()); 265 float edge_overscroll_pull[EdgeEffect::EDGE_COUNT] = { 266 min(overscroll_pull.y(), 0.f), // Top 267 min(overscroll_pull.x(), 0.f), // Left 268 max(overscroll_pull.y(), 0.f), // Bottom 269 max(overscroll_pull.x(), 0.f) // Right 270 }; 271 272 for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) { 273 if (!edge_overscroll_pull[i]) 274 continue; 275 276 edge_effects_[i]->Pull(current_time, std::abs(edge_overscroll_pull[i])); 277 GetOppositeEdge(i)->Release(current_time); 278 } 279 } 280 281 void OverscrollGlow::Absorb(base::TimeTicks current_time, 282 gfx::Vector2dF velocity, 283 gfx::Vector2dF overscroll, 284 gfx::Vector2dF old_overscroll) { 285 DCHECK(enabled_ && initialized_); 286 if (overscroll.IsZero() || velocity.IsZero()) 287 return; 288 289 // Only trigger on initial overscroll at a non-zero velocity 290 const float overscroll_velocities[EdgeEffect::EDGE_COUNT] = { 291 old_overscroll.y() >= 0 && overscroll.y() < 0 ? min(velocity.y(), 0.f) : 0, 292 old_overscroll.x() >= 0 && overscroll.x() < 0 ? min(velocity.x(), 0.f) : 0, 293 old_overscroll.y() <= 0 && overscroll.y() > 0 ? max(velocity.y(), 0.f) : 0, 294 old_overscroll.x() <= 0 && overscroll.x() > 0 ? max(velocity.x(), 0.f) : 0 295 }; 296 297 for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) { 298 if (!overscroll_velocities[i]) 299 continue; 300 301 edge_effects_[i]->Absorb(current_time, std::abs(overscroll_velocities[i])); 302 GetOppositeEdge(i)->Release(current_time); 303 } 304 } 305 306 void OverscrollGlow::Release(base::TimeTicks current_time) { 307 DCHECK(initialized_); 308 for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) { 309 edge_effects_[i]->Release(current_time); 310 } 311 old_overscroll_ = old_velocity_ = gfx::Vector2dF(); 312 } 313 314 void OverscrollGlow::ReleaseAxis(Axis axis, base::TimeTicks current_time) { 315 DCHECK(initialized_); 316 switch (axis) { 317 case AXIS_X: 318 edge_effects_[EdgeEffect::EDGE_LEFT]->Release(current_time); 319 edge_effects_[EdgeEffect::EDGE_RIGHT]->Release(current_time); 320 old_overscroll_.set_x(0); 321 old_velocity_.set_x(0); 322 break; 323 case AXIS_Y: 324 edge_effects_[EdgeEffect::EDGE_TOP]->Release(current_time); 325 edge_effects_[EdgeEffect::EDGE_BOTTOM]->Release(current_time); 326 old_overscroll_.set_y(0); 327 old_velocity_.set_y(0); 328 break; 329 }; 330 } 331 332 EdgeEffect* OverscrollGlow::GetOppositeEdge(int edge_index) { 333 DCHECK(initialized_); 334 return edge_effects_[(edge_index + 2) % EdgeEffect::EDGE_COUNT].get(); 335 } 336 337 } // namespace content 338 339