• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CreateSkBitmapFromAndroidResource(const char * name,gfx::Size size)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:
OverscrollResources()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 
edge_bitmap() const54   const SkBitmap& edge_bitmap() const { return edge_bitmap_; }
glow_bitmap() const55   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 
CreateImageLayer(const SkBitmap & bitmap)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 
IsApproxZero(float value)74 bool IsApproxZero(float value) {
75   return std::abs(value) < kEpsilon;
76 }
77 
ZeroSmallComponents(gfx::Vector2dF vector)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.
EnsureResources()87 void EnsureResources() {
88   g_overscroll_resources.Get();
89 }
90 
91 } // namespace
92 
Create(bool enabled)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 
OverscrollGlow(bool enabled)103 OverscrollGlow::OverscrollGlow(bool enabled)
104     : enabled_(enabled), initialized_(false) {}
105 
~OverscrollGlow()106 OverscrollGlow::~OverscrollGlow() {
107   Detach();
108 }
109 
Enable()110 void OverscrollGlow::Enable() {
111   enabled_ = true;
112 }
113 
Disable()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 
OnOverscrolled(cc::Layer * overscrolling_layer,base::TimeTicks current_time,gfx::Vector2dF accumulated_overscroll,gfx::Vector2dF overscroll_delta,gfx::Vector2dF velocity)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 
Animate(base::TimeTicks current_time)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 
UpdateDisplayParameters(const DisplayParameters & params)199 void OverscrollGlow::UpdateDisplayParameters(const DisplayParameters& params) {
200   display_params_ = params;
201 }
202 
NeedsAnimate() const203 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 
UpdateLayerAttachment(cc::Layer * parent)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 
Detach()227 void OverscrollGlow::Detach() {
228   if (root_layer_)
229     root_layer_->RemoveFromParent();
230 }
231 
InitializeIfNecessary()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 
Pull(base::TimeTicks current_time,gfx::Vector2dF overscroll_delta)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 
Absorb(base::TimeTicks current_time,gfx::Vector2dF velocity,bool x_overscroll_started,bool y_overscroll_started)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 
Release(base::TimeTicks current_time)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 
ReleaseAxis(Axis axis,base::TimeTicks current_time)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 
GetOppositeEdge(int edge_index)330 EdgeEffect* OverscrollGlow::GetOppositeEdge(int edge_index) {
331   DCHECK(initialized_);
332   return edge_effects_[(edge_index + 2) % EdgeEffect::EDGE_COUNT].get();
333 }
334 
DisplayParameters()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