| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.nes |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/android/edge_effect.h" | 5 #include "content/browser/android/edge_effect.h" |
| 6 | 6 |
| 7 #include "cc/layers/layer.h" | 7 #include "base/debug/trace_event.h" |
| 8 #include "base/lazy_instance.h" |
| 9 #include "cc/layers/image_layer.h" |
| 10 #include "skia/ext/image_operations.h" |
| 11 #include "ui/gfx/android/java_bitmap.h" |
| 8 | 12 |
| 9 namespace content { | 13 namespace content { |
| 10 | 14 |
| 11 namespace { | 15 namespace { |
| 12 | 16 |
| 13 enum State { | |
| 14 STATE_IDLE = 0, | |
| 15 STATE_PULL, | |
| 16 STATE_ABSORB, | |
| 17 STATE_RECEDE, | |
| 18 STATE_PULL_DECAY | |
| 19 }; | |
| 20 | |
| 21 // Time it will take the effect to fully recede in ms | 17 // Time it will take the effect to fully recede in ms |
| 22 const int kRecedeTime = 1000; | 18 const int kRecedeTime = 1000; |
| 23 | 19 |
| 24 // Time it will take before a pulled glow begins receding in ms | 20 // Time it will take before a pulled glow begins receding in ms |
| 25 const int kPullTime = 167; | 21 const int kPullTime = 167; |
| 26 | 22 |
| 27 // Time it will take in ms for a pulled glow to decay before release | 23 // Time it will take in ms for a pulled glow to decay before release |
| 28 const int kPullDecayTime = 1000; | 24 const int kPullDecayTime = 1000; |
| 29 | 25 |
| 30 const float kMaxAlpha = 1.f; | 26 const float kMaxAlpha = 1.f; |
| 31 const float kHeldEdgeScaleY = .5f; | 27 const float kHeldEdgeScaleY = .5f; |
| 32 | 28 |
| 33 const float kMaxGlowHeight = 4.f; | 29 const float kMaxGlowHeight = 4.f; |
| 34 | 30 |
| 35 const float kPullGlowBegin = 1.f; | 31 const float kPullGlowBegin = 1.f; |
| 36 const float kPullEdgeBegin = 0.6f; | 32 const float kPullEdgeBegin = 0.6f; |
| 37 | 33 |
| 38 // Min/max velocity that will be absorbed | 34 // Min/max velocity that will be absorbed |
| 39 const float kMinVelocity = 100.f; | 35 const float kMinVelocity = 100.f; |
| 40 const float kMaxVelocity = 10000.f; | 36 const float kMaxVelocity = 10000.f; |
| 41 | 37 |
| 42 const float kEpsilon = 0.001f; | 38 const float kEpsilon = 0.001f; |
| 43 | 39 |
| 44 const float kGlowHeightToWidthRatio = 0.25f; | 40 const float kGlowHeightWidthRatio = 0.25f; |
| 45 | 41 |
| 46 // How much dragging should effect the height of the edge image. | 42 // How much dragging should effect the height of the edge image. |
| 47 // Number determined by user testing. | 43 // Number determined by user testing. |
| 48 const int kPullDistanceEdgeFactor = 7; | 44 const int kPullDistanceEdgeFactor = 7; |
| 49 | 45 |
| 50 // How much dragging should effect the height of the glow image. | 46 // How much dragging should effect the height of the glow image. |
| 51 // Number determined by user testing. | 47 // Number determined by user testing. |
| 52 const int kPullDistanceGlowFactor = 7; | 48 const int kPullDistanceGlowFactor = 7; |
| 53 const float kPullDistanceAlphaGlowFactor = 1.1f; | 49 const float kPullDistanceAlphaGlowFactor = 1.1f; |
| 54 | 50 |
| 55 const int kVelocityEdgeFactor = 8; | 51 const int kVelocityEdgeFactor = 8; |
| 56 const int kVelocityGlowFactor = 12; | 52 const int kVelocityGlowFactor = 12; |
| 57 | 53 |
| 54 const int kScaledEdgeHeight = 12; |
| 55 const int kScaledGlowHeight = 64; |
| 56 const float kEdgeHeightAtMdpi = 12.f; |
| 57 const float kGlowHeightAtMdpi = 128.f; |
| 58 |
| 58 template <typename T> | 59 template <typename T> |
| 59 T Lerp(T a, T b, T t) { | 60 T Lerp(T a, T b, T t) { |
| 60 return a + (b - a) * t; | 61 return a + (b - a) * t; |
| 61 } | 62 } |
| 62 | 63 |
| 63 template <typename T> | 64 template <typename T> |
| 64 T Clamp(T value, T low, T high) { | 65 T Clamp(T value, T low, T high) { |
| 65 return value < low ? low : (value > high ? high : value); | 66 return value < low ? low : (value > high ? high : value); |
| 66 } | 67 } |
| 67 | 68 |
| 68 template <typename T> | 69 template <typename T> |
| 69 T Damp(T input, T factor) { | 70 T Damp(T input, T factor) { |
| 70 T result; | 71 T result; |
| 71 if (factor == 1) { | 72 if (factor == 1) { |
| 72 result = 1 - (1 - input) * (1 - input); | 73 result = 1 - (1 - input) * (1 - input); |
| 73 } else { | 74 } else { |
| 74 result = 1 - std::pow(1 - input, 2 * factor); | 75 result = 1 - std::pow(1 - input, 2 * factor); |
| 75 } | 76 } |
| 76 return result; | 77 return result; |
| 77 } | 78 } |
| 78 | 79 |
| 79 gfx::Transform ComputeTransform(EdgeEffect::Edge edge, | |
| 80 const gfx::SizeF& window_size, | |
| 81 int offset, | |
| 82 int height) { | |
| 83 // Edge effects that require rotation are translated to the center about which | |
| 84 // the layer should be rotated to align with the corresponding edge. | |
| 85 switch (edge) { | |
| 86 case EdgeEffect::EDGE_TOP: | |
| 87 return gfx::Transform(1, 0, 0, 1, 0, offset); | |
| 88 case EdgeEffect::EDGE_LEFT: | |
| 89 return gfx::Transform(0, 1, -1, 0, | |
| 90 (-window_size.height() + height) / 2.f + offset, | |
| 91 (window_size.height() - height) / 2.f); | |
| 92 case EdgeEffect::EDGE_BOTTOM: | |
| 93 return gfx::Transform(-1, 0, 0, -1, | |
| 94 0, window_size.height() - height + offset); | |
| 95 case EdgeEffect::EDGE_RIGHT: | |
| 96 return gfx::Transform(0, -1, 1, 0, | |
| 97 (-window_size.height() - height) / 2.f + window_size.width() + offset, | |
| 98 (window_size.height() - height) / 2.f); | |
| 99 default: | |
| 100 NOTREACHED() << "Invalid edge: " << edge; | |
| 101 return gfx::Transform(); | |
| 102 }; | |
| 103 } | |
| 104 | |
| 105 gfx::Size ComputeBounds(EdgeEffect::Edge edge, | |
| 106 const gfx::SizeF& window_size, | |
| 107 int height) { | |
| 108 switch (edge) { | |
| 109 case EdgeEffect::EDGE_TOP: | |
| 110 case EdgeEffect::EDGE_BOTTOM: | |
| 111 return gfx::Size(window_size.width(), height); | |
| 112 case EdgeEffect::EDGE_LEFT: | |
| 113 case EdgeEffect::EDGE_RIGHT: | |
| 114 return gfx::Size(window_size.height(), height); | |
| 115 default: | |
| 116 NOTREACHED() << "Invalid edge: " << edge; | |
| 117 return gfx::Size(); | |
| 118 }; | |
| 119 } | |
| 120 | |
| 121 void DisableLayer(cc::Layer* layer) { | 80 void DisableLayer(cc::Layer* layer) { |
| 122 DCHECK(layer); | 81 DCHECK(layer); |
| 123 layer->SetIsDrawable(false); | 82 layer->SetIsDrawable(false); |
| 124 layer->SetTransform(gfx::Transform()); | 83 layer->SetTransform(gfx::Transform()); |
| 125 layer->SetOpacity(1.f); | 84 layer->SetOpacity(1.f); |
| 126 } | 85 } |
| 127 | 86 |
| 128 void UpdateLayer(cc::Layer* layer, | 87 void UpdateLayer(cc::Layer* layer, |
| 129 EdgeEffect::Edge edge, | 88 const gfx::Size& size, |
| 130 const gfx::SizeF& window_size, | 89 const gfx::Transform& transform, |
| 131 int offset, | |
| 132 int height, | |
| 133 float opacity) { | 90 float opacity) { |
| 134 DCHECK(layer); | 91 DCHECK(layer); |
| 135 layer->SetIsDrawable(true); | 92 layer->SetIsDrawable(true); |
| 136 gfx::Size bounds = ComputeBounds(edge, window_size, height); | 93 layer->SetTransformOrigin(gfx::Point3F(size.width() * 0.5f, 0, 0)); |
| 137 layer->SetTransformOrigin( | 94 layer->SetTransform(transform); |
| 138 gfx::Point3F(bounds.width() * 0.5f, bounds.height() * 0.5f, 0)); | 95 layer->SetBounds(size); |
| 139 layer->SetTransform(ComputeTransform(edge, window_size, offset, height)); | |
| 140 layer->SetBounds(bounds); | |
| 141 layer->SetOpacity(Clamp(opacity, 0.f, 1.f)); | 96 layer->SetOpacity(Clamp(opacity, 0.f, 1.f)); |
| 142 } | 97 } |
| 143 | 98 |
| 99 scoped_refptr<cc::Layer> CreateImageLayer(const SkBitmap& bitmap) { |
| 100 scoped_refptr<cc::ImageLayer> layer = cc::ImageLayer::Create(); |
| 101 layer->SetBitmap(bitmap); |
| 102 return layer; |
| 103 } |
| 104 |
| 105 SkBitmap CreateSkBitmapFromAndroidResource(const char* name, gfx::Size size) { |
| 106 base::android::ScopedJavaLocalRef<jobject> jobj = |
| 107 gfx::CreateJavaBitmapFromAndroidResource(name, size); |
| 108 if (jobj.is_null()) |
| 109 return SkBitmap(); |
| 110 |
| 111 SkBitmap bitmap = CreateSkBitmapFromJavaBitmap(gfx::JavaBitmap(jobj.obj())); |
| 112 if (bitmap.isNull()) |
| 113 return bitmap; |
| 114 |
| 115 return skia::ImageOperations::Resize( |
| 116 bitmap, skia::ImageOperations::RESIZE_BOX, size.width(), size.height()); |
| 117 } |
| 118 |
| 119 class EdgeEffectResources { |
| 120 public: |
| 121 EdgeEffectResources() { |
| 122 TRACE_EVENT0("browser", "EdgeEffectResources::Create"); |
| 123 edge_bitmap_ = |
| 124 CreateSkBitmapFromAndroidResource("android:drawable/overscroll_edge", |
| 125 gfx::Size(128, kScaledEdgeHeight)); |
| 126 glow_bitmap_ = |
| 127 CreateSkBitmapFromAndroidResource("android:drawable/overscroll_glow", |
| 128 gfx::Size(128, kScaledGlowHeight)); |
| 129 } |
| 130 |
| 131 const SkBitmap& edge_bitmap() const { return edge_bitmap_; } |
| 132 const SkBitmap& glow_bitmap() const { return glow_bitmap_; } |
| 133 |
| 134 private: |
| 135 SkBitmap edge_bitmap_; |
| 136 SkBitmap glow_bitmap_; |
| 137 |
| 138 DISALLOW_COPY_AND_ASSIGN(EdgeEffectResources); |
| 139 }; |
| 140 |
| 141 // Leaky to allow access from a worker thread. |
| 142 base::LazyInstance<EdgeEffectResources>::Leaky g_edge_effect_resources = |
| 143 LAZY_INSTANCE_INITIALIZER; |
| 144 |
| 144 } // namespace | 145 } // namespace |
| 145 | 146 |
| 146 EdgeEffect::EdgeEffect(scoped_refptr<cc::Layer> edge, | 147 // static |
| 147 scoped_refptr<cc::Layer> glow) | 148 scoped_ptr<EdgeEffect> EdgeEffect::Create(cc::Layer* root_layer, |
| 149 float device_scale_factor) { |
| 150 DCHECK(root_layer); |
| 151 |
| 152 const SkBitmap& edge = g_edge_effect_resources.Get().edge_bitmap(); |
| 153 const SkBitmap& glow = g_edge_effect_resources.Get().glow_bitmap(); |
| 154 if (edge.isNull() || glow.isNull()) |
| 155 return scoped_ptr<EdgeEffect>(); |
| 156 |
| 157 scoped_refptr<cc::Layer> edge_layer = CreateImageLayer(edge); |
| 158 scoped_refptr<cc::Layer> glow_layer = CreateImageLayer(glow); |
| 159 root_layer->AddChild(edge_layer); |
| 160 root_layer->AddChild(glow_layer); |
| 161 return make_scoped_ptr( |
| 162 new EdgeEffect(edge_layer, glow_layer, device_scale_factor)); |
| 163 } |
| 164 |
| 165 EdgeEffect::EdgeEffect(const scoped_refptr<cc::Layer>& edge, |
| 166 const scoped_refptr<cc::Layer>& glow, |
| 167 float device_scale_factor) |
| 148 : edge_(edge) | 168 : edge_(edge) |
| 149 , glow_(glow) | 169 , glow_(glow) |
| 170 , base_edge_height_(kEdgeHeightAtMdpi * device_scale_factor) |
| 171 , base_glow_height_(kGlowHeightAtMdpi * device_scale_factor) |
| 150 , edge_alpha_(0) | 172 , edge_alpha_(0) |
| 151 , edge_scale_y_(0) | 173 , edge_scale_y_(0) |
| 152 , glow_alpha_(0) | 174 , glow_alpha_(0) |
| 153 , glow_scale_y_(0) | 175 , glow_scale_y_(0) |
| 154 , edge_alpha_start_(0) | 176 , edge_alpha_start_(0) |
| 155 , edge_alpha_finish_(0) | 177 , edge_alpha_finish_(0) |
| 156 , edge_scale_y_start_(0) | 178 , edge_scale_y_start_(0) |
| 157 , edge_scale_y_finish_(0) | 179 , edge_scale_y_finish_(0) |
| 158 , glow_alpha_start_(0) | 180 , glow_alpha_start_(0) |
| 159 , glow_alpha_finish_(0) | 181 , glow_alpha_finish_(0) |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 234 | 256 |
| 235 edge_alpha_finish_ = 0.f; | 257 edge_alpha_finish_ = 0.f; |
| 236 edge_scale_y_finish_ = 0.f; | 258 edge_scale_y_finish_ = 0.f; |
| 237 glow_alpha_finish_ = 0.f; | 259 glow_alpha_finish_ = 0.f; |
| 238 glow_scale_y_finish_ = 0.f; | 260 glow_scale_y_finish_ = 0.f; |
| 239 | 261 |
| 240 start_time_ = current_time; | 262 start_time_ = current_time; |
| 241 duration_ = base::TimeDelta::FromMilliseconds(kRecedeTime); | 263 duration_ = base::TimeDelta::FromMilliseconds(kRecedeTime); |
| 242 } | 264 } |
| 243 | 265 |
| 266 void EdgeEffect::Pull(base::TimeTicks current_time, |
| 267 float delta_distance, |
| 268 float displacement) { |
| 269 Pull(current_time, delta_distance); |
| 270 } |
| 271 |
| 244 void EdgeEffect::Absorb(base::TimeTicks current_time, float velocity) { | 272 void EdgeEffect::Absorb(base::TimeTicks current_time, float velocity) { |
| 245 state_ = STATE_ABSORB; | 273 state_ = STATE_ABSORB; |
| 246 velocity = Clamp(std::abs(velocity), kMinVelocity, kMaxVelocity); | 274 velocity = Clamp(std::abs(velocity), kMinVelocity, kMaxVelocity); |
| 247 | 275 |
| 248 start_time_ = current_time; | 276 start_time_ = current_time; |
| 249 // This should never be less than 1 millisecond. | 277 // This should never be less than 1 millisecond. |
| 250 duration_ = base::TimeDelta::FromMilliseconds(0.15f + (velocity * 0.02f)); | 278 duration_ = base::TimeDelta::FromMilliseconds(0.15f + (velocity * 0.02f)); |
| 251 | 279 |
| 252 // The edge should always be at least partially visible, regardless | 280 // The edge should always be at least partially visible, regardless |
| 253 // of velocity. | 281 // of velocity. |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 341 break; | 369 break; |
| 342 } | 370 } |
| 343 } | 371 } |
| 344 | 372 |
| 345 if (state_ == STATE_RECEDE && glow_scale_y_ <= 0 && edge_scale_y_ <= 0) | 373 if (state_ == STATE_RECEDE && glow_scale_y_ <= 0 && edge_scale_y_ <= 0) |
| 346 Finish(); | 374 Finish(); |
| 347 | 375 |
| 348 return !IsFinished(); | 376 return !IsFinished(); |
| 349 } | 377 } |
| 350 | 378 |
| 351 void EdgeEffect::ApplyToLayers(gfx::SizeF window_size, | 379 void EdgeEffect::ApplyToLayers(const gfx::SizeF& size, |
| 352 Edge edge, | 380 const gfx::Transform& transform) { |
| 353 float edge_height, | |
| 354 float glow_height, | |
| 355 float offset) { | |
| 356 if (IsFinished()) | 381 if (IsFinished()) |
| 357 return; | 382 return; |
| 358 | 383 |
| 359 // An empty window size, while meaningless, is also relatively harmless, and | 384 // An empty window size, while meaningless, is also relatively harmless, and |
| 360 // will simply prevent any drawing of the layers. | 385 // will simply prevent any drawing of the layers. |
| 361 if (window_size.IsEmpty()) { | 386 if (size.IsEmpty()) { |
| 362 DisableLayer(edge_.get()); | 387 DisableLayer(edge_.get()); |
| 363 DisableLayer(glow_.get()); | 388 DisableLayer(glow_.get()); |
| 364 return; | 389 return; |
| 365 } | 390 } |
| 366 | 391 |
| 367 // Glow | 392 // Glow |
| 368 const int scaled_glow_height = static_cast<int>( | 393 const int scaled_glow_height = static_cast<int>( |
| 369 std::min(glow_height * glow_scale_y_ * kGlowHeightToWidthRatio * 0.6f, | 394 std::min(base_glow_height_ * glow_scale_y_ * kGlowHeightWidthRatio * 0.6f, |
| 370 glow_height * kMaxGlowHeight) + 0.5f); | 395 base_glow_height_ * kMaxGlowHeight) + 0.5f); |
| 371 UpdateLayer( | 396 const gfx::Size glow_size(size.width(), scaled_glow_height); |
| 372 glow_.get(), edge, window_size, offset, scaled_glow_height, glow_alpha_); | 397 UpdateLayer(glow_.get(), glow_size, transform, glow_alpha_); |
| 373 | 398 |
| 374 // Edge | 399 // Edge |
| 375 const int scaled_edge_height = static_cast<int>(edge_height * edge_scale_y_); | 400 const int scaled_edge_height = |
| 376 UpdateLayer( | 401 static_cast<int>(base_edge_height_ * edge_scale_y_); |
| 377 edge_.get(), edge, window_size, offset, scaled_edge_height, edge_alpha_); | 402 const gfx::Size edge_size(size.width(), scaled_edge_height); |
| 403 UpdateLayer(edge_.get(), edge_size, transform, edge_alpha_); |
| 404 } |
| 405 |
| 406 // static |
| 407 void EdgeEffect::EnsureResources() { |
| 408 g_edge_effect_resources.Get(); |
| 378 } | 409 } |
| 379 | 410 |
| 380 } // namespace content | 411 } // namespace content |
| OLD | NEW |