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