Respond with QuotaExceededError when IndexedDB has no disk space on open.
[chromium-blink-merge.git] / content / browser / android / edge_effect.cc
blobd8411bc1f8952dc086cd4ae0d5be466d353cb185
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.
5 #include "content/browser/android/edge_effect.h"
7 #include "cc/layers/layer.h"
8 #include "ui/gfx/screen.h"
10 namespace content {
12 namespace {
14 enum State {
15 STATE_IDLE = 0,
16 STATE_PULL,
17 STATE_ABSORB,
18 STATE_RECEDE,
19 STATE_PULL_DECAY
22 // Time it will take the effect to fully recede in ms
23 const int kRecedeTime = 1000;
25 // Time it will take before a pulled glow begins receding in ms
26 const int kPullTime = 167;
28 // Time it will take in ms for a pulled glow to decay before release
29 const int kPullDecayTime = 1000;
31 const float kMaxAlpha = 1.f;
32 const float kHeldEdgeScaleY = .5f;
34 const float kMaxGlowHeight = 4.f;
36 const float kPullGlowBegin = 1.f;
37 const float kPullEdgeBegin = 0.6f;
39 // Minimum velocity that will be absorbed
40 const float kMinVelocity = 100.f;
42 const float kEpsilon = 0.001f;
44 // How much dragging should effect the height of the edge image.
45 // Number determined by user testing.
46 const int kPullDistanceEdgeFactor = 7;
48 // How much dragging should effect the height of the glow image.
49 // Number determined by user testing.
50 const int kPullDistanceGlowFactor = 7;
51 const float kPullDistanceAlphaGlowFactor = 1.1f;
53 const int kVelocityEdgeFactor = 8;
54 const int kVelocityGlowFactor = 16;
56 template <typename T>
57 T Lerp(T a, T b, T t) {
58 return a + (b - a) * t;
61 template <typename T>
62 T Clamp(T value, T low, T high) {
63 return value < low ? low : (value > high ? high : value);
66 template <typename T>
67 T Damp(T input, T factor) {
68 T result;
69 if (factor == 1) {
70 result = 1 - (1 - input) * (1 - input);
71 } else {
72 result = 1 - std::pow(1 - input, 2 * factor);
74 return result;
77 gfx::Transform ComputeTransform(EdgeEffect::Edge edge,
78 gfx::SizeF size, int height) {
79 switch (edge) {
80 default:
81 case EdgeEffect::EDGE_TOP:
82 return gfx::Transform(1, 0, 0, 1, 0, 0);
83 case EdgeEffect::EDGE_LEFT:
84 return gfx::Transform(0, 1, -1, 0,
85 (-size.width() + height) / 2 ,
86 (size.width() - height) / 2);
87 case EdgeEffect::EDGE_BOTTOM:
88 return gfx::Transform(-1, 0, 0, -1, 0, size.height() - height);
89 case EdgeEffect::EDGE_RIGHT:
90 return gfx::Transform(0, -1, 1, 0,
91 (-size.width() - height) / 2 + size.height(),
92 (size.width() - height) / 2);
96 void DisableLayer(cc::Layer* layer) {
97 DCHECK(layer);
98 layer->SetIsDrawable(false);
99 layer->SetTransform(gfx::Transform());
100 layer->SetOpacity(1.f);
103 void UpdateLayer(cc::Layer* layer,
104 EdgeEffect::Edge edge,
105 gfx::SizeF size,
106 int height,
107 float opacity) {
108 DCHECK(layer);
109 layer->SetIsDrawable(true);
110 layer->SetTransform(ComputeTransform(edge, size, height));
111 layer->SetBounds(gfx::Size(size.width(), height));
112 layer->SetOpacity(Clamp(opacity, 0.f, 1.f));
115 } // namespace
117 EdgeEffect::EdgeEffect(scoped_refptr<cc::Layer> edge,
118 scoped_refptr<cc::Layer> glow)
119 : edge_(edge)
120 , glow_(glow)
121 , edge_alpha_(0)
122 , edge_scale_y_(0)
123 , glow_alpha_(0)
124 , glow_scale_y_(0)
125 , edge_alpha_start_(0)
126 , edge_alpha_finish_(0)
127 , edge_scale_y_start_(0)
128 , edge_scale_y_finish_(0)
129 , glow_alpha_start_(0)
130 , glow_alpha_finish_(0)
131 , glow_scale_y_start_(0)
132 , glow_scale_y_finish_(0)
133 , state_(STATE_IDLE)
134 , pull_distance_(0)
135 , dpi_scale_(1) {
136 // Prevent the provided layers from drawing until the effect is activated.
137 DisableLayer(edge_.get());
138 DisableLayer(glow_.get());
140 dpi_scale_ =
141 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().device_scale_factor();
144 EdgeEffect::~EdgeEffect() { }
146 bool EdgeEffect::IsFinished() const {
147 return state_ == STATE_IDLE;
150 void EdgeEffect::Finish() {
151 DisableLayer(edge_.get());
152 DisableLayer(glow_.get());
153 pull_distance_ = 0;
154 state_ = STATE_IDLE;
157 void EdgeEffect::Pull(base::TimeTicks current_time, float delta_distance) {
158 if (state_ == STATE_PULL_DECAY && current_time - start_time_ < duration_) {
159 return;
161 if (state_ != STATE_PULL) {
162 glow_scale_y_ = kPullGlowBegin;
164 state_ = STATE_PULL;
166 start_time_ = current_time;
167 duration_ = base::TimeDelta::FromMilliseconds(kPullTime);
169 delta_distance *= dpi_scale_;
170 float abs_delta_distance = std::abs(delta_distance);
171 pull_distance_ += delta_distance;
172 float distance = std::abs(pull_distance_);
174 edge_alpha_ = edge_alpha_start_ = Clamp(distance, kPullEdgeBegin, kMaxAlpha);
175 edge_scale_y_ = edge_scale_y_start_
176 = Clamp(distance * kPullDistanceEdgeFactor, kHeldEdgeScaleY, 1.f);
178 glow_alpha_ = glow_alpha_start_ =
179 std::min(kMaxAlpha,
180 glow_alpha_ + abs_delta_distance * kPullDistanceAlphaGlowFactor);
182 float glow_change = abs_delta_distance;
183 if (delta_distance > 0 && pull_distance_ < 0)
184 glow_change = -glow_change;
185 if (pull_distance_ == 0)
186 glow_scale_y_ = 0;
188 // Do not allow glow to get larger than kMaxGlowHeight.
189 glow_scale_y_ = glow_scale_y_start_ =
190 Clamp(glow_scale_y_ + glow_change * kPullDistanceGlowFactor,
191 0.f, kMaxGlowHeight);
193 edge_alpha_finish_ = edge_alpha_;
194 edge_scale_y_finish_ = edge_scale_y_;
195 glow_alpha_finish_ = glow_alpha_;
196 glow_scale_y_finish_ = glow_scale_y_;
199 void EdgeEffect::Release(base::TimeTicks current_time) {
200 pull_distance_ = 0;
202 if (state_ != STATE_PULL && state_ != STATE_PULL_DECAY)
203 return;
205 state_ = STATE_RECEDE;
206 edge_alpha_start_ = edge_alpha_;
207 edge_scale_y_start_ = edge_scale_y_;
208 glow_alpha_start_ = glow_alpha_;
209 glow_scale_y_start_ = glow_scale_y_;
211 edge_alpha_finish_ = 0.f;
212 edge_scale_y_finish_ = 0.f;
213 glow_alpha_finish_ = 0.f;
214 glow_scale_y_finish_ = 0.f;
216 start_time_ = current_time;
217 duration_ = base::TimeDelta::FromMilliseconds(kRecedeTime);
220 void EdgeEffect::Absorb(base::TimeTicks current_time, float velocity) {
221 state_ = STATE_ABSORB;
222 float scaled_velocity =
223 dpi_scale_ * std::max(kMinVelocity, std::abs(velocity));
225 start_time_ = current_time;
226 // This should never be less than 1 millisecond.
227 duration_ = base::TimeDelta::FromMilliseconds(0.1f + (velocity * 0.03f));
229 // The edge should always be at least partially visible, regardless
230 // of velocity.
231 edge_alpha_start_ = 0.f;
232 edge_scale_y_ = edge_scale_y_start_ = 0.f;
233 // The glow depends more on the velocity, and therefore starts out
234 // nearly invisible.
235 glow_alpha_start_ = 0.5f;
236 glow_scale_y_start_ = 0.f;
238 // Factor the velocity by 8. Testing on device shows this works best to
239 // reflect the strength of the user's scrolling.
240 edge_alpha_finish_ = Clamp(scaled_velocity * kVelocityEdgeFactor, 0.f, 1.f);
241 // Edge should never get larger than the size of its asset.
242 edge_scale_y_finish_ = Clamp(scaled_velocity * kVelocityEdgeFactor,
243 kHeldEdgeScaleY, 1.f);
245 // Growth for the size of the glow should be quadratic to properly
246 // respond
247 // to a user's scrolling speed. The faster the scrolling speed, the more
248 // intense the effect should be for both the size and the saturation.
249 glow_scale_y_finish_ = std::min(
250 0.025f + (scaled_velocity * (scaled_velocity / 100) * 0.00015f), 1.75f);
251 // Alpha should change for the glow as well as size.
252 glow_alpha_finish_ = Clamp(glow_alpha_start_,
253 scaled_velocity * kVelocityGlowFactor * .00001f,
254 kMaxAlpha);
257 bool EdgeEffect::Update(base::TimeTicks current_time) {
258 if (IsFinished())
259 return false;
261 const double dt = (current_time - start_time_).InMilliseconds();
262 const double t = std::min(dt / duration_.InMilliseconds(), 1.);
263 const float interp = static_cast<float>(Damp(t, 1.));
265 edge_alpha_ = Lerp(edge_alpha_start_, edge_alpha_finish_, interp);
266 edge_scale_y_ = Lerp(edge_scale_y_start_, edge_scale_y_finish_, interp);
267 glow_alpha_ = Lerp(glow_alpha_start_, glow_alpha_finish_, interp);
268 glow_scale_y_ = Lerp(glow_scale_y_start_, glow_scale_y_finish_, interp);
270 if (t >= 1.f - kEpsilon) {
271 switch (state_) {
272 case STATE_ABSORB:
273 state_ = STATE_RECEDE;
274 start_time_ = current_time;
275 duration_ = base::TimeDelta::FromMilliseconds(kRecedeTime);
277 edge_alpha_start_ = edge_alpha_;
278 edge_scale_y_start_ = edge_scale_y_;
279 glow_alpha_start_ = glow_alpha_;
280 glow_scale_y_start_ = glow_scale_y_;
282 // After absorb, the glow and edge should fade to nothing.
283 edge_alpha_finish_ = 0.f;
284 edge_scale_y_finish_ = 0.f;
285 glow_alpha_finish_ = 0.f;
286 glow_scale_y_finish_ = 0.f;
287 break;
288 case STATE_PULL:
289 state_ = STATE_PULL_DECAY;
290 start_time_ = current_time;
291 duration_ = base::TimeDelta::FromMilliseconds(kPullDecayTime);
293 edge_alpha_start_ = edge_alpha_;
294 edge_scale_y_start_ = edge_scale_y_;
295 glow_alpha_start_ = glow_alpha_;
296 glow_scale_y_start_ = glow_scale_y_;
298 // After pull, the glow and edge should fade to nothing.
299 edge_alpha_finish_ = 0.f;
300 edge_scale_y_finish_ = 0.f;
301 glow_alpha_finish_ = 0.f;
302 glow_scale_y_finish_ = 0.f;
303 break;
304 case STATE_PULL_DECAY:
306 // When receding, we want edge to decrease more slowly
307 // than the glow.
308 float factor = glow_scale_y_finish_ != 0 ?
309 1 / (glow_scale_y_finish_ * glow_scale_y_finish_) :
310 std::numeric_limits<float>::max();
311 edge_scale_y_ = edge_scale_y_start_ +
312 (edge_scale_y_finish_ - edge_scale_y_start_) * interp * factor;
313 state_ = STATE_RECEDE;
315 break;
316 case STATE_RECEDE:
317 Finish();
318 break;
319 default:
320 break;
324 if (state_ == STATE_RECEDE && glow_scale_y_ <= 0 && edge_scale_y_ <= 0)
325 Finish();
327 return !IsFinished();
330 void EdgeEffect::ApplyToLayers(gfx::SizeF size, Edge edge) {
331 if (IsFinished())
332 return;
334 // An empty effect size, while meaningless, is also relatively harmless, and
335 // will simply prevent any drawing of the layers.
336 if (size.IsEmpty()) {
337 DisableLayer(edge_.get());
338 DisableLayer(glow_.get());
339 return;
342 float dummy_scale_x, dummy_scale_y;
344 // Glow
345 gfx::Size glow_image_bounds;
346 glow_->CalculateContentsScale(1.f, 1.f, 1.f, false,
347 &dummy_scale_x, &dummy_scale_y,
348 &glow_image_bounds);
349 const int glow_height = glow_image_bounds.height();
350 const int glow_width = glow_image_bounds.width();
351 const int glow_bottom = static_cast<int>(std::min(
352 glow_height * glow_scale_y_ * glow_height / glow_width * 0.6f,
353 glow_height * kMaxGlowHeight) * dpi_scale_ + 0.5f);
354 UpdateLayer(glow_.get(), edge, size, glow_bottom, glow_alpha_);
356 // Edge
357 gfx::Size edge_image_bounds;
358 edge_->CalculateContentsScale(1.f, 1.f, 1.f, false,
359 &dummy_scale_x, &dummy_scale_y,
360 &edge_image_bounds);
361 const int edge_height = edge_image_bounds.height();
362 const int edge_bottom = static_cast<int>(
363 edge_height * edge_scale_y_ * dpi_scale_);
364 UpdateLayer(edge_.get(), edge, size, edge_bottom, edge_alpha_);
367 } // namespace content