Home | History | Annotate | Download | only in android
      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