OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 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/renderer_host/input/gestures/scale_gesture_detector.h" |
| 6 |
| 7 #include <limits.h> |
| 8 #include <math.h> |
| 9 |
| 10 #include "base/logging.h" |
| 11 #include "content/browser/renderer_host/input/gestures/motion_event.h" |
| 12 |
| 13 using base::TimeDelta; |
| 14 using base::TimeTicks; |
| 15 |
| 16 namespace content { |
| 17 namespace { |
| 18 const TimeDelta TOUCH_STABILIZE_TIME = TimeDelta::FromMilliseconds(128); |
| 19 const float SCALE_FACTOR = .5f; |
| 20 } |
| 21 |
| 22 ScaleGestureDetector::Config::Config() |
| 23 : quick_scale_enabled(false), |
| 24 min_scaling_touch_major(1), |
| 25 min_scaling_span(1) {} |
| 26 |
| 27 ScaleGestureDetector::Config::~Config() {} |
| 28 |
| 29 bool ScaleGestureDetector::SimpleOnScaleGestureListener::OnScale( |
| 30 const ScaleGestureDetector&) { |
| 31 return false; |
| 32 } |
| 33 |
| 34 bool ScaleGestureDetector::SimpleOnScaleGestureListener::OnScaleBegin( |
| 35 const ScaleGestureDetector&) { |
| 36 return true; |
| 37 } |
| 38 |
| 39 void ScaleGestureDetector::SimpleOnScaleGestureListener::OnScaleEnd( |
| 40 const ScaleGestureDetector&) {} |
| 41 |
| 42 |
| 43 ScaleGestureDetector::ScaleGestureDetector(Config config, |
| 44 OnScaleGestureListener* listener) |
| 45 : listener_(listener), |
| 46 config_(config), |
| 47 focus_x_(0), |
| 48 focus_y_(0), |
| 49 quick_scale_enabled_(false), |
| 50 curr_span_(0), |
| 51 prev_span_(0), |
| 52 initial_span_(0), |
| 53 curr_span_x_(0), |
| 54 curr_span_y_(0), |
| 55 prev_span_x_(0), |
| 56 prev_span_y_(0), |
| 57 in_progress_(0), |
| 58 span_slop_(0), |
| 59 min_span_(0), |
| 60 touch_upper_(0), |
| 61 touch_lower_(0), |
| 62 touch_history_last_accepted_(0), |
| 63 touch_history_direction_(0), |
| 64 touch_min_major_(0), |
| 65 double_tap_focus_x_(0), |
| 66 double_tap_focus_y_(0), |
| 67 double_tap_mode_(DOUBLE_TAP_MODE_NONE), |
| 68 event_before_or_above_starting_gesture_event_(false) { |
| 69 DCHECK(listener_); |
| 70 span_slop_ = config.gesture_detector_config.scaled_touch_slop * 2; |
| 71 touch_min_major_ = config.min_scaling_touch_major; |
| 72 min_span_ = config.min_scaling_span; |
| 73 SetQuickScaleEnabled(config.quick_scale_enabled); |
| 74 } |
| 75 |
| 76 ScaleGestureDetector::~ScaleGestureDetector() {} |
| 77 |
| 78 bool ScaleGestureDetector::OnTouchEvent(const MotionEvent& event) { |
| 79 curr_time_ = event.GetEventTime(); |
| 80 |
| 81 const int action = event.GetActionMasked(); |
| 82 |
| 83 // Forward the event to check for double tap gesture |
| 84 if (quick_scale_enabled_) { |
| 85 DCHECK(gesture_detector_); |
| 86 gesture_detector_->OnTouchEvent(event); |
| 87 } |
| 88 |
| 89 const bool stream_complete = action == MotionEvent::ACTION_UP || |
| 90 action == MotionEvent::ACTION_CANCEL; |
| 91 |
| 92 if (action == MotionEvent::ACTION_DOWN || stream_complete) { |
| 93 // Reset any scale in progress with the listener. |
| 94 // If it's an ACTION_DOWN we're beginning a new event stream. |
| 95 // This means the app probably didn't give us all the events. Shame on it. |
| 96 if (in_progress_) { |
| 97 listener_->OnScaleEnd(*this); |
| 98 in_progress_ = false; |
| 99 initial_span_ = 0; |
| 100 double_tap_mode_ = DOUBLE_TAP_MODE_NONE; |
| 101 } else if (double_tap_mode_ == DOUBLE_TAP_MODE_IN_PROGRESS && |
| 102 stream_complete) { |
| 103 in_progress_ = false; |
| 104 initial_span_ = 0; |
| 105 double_tap_mode_ = DOUBLE_TAP_MODE_NONE; |
| 106 } |
| 107 |
| 108 if (stream_complete) { |
| 109 ClearTouchHistory(); |
| 110 return true; |
| 111 } |
| 112 } |
| 113 |
| 114 const bool config_changed = action == MotionEvent::ACTION_DOWN || |
| 115 action == MotionEvent::ACTION_POINTER_UP || |
| 116 action == MotionEvent::ACTION_POINTER_DOWN; |
| 117 |
| 118 const bool pionter_up = action == MotionEvent::ACTION_POINTER_UP; |
| 119 const int skip_index = pionter_up ? event.GetActionIndex() : -1; |
| 120 |
| 121 // Determine focal point |
| 122 float sum_x = 0, sum_y = 0; |
| 123 const int count = event.GetPointerCount(); |
| 124 const int div = pionter_up ? count - 1 : count; |
| 125 float focus_x; |
| 126 float focus_y; |
| 127 if (double_tap_mode_ == DOUBLE_TAP_MODE_IN_PROGRESS) { |
| 128 // In double tap mode, the focal pt is always where the double tap |
| 129 // gesture started |
| 130 focus_x = double_tap_focus_x_; |
| 131 focus_y = double_tap_focus_y_; |
| 132 if (event.GetY() < focus_y) { |
| 133 event_before_or_above_starting_gesture_event_ = true; |
| 134 } else { |
| 135 event_before_or_above_starting_gesture_event_ = false; |
| 136 } |
| 137 } else { |
| 138 for (int i = 0; i < count; i++) { |
| 139 if (skip_index == i) |
| 140 continue; |
| 141 sum_x += event.GetX(i); |
| 142 sum_y += event.GetY(i); |
| 143 } |
| 144 |
| 145 focus_x = sum_x / div; |
| 146 focus_y = sum_y / div; |
| 147 } |
| 148 |
| 149 AddTouchHistory(event); |
| 150 |
| 151 // Determine average deviation from focal point |
| 152 float dev_sum_x = 0, dev_sum_y = 0; |
| 153 for (int i = 0; i < count; i++) { |
| 154 if (skip_index == i) |
| 155 continue; |
| 156 |
| 157 // Convert the resulting diameter into a radius. |
| 158 const float touch_size = touch_history_last_accepted_ / 2; |
| 159 dev_sum_x += std::abs(event.GetX(i) - focus_x) + touch_size; |
| 160 dev_sum_y += std::abs(event.GetY(i) - focus_y) + touch_size; |
| 161 } |
| 162 const float dev_x = dev_sum_x / div; |
| 163 const float dev_y = dev_sum_y / div; |
| 164 |
| 165 // Span is the average distance between touch points through the focal point; |
| 166 // i.e. the diameter of the circle with a radius of the average deviation from |
| 167 // the focal point. |
| 168 const float span_x = dev_x * 2; |
| 169 const float span_y = dev_y * 2; |
| 170 float span; |
| 171 if (InDoubleTapMode()) { |
| 172 span = span_y; |
| 173 } else { |
| 174 span = std::sqrt(span_x * span_x + span_y * span_y); |
| 175 } |
| 176 |
| 177 // Dispatch begin/end events as needed. |
| 178 // If the configuration changes, notify the app to reset its current state by |
| 179 // beginning a fresh scale event stream. |
| 180 const bool was_in_progress = in_progress_; |
| 181 focus_x_ = focus_x; |
| 182 focus_y_ = focus_y; |
| 183 if (!InDoubleTapMode() && |
| 184 in_progress_ && |
| 185 (span < min_span_ || config_changed)) { |
| 186 listener_->OnScaleEnd(*this); |
| 187 in_progress_ = false; |
| 188 initial_span_ = span; |
| 189 double_tap_mode_ = DOUBLE_TAP_MODE_NONE; |
| 190 } |
| 191 if (config_changed) { |
| 192 prev_span_x_ = curr_span_x_ = span_x; |
| 193 prev_span_y_ = curr_span_y_ = span_y; |
| 194 initial_span_ = prev_span_ = curr_span_ = span; |
| 195 } |
| 196 |
| 197 const int min_span = InDoubleTapMode() ? span_slop_ : min_span_; |
| 198 if (!in_progress_ && span >= min_span && |
| 199 (was_in_progress || std::abs(span - initial_span_) > span_slop_)) { |
| 200 prev_span_x_ = curr_span_x_ = span_x; |
| 201 prev_span_y_ = curr_span_y_ = span_y; |
| 202 prev_span_ = curr_span_ = span; |
| 203 prev_time_ = curr_time_; |
| 204 in_progress_ = listener_->OnScaleBegin(*this); |
| 205 } |
| 206 |
| 207 // Handle motion; focal point and span/scale factor are changing. |
| 208 if (action == MotionEvent::ACTION_MOVE) { |
| 209 curr_span_x_ = span_x; |
| 210 curr_span_y_ = span_y; |
| 211 curr_span_ = span; |
| 212 |
| 213 bool update_prev = true; |
| 214 |
| 215 if (in_progress_) { |
| 216 update_prev = listener_->OnScale(*this); |
| 217 } |
| 218 |
| 219 if (update_prev) { |
| 220 prev_span_x_ = curr_span_x_; |
| 221 prev_span_y_ = curr_span_y_; |
| 222 prev_span_ = curr_span_; |
| 223 prev_time_ = curr_time_; |
| 224 } |
| 225 } |
| 226 |
| 227 return true; |
| 228 } |
| 229 |
| 230 void ScaleGestureDetector::SetQuickScaleEnabled(bool scales) { |
| 231 quick_scale_enabled_ = scales; |
| 232 if (quick_scale_enabled_ && !gesture_detector_) { |
| 233 gesture_detector_.reset( |
| 234 new GestureDetector(config_.gesture_detector_config, this, this)); |
| 235 } |
| 236 } |
| 237 |
| 238 bool ScaleGestureDetector::IsQuickScaleEnabled() const { |
| 239 return quick_scale_enabled_; |
| 240 } |
| 241 |
| 242 bool ScaleGestureDetector::IsInProgress() const { |
| 243 return in_progress_; |
| 244 } |
| 245 |
| 246 float ScaleGestureDetector::GetFocusX() const { |
| 247 return focus_x_; |
| 248 } |
| 249 |
| 250 float ScaleGestureDetector::GetFocusY() const { |
| 251 return focus_y_; |
| 252 } |
| 253 |
| 254 float ScaleGestureDetector::GetCurrentSpan() const { |
| 255 return curr_span_; |
| 256 } |
| 257 |
| 258 float ScaleGestureDetector::GetCurrentSpanX() const { |
| 259 return curr_span_x_; |
| 260 } |
| 261 |
| 262 float ScaleGestureDetector::GetCurrentSpanY() const { |
| 263 return curr_span_y_; |
| 264 } |
| 265 |
| 266 float ScaleGestureDetector::GetPreviousSpan() const { |
| 267 return prev_span_; |
| 268 } |
| 269 |
| 270 float ScaleGestureDetector::GetPreviousSpanX() const { |
| 271 return prev_span_x_; |
| 272 } |
| 273 |
| 274 float ScaleGestureDetector::GetPreviousSpanY() const { |
| 275 return prev_span_y_; |
| 276 } |
| 277 |
| 278 float ScaleGestureDetector::GetScaleFactor() const { |
| 279 if (InDoubleTapMode()) { |
| 280 // Drag is moving up; the further away from the gesture |
| 281 // start, the smaller the span should be, the closer, |
| 282 // the larger the span, and therefore the larger the scale |
| 283 const bool scale_up = |
| 284 (event_before_or_above_starting_gesture_event_ && |
| 285 (curr_span_ < prev_span_)) || |
| 286 (!event_before_or_above_starting_gesture_event_ && |
| 287 (curr_span_ > prev_span_)); |
| 288 const float span_diff = |
| 289 (std::abs(1.f - (curr_span_ / prev_span_)) * SCALE_FACTOR); |
| 290 return prev_span_ <= 0 ? |
| 291 1.f : (scale_up ? (1.f + span_diff) : (1.f - span_diff)); |
| 292 } |
| 293 return prev_span_ > 0 ? curr_span_ / prev_span_ : 1; |
| 294 } |
| 295 |
| 296 base::TimeDelta ScaleGestureDetector::GetTimeDelta() const { |
| 297 return curr_time_ - prev_time_; |
| 298 } |
| 299 |
| 300 base::TimeTicks ScaleGestureDetector::GetEventTime() const { |
| 301 return curr_time_; |
| 302 } |
| 303 |
| 304 bool ScaleGestureDetector::OnDoubleTap(const MotionEvent& ev) { |
| 305 // Double tap: start watching for a swipe |
| 306 double_tap_focus_x_ = ev.GetX(); |
| 307 double_tap_focus_y_ = ev.GetY(); |
| 308 double_tap_mode_ = DOUBLE_TAP_MODE_IN_PROGRESS; |
| 309 return true; |
| 310 } |
| 311 |
| 312 void ScaleGestureDetector::AddTouchHistory(const MotionEvent& ev) { |
| 313 const base::TimeTicks current_time = base::TimeTicks::Now(); |
| 314 const int count = ev.GetPointerCount(); |
| 315 bool accept = |
| 316 current_time - touch_history_last_accepted_time_ >= TOUCH_STABILIZE_TIME; |
| 317 float total = 0; |
| 318 int sample_count = 0; |
| 319 for (int i = 0; i < count; i++) { |
| 320 const bool has_last_accepted = !std::isnan(touch_history_last_accepted_); |
| 321 const int history_size = ev.GetHistorySize(); |
| 322 const int pointersample_count = history_size + 1; |
| 323 for (int h = 0; h < pointersample_count; h++) { |
| 324 float major; |
| 325 if (h < history_size) { |
| 326 major = ev.GetHistoricalTouchMajor(i, h); |
| 327 } else { |
| 328 major = ev.GetTouchMajor(i); |
| 329 } |
| 330 if (major < touch_min_major_) major = touch_min_major_; |
| 331 total += major; |
| 332 |
| 333 if (std::isnan(touch_upper_) || major > touch_upper_) { |
| 334 touch_upper_ = major; |
| 335 } |
| 336 if (std::isnan(touch_lower_) || major < touch_lower_) { |
| 337 touch_lower_ = major; |
| 338 } |
| 339 |
| 340 if (has_last_accepted) { |
| 341 const float majorDelta = major - touch_history_last_accepted_; |
| 342 const int direction_sig = |
| 343 majorDelta > 0 ? 1 : (majorDelta < 0 ? -1 : 0); |
| 344 if (direction_sig != touch_history_direction_ || |
| 345 (direction_sig == 0 && touch_history_direction_ == 0)) { |
| 346 touch_history_direction_ = direction_sig; |
| 347 touch_history_last_accepted_time_ = |
| 348 h < history_size ? ev.GetHistoricalEventTime(h) |
| 349 : ev.GetEventTime(); |
| 350 accept = false; |
| 351 } |
| 352 } |
| 353 } |
| 354 sample_count += pointersample_count; |
| 355 } |
| 356 |
| 357 const float avg = total / sample_count; |
| 358 |
| 359 if (accept) { |
| 360 float newAccepted = (touch_upper_ + touch_lower_ + avg) / 3; |
| 361 touch_upper_ = (touch_upper_ + newAccepted) / 2; |
| 362 touch_lower_ = (touch_lower_ + newAccepted) / 2; |
| 363 touch_history_last_accepted_ = newAccepted; |
| 364 touch_history_direction_ = 0; |
| 365 touch_history_last_accepted_time_ = ev.GetEventTime(); |
| 366 } |
| 367 } |
| 368 |
| 369 void ScaleGestureDetector::ClearTouchHistory() { |
| 370 touch_upper_ = std::numeric_limits<float>::quiet_NaN(); |
| 371 touch_lower_ = std::numeric_limits<float>::quiet_NaN(); |
| 372 touch_history_last_accepted_ = std::numeric_limits<float>::quiet_NaN(); |
| 373 touch_history_direction_ = 0; |
| 374 touch_history_last_accepted_time_ = base::TimeTicks(); |
| 375 } |
| 376 |
| 377 bool ScaleGestureDetector::InDoubleTapMode() const { |
| 378 return double_tap_mode_ == DOUBLE_TAP_MODE_IN_PROGRESS; |
| 379 } |
| 380 |
| 381 } // namespace content |
OLD | NEW |