| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 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/overscroll_glow.h" | 5 #include "content/browser/android/overscroll_glow.h" |
| 6 | 6 |
| 7 #include "base/debug/trace_event.h" | |
| 8 #include "base/lazy_instance.h" | |
| 9 #include "cc/layers/layer.h" | 7 #include "cc/layers/layer.h" |
| 10 #include "content/browser/android/edge_effect.h" | 8 #include "content/browser/android/edge_effect_base.h" |
| 11 | 9 |
| 12 using std::max; | 10 using std::max; |
| 13 using std::min; | 11 using std::min; |
| 14 | 12 |
| 15 namespace content { | 13 namespace content { |
| 16 | 14 |
| 17 namespace { | 15 namespace { |
| 18 | 16 |
| 19 const float kEpsilon = 1e-3f; | 17 const float kEpsilon = 1e-3f; |
| 20 const float kEdgeHeightAtMdpi = 12.f; | |
| 21 const float kGlowHeightAtMdpi = 128.f; | |
| 22 | 18 |
| 23 bool IsApproxZero(float value) { | 19 bool IsApproxZero(float value) { |
| 24 return std::abs(value) < kEpsilon; | 20 return std::abs(value) < kEpsilon; |
| 25 } | 21 } |
| 26 | 22 |
| 27 gfx::Vector2dF ZeroSmallComponents(gfx::Vector2dF vector) { | 23 gfx::Vector2dF ZeroSmallComponents(gfx::Vector2dF vector) { |
| 28 if (IsApproxZero(vector.x())) | 24 if (IsApproxZero(vector.x())) |
| 29 vector.set_x(0); | 25 vector.set_x(0); |
| 30 if (IsApproxZero(vector.y())) | 26 if (IsApproxZero(vector.y())) |
| 31 vector.set_y(0); | 27 vector.set_y(0); |
| 32 return vector; | 28 return vector; |
| 33 } | 29 } |
| 34 | 30 |
| 31 gfx::Transform ComputeTransform(OverscrollGlow::Edge edge, |
| 32 const gfx::SizeF& window_size, |
| 33 float offset) { |
| 34 // Transforms assume the edge layers are anchored to their *top center point*. |
| 35 switch (edge) { |
| 36 case OverscrollGlow::EDGE_TOP: |
| 37 return gfx::Transform(1, 0, 0, 1, 0, offset); |
| 38 case OverscrollGlow::EDGE_LEFT: |
| 39 return gfx::Transform(0, |
| 40 1, |
| 41 -1, |
| 42 0, |
| 43 -window_size.height() / 2.f + offset, |
| 44 window_size.height() / 2.f); |
| 45 case OverscrollGlow::EDGE_BOTTOM: |
| 46 return gfx::Transform(-1, 0, 0, -1, 0, window_size.height() + offset); |
| 47 case OverscrollGlow::EDGE_RIGHT: |
| 48 return gfx::Transform( |
| 49 0, |
| 50 -1, |
| 51 1, |
| 52 0, |
| 53 -window_size.height() / 2.f + window_size.width() + offset, |
| 54 window_size.height() / 2.f); |
| 55 default: |
| 56 NOTREACHED() << "Invalid edge: " << edge; |
| 57 return gfx::Transform(); |
| 58 }; |
| 59 } |
| 60 |
| 61 gfx::SizeF ComputeSize(OverscrollGlow::Edge edge, |
| 62 const gfx::SizeF& window_size) { |
| 63 switch (edge) { |
| 64 case OverscrollGlow::EDGE_TOP: |
| 65 case OverscrollGlow::EDGE_BOTTOM: |
| 66 return window_size; |
| 67 case OverscrollGlow::EDGE_LEFT: |
| 68 case OverscrollGlow::EDGE_RIGHT: |
| 69 return gfx::SizeF(window_size.height(), window_size.width()); |
| 70 default: |
| 71 NOTREACHED() << "Invalid edge: " << edge; |
| 72 return gfx::SizeF(); |
| 73 }; |
| 74 } |
| 75 |
| 35 } // namespace | 76 } // namespace |
| 36 | 77 |
| 37 scoped_ptr<OverscrollGlow> OverscrollGlow::Create( | 78 OverscrollGlow::OverscrollGlow(const EdgeEffectProvider& edge_effect_provider) |
| 38 ui::SystemUIResourceManager* resource_manager) { | 79 : edge_effect_provider_(edge_effect_provider), |
| 39 return make_scoped_ptr(new OverscrollGlow(resource_manager)); | 80 enabled_(true), |
| 40 } | 81 initialized_(false) { |
| 41 | 82 DCHECK(!edge_effect_provider_.is_null()); |
| 42 OverscrollGlow::OverscrollGlow(ui::SystemUIResourceManager* resource_manager) | |
| 43 : enabled_(true), initialized_(false), resource_manager_(resource_manager) { | |
| 44 DCHECK(resource_manager_); | |
| 45 EdgeEffect::PreloadResources(resource_manager_); | |
| 46 } | 83 } |
| 47 | 84 |
| 48 OverscrollGlow::~OverscrollGlow() { | 85 OverscrollGlow::~OverscrollGlow() { |
| 49 Detach(); | 86 Detach(); |
| 50 } | 87 } |
| 51 | 88 |
| 52 void OverscrollGlow::Enable() { | 89 void OverscrollGlow::Enable() { |
| 53 enabled_ = true; | 90 enabled_ = true; |
| 54 } | 91 } |
| 55 | 92 |
| 56 void OverscrollGlow::Disable() { | 93 void OverscrollGlow::Disable() { |
| 57 if (!enabled_) | 94 if (!enabled_) |
| 58 return; | 95 return; |
| 59 enabled_ = false; | 96 enabled_ = false; |
| 60 if (!enabled_ && initialized_) { | 97 if (!enabled_ && initialized_) { |
| 61 Detach(); | 98 Detach(); |
| 62 for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) | 99 for (size_t i = 0; i < EDGE_COUNT; ++i) |
| 63 edge_effects_[i]->Finish(); | 100 edge_effects_[i]->Finish(); |
| 64 } | 101 } |
| 65 } | 102 } |
| 66 | 103 |
| 67 bool OverscrollGlow::OnOverscrolled(cc::Layer* overscrolling_layer, | 104 bool OverscrollGlow::OnOverscrolled(cc::Layer* overscrolling_layer, |
| 68 base::TimeTicks current_time, | 105 base::TimeTicks current_time, |
| 69 gfx::Vector2dF accumulated_overscroll, | 106 gfx::Vector2dF accumulated_overscroll, |
| 70 gfx::Vector2dF overscroll_delta, | 107 gfx::Vector2dF overscroll_delta, |
| 71 gfx::Vector2dF velocity) { | 108 gfx::Vector2dF velocity, |
| 109 gfx::Vector2dF displacement) { |
| 72 DCHECK(overscrolling_layer); | 110 DCHECK(overscrolling_layer); |
| 73 | 111 |
| 74 if (!enabled_) | 112 if (!enabled_) |
| 75 return false; | 113 return false; |
| 76 | 114 |
| 77 // The size of the glow determines the relative effect of the inputs; an | 115 // The size of the glow determines the relative effect of the inputs; an |
| 78 // empty-sized effect is effectively disabled. | 116 // empty-sized effect is effectively disabled. |
| 79 if (display_params_.size.IsEmpty()) | 117 if (display_params_.size.IsEmpty()) |
| 80 return false; | 118 return false; |
| 81 | 119 |
| 82 // Ignore sufficiently small values that won't meaningfuly affect animation. | 120 // Ignore sufficiently small values that won't meaningfuly affect animation. |
| 83 overscroll_delta = ZeroSmallComponents(overscroll_delta); | 121 overscroll_delta = ZeroSmallComponents(overscroll_delta); |
| 84 if (overscroll_delta.IsZero()) { | 122 if (overscroll_delta.IsZero()) { |
| 85 if (initialized_) { | 123 if (initialized_) { |
| 86 Release(current_time); | 124 Release(current_time); |
| 87 UpdateLayerAttachment(overscrolling_layer); | 125 UpdateLayerAttachment(overscrolling_layer); |
| 88 } | 126 } |
| 89 return NeedsAnimate(); | 127 return NeedsAnimate(); |
| 90 } | 128 } |
| 91 | 129 |
| 92 if (!InitializeIfNecessary()) | 130 if (!InitializeIfNecessary()) |
| 93 return false; | 131 return false; |
| 94 | 132 |
| 95 gfx::Vector2dF old_overscroll = accumulated_overscroll - overscroll_delta; | 133 gfx::Vector2dF old_overscroll = accumulated_overscroll - overscroll_delta; |
| 96 bool x_overscroll_started = | 134 bool x_overscroll_started = |
| 97 !IsApproxZero(overscroll_delta.x()) && IsApproxZero(old_overscroll.x()); | 135 !IsApproxZero(overscroll_delta.x()) && IsApproxZero(old_overscroll.x()); |
| 98 bool y_overscroll_started = | 136 bool y_overscroll_started = |
| 99 !IsApproxZero(overscroll_delta.y()) && IsApproxZero(old_overscroll.y()); | 137 !IsApproxZero(overscroll_delta.y()) && IsApproxZero(old_overscroll.y()); |
| 100 | 138 |
| 101 if (x_overscroll_started) | |
| 102 ReleaseAxis(AXIS_X, current_time); | |
| 103 if (y_overscroll_started) | |
| 104 ReleaseAxis(AXIS_Y, current_time); | |
| 105 | |
| 106 velocity = ZeroSmallComponents(velocity); | 139 velocity = ZeroSmallComponents(velocity); |
| 107 if (!velocity.IsZero()) | 140 if (!velocity.IsZero()) |
| 108 Absorb(current_time, velocity, x_overscroll_started, y_overscroll_started); | 141 Absorb(current_time, velocity, x_overscroll_started, y_overscroll_started); |
| 109 else | 142 else |
| 110 Pull(current_time, overscroll_delta); | 143 Pull(current_time, overscroll_delta, displacement); |
| 111 | 144 |
| 112 UpdateLayerAttachment(overscrolling_layer); | 145 UpdateLayerAttachment(overscrolling_layer); |
| 113 return NeedsAnimate(); | 146 return NeedsAnimate(); |
| 114 } | 147 } |
| 115 | 148 |
| 116 bool OverscrollGlow::Animate(base::TimeTicks current_time) { | 149 bool OverscrollGlow::Animate(base::TimeTicks current_time) { |
| 117 if (!NeedsAnimate()) { | 150 if (!NeedsAnimate()) { |
| 118 Detach(); | 151 Detach(); |
| 119 return false; | 152 return false; |
| 120 } | 153 } |
| 121 | 154 |
| 122 for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) { | 155 for (size_t i = 0; i < EDGE_COUNT; ++i) { |
| 123 if (edge_effects_[i]->Update(current_time)) { | 156 if (edge_effects_[i]->Update(current_time)) { |
| 157 Edge edge = static_cast<Edge>(i); |
| 124 edge_effects_[i]->ApplyToLayers( | 158 edge_effects_[i]->ApplyToLayers( |
| 125 display_params_.size, | 159 ComputeSize(edge, display_params_.size), |
| 126 static_cast<EdgeEffect::Edge>(i), | 160 ComputeTransform( |
| 127 kEdgeHeightAtMdpi * display_params_.device_scale_factor, | 161 edge, display_params_.size, display_params_.edge_offsets[i])); |
| 128 kGlowHeightAtMdpi * display_params_.device_scale_factor, | |
| 129 display_params_.edge_offsets[i]); | |
| 130 } | 162 } |
| 131 } | 163 } |
| 132 | 164 |
| 133 if (!NeedsAnimate()) { | 165 if (!NeedsAnimate()) { |
| 134 Detach(); | 166 Detach(); |
| 135 return false; | 167 return false; |
| 136 } | 168 } |
| 137 | 169 |
| 138 return true; | 170 return true; |
| 139 } | 171 } |
| 140 | 172 |
| 141 void OverscrollGlow::UpdateDisplayParameters(const DisplayParameters& params) { | 173 void OverscrollGlow::UpdateDisplayParameters(const DisplayParameters& params) { |
| 142 display_params_ = params; | 174 display_params_ = params; |
| 143 } | 175 } |
| 144 | 176 |
| 145 bool OverscrollGlow::NeedsAnimate() const { | 177 bool OverscrollGlow::NeedsAnimate() const { |
| 146 if (!enabled_ || !initialized_) | 178 if (!enabled_ || !initialized_) |
| 147 return false; | 179 return false; |
| 148 for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) { | 180 for (size_t i = 0; i < EDGE_COUNT; ++i) { |
| 149 if (!edge_effects_[i]->IsFinished()) | 181 if (!edge_effects_[i]->IsFinished()) |
| 150 return true; | 182 return true; |
| 151 } | 183 } |
| 152 return false; | 184 return false; |
| 153 } | 185 } |
| 154 | 186 |
| 155 void OverscrollGlow::UpdateLayerAttachment(cc::Layer* parent) { | 187 void OverscrollGlow::UpdateLayerAttachment(cc::Layer* parent) { |
| 156 DCHECK(parent); | 188 DCHECK(parent); |
| 157 if (!root_layer_) | 189 if (!root_layer_) |
| 158 return; | 190 return; |
| 159 | 191 |
| 160 if (!NeedsAnimate()) { | 192 if (!NeedsAnimate()) { |
| 161 Detach(); | 193 Detach(); |
| 162 return; | 194 return; |
| 163 } | 195 } |
| 164 | 196 |
| 165 if (root_layer_->parent() != parent) | 197 if (root_layer_->parent() != parent) |
| 166 parent->AddChild(root_layer_); | 198 parent->AddChild(root_layer_); |
| 167 | 199 |
| 168 for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) | 200 for (size_t i = 0; i < EDGE_COUNT; ++i) |
| 169 edge_effects_[i]->SetParent(root_layer_); | 201 edge_effects_[i]->SetParent(root_layer_); |
| 170 } | 202 } |
| 171 | 203 |
| 172 void OverscrollGlow::Detach() { | 204 void OverscrollGlow::Detach() { |
| 173 if (root_layer_) | 205 if (root_layer_) |
| 174 root_layer_->RemoveFromParent(); | 206 root_layer_->RemoveFromParent(); |
| 175 } | 207 } |
| 176 | 208 |
| 177 bool OverscrollGlow::InitializeIfNecessary() { | 209 bool OverscrollGlow::InitializeIfNecessary() { |
| 178 DCHECK(enabled_); | 210 DCHECK(enabled_); |
| 179 if (initialized_) | 211 if (initialized_) |
| 180 return true; | 212 return true; |
| 181 | 213 |
| 182 DCHECK(!root_layer_); | 214 DCHECK(!root_layer_); |
| 183 root_layer_ = cc::Layer::Create(); | 215 root_layer_ = cc::Layer::Create(); |
| 184 for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) | 216 for (size_t i = 0; i < EDGE_COUNT; ++i) { |
| 185 edge_effects_[i] = make_scoped_ptr(new EdgeEffect(resource_manager_)); | 217 edge_effects_[i] = edge_effect_provider_.Run(); |
| 218 DCHECK(edge_effects_[i]); |
| 219 } |
| 186 | 220 |
| 187 initialized_ = true; | 221 initialized_ = true; |
| 188 return true; | 222 return true; |
| 189 } | 223 } |
| 190 | 224 |
| 191 void OverscrollGlow::Pull(base::TimeTicks current_time, | 225 void OverscrollGlow::Pull(base::TimeTicks current_time, |
| 192 gfx::Vector2dF overscroll_delta) { | 226 const gfx::Vector2dF& overscroll_delta, |
| 227 const gfx::Vector2dF& overscroll_location) { |
| 193 DCHECK(enabled_ && initialized_); | 228 DCHECK(enabled_ && initialized_); |
| 194 overscroll_delta = ZeroSmallComponents(overscroll_delta); | 229 DCHECK(!overscroll_delta.IsZero()); |
| 195 if (overscroll_delta.IsZero()) | 230 const float inv_width = 1.f / display_params_.size.width(); |
| 196 return; | 231 const float inv_height = 1.f / display_params_.size.height(); |
| 197 | 232 |
| 198 gfx::Vector2dF overscroll_pull = | 233 gfx::Vector2dF overscroll_pull = |
| 199 gfx::ScaleVector2d(overscroll_delta, | 234 gfx::ScaleVector2d(overscroll_delta, inv_width, inv_height); |
| 200 1.f / display_params_.size.width(), | 235 const float edge_pull[EDGE_COUNT] = { |
| 201 1.f / display_params_.size.height()); | |
| 202 float edge_overscroll_pull[EdgeEffect::EDGE_COUNT] = { | |
| 203 min(overscroll_pull.y(), 0.f), // Top | 236 min(overscroll_pull.y(), 0.f), // Top |
| 204 min(overscroll_pull.x(), 0.f), // Left | 237 min(overscroll_pull.x(), 0.f), // Left |
| 205 max(overscroll_pull.y(), 0.f), // Bottom | 238 max(overscroll_pull.y(), 0.f), // Bottom |
| 206 max(overscroll_pull.x(), 0.f) // Right | 239 max(overscroll_pull.x(), 0.f) // Right |
| 207 }; | 240 }; |
| 208 | 241 |
| 209 for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) { | 242 gfx::Vector2dF displacement = |
| 210 if (!edge_overscroll_pull[i]) | 243 gfx::ScaleVector2d(overscroll_location, inv_width, inv_height); |
| 244 displacement.set_x(max(0.f, min(1.f, displacement.x()))); |
| 245 displacement.set_y(max(0.f, min(1.f, displacement.y()))); |
| 246 const float edge_displacement[EDGE_COUNT] = { |
| 247 1.f - displacement.x(), // Top |
| 248 displacement.y(), // Left |
| 249 displacement.x(), // Bottom |
| 250 1.f - displacement.y() // Right |
| 251 }; |
| 252 |
| 253 for (size_t i = 0; i < EDGE_COUNT; ++i) { |
| 254 if (!edge_pull[i]) |
| 211 continue; | 255 continue; |
| 212 | 256 |
| 213 edge_effects_[i]->Pull(current_time, std::abs(edge_overscroll_pull[i])); | 257 edge_effects_[i]->Pull( |
| 258 current_time, std::abs(edge_pull[i]), edge_displacement[i]); |
| 214 GetOppositeEdge(i)->Release(current_time); | 259 GetOppositeEdge(i)->Release(current_time); |
| 215 } | 260 } |
| 216 } | 261 } |
| 217 | 262 |
| 218 void OverscrollGlow::Absorb(base::TimeTicks current_time, | 263 void OverscrollGlow::Absorb(base::TimeTicks current_time, |
| 219 gfx::Vector2dF velocity, | 264 const gfx::Vector2dF& velocity, |
| 220 bool x_overscroll_started, | 265 bool x_overscroll_started, |
| 221 bool y_overscroll_started) { | 266 bool y_overscroll_started) { |
| 222 DCHECK(enabled_ && initialized_); | 267 DCHECK(enabled_ && initialized_); |
| 223 if (velocity.IsZero()) | 268 DCHECK(!velocity.IsZero()); |
| 224 return; | |
| 225 | 269 |
| 226 // Only trigger on initial overscroll at a non-zero velocity | 270 // Only trigger on initial overscroll at a non-zero velocity |
| 227 const float overscroll_velocities[EdgeEffect::EDGE_COUNT] = { | 271 const float overscroll_velocities[EDGE_COUNT] = { |
| 228 y_overscroll_started ? min(velocity.y(), 0.f) : 0, // Top | 272 y_overscroll_started ? min(velocity.y(), 0.f) : 0, // Top |
| 229 x_overscroll_started ? min(velocity.x(), 0.f) : 0, // Left | 273 x_overscroll_started ? min(velocity.x(), 0.f) : 0, // Left |
| 230 y_overscroll_started ? max(velocity.y(), 0.f) : 0, // Bottom | 274 y_overscroll_started ? max(velocity.y(), 0.f) : 0, // Bottom |
| 231 x_overscroll_started ? max(velocity.x(), 0.f) : 0 // Right | 275 x_overscroll_started ? max(velocity.x(), 0.f) : 0 // Right |
| 232 }; | 276 }; |
| 233 | 277 |
| 234 for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) { | 278 for (size_t i = 0; i < EDGE_COUNT; ++i) { |
| 235 if (!overscroll_velocities[i]) | 279 if (!overscroll_velocities[i]) |
| 236 continue; | 280 continue; |
| 237 | 281 |
| 238 edge_effects_[i]->Absorb(current_time, std::abs(overscroll_velocities[i])); | 282 edge_effects_[i]->Absorb(current_time, std::abs(overscroll_velocities[i])); |
| 239 GetOppositeEdge(i)->Release(current_time); | 283 GetOppositeEdge(i)->Release(current_time); |
| 240 } | 284 } |
| 241 } | 285 } |
| 242 | 286 |
| 243 void OverscrollGlow::Release(base::TimeTicks current_time) { | 287 void OverscrollGlow::Release(base::TimeTicks current_time) { |
| 244 DCHECK(initialized_); | 288 DCHECK(initialized_); |
| 245 for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) | 289 for (size_t i = 0; i < EDGE_COUNT; ++i) |
| 246 edge_effects_[i]->Release(current_time); | 290 edge_effects_[i]->Release(current_time); |
| 247 } | 291 } |
| 248 | 292 |
| 249 void OverscrollGlow::ReleaseAxis(Axis axis, base::TimeTicks current_time) { | 293 EdgeEffectBase* OverscrollGlow::GetOppositeEdge(int edge_index) { |
| 250 DCHECK(initialized_); | 294 DCHECK(initialized_); |
| 251 switch (axis) { | 295 return edge_effects_[(edge_index + 2) % EDGE_COUNT].get(); |
| 252 case AXIS_X: | |
| 253 edge_effects_[EdgeEffect::EDGE_LEFT]->Release(current_time); | |
| 254 edge_effects_[EdgeEffect::EDGE_RIGHT]->Release(current_time); | |
| 255 break; | |
| 256 case AXIS_Y: | |
| 257 edge_effects_[EdgeEffect::EDGE_TOP]->Release(current_time); | |
| 258 edge_effects_[EdgeEffect::EDGE_BOTTOM]->Release(current_time); | |
| 259 break; | |
| 260 }; | |
| 261 } | 296 } |
| 262 | 297 |
| 263 EdgeEffect* OverscrollGlow::GetOppositeEdge(int edge_index) { | 298 OverscrollGlow::DisplayParameters::DisplayParameters() { |
| 264 DCHECK(initialized_); | |
| 265 return edge_effects_[(edge_index + 2) % EdgeEffect::EDGE_COUNT].get(); | |
| 266 } | |
| 267 | |
| 268 OverscrollGlow::DisplayParameters::DisplayParameters() | |
| 269 : device_scale_factor(1) { | |
| 270 edge_offsets[0] = edge_offsets[1] = edge_offsets[2] = edge_offsets[3] = 0.f; | 299 edge_offsets[0] = edge_offsets[1] = edge_offsets[2] = edge_offsets[3] = 0.f; |
| 271 } | 300 } |
| 272 | 301 |
| 273 } // namespace content | 302 } // namespace content |
| OLD | NEW |