Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 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 "ash/touch/touch_transformer_controller.h" | 5 #include "ash/touch/touch_transformer_controller.h" |
| 6 | 6 |
| 7 #include "ash/display/window_tree_host_manager.h" | 7 #include "ash/display/window_tree_host_manager.h" |
| 8 #include "ash/host/ash_window_tree_host.h" | 8 #include "ash/host/ash_window_tree_host.h" |
| 9 #include "ash/root_window_controller.h" | 9 #include "ash/root_window_controller.h" |
| 10 #include "ash/shell.h" | 10 #include "ash/shell.h" |
| 11 #include "third_party/skia/include/core/SkMatrix44.h" | |
| 11 #include "ui/aura/window_tree_host.h" | 12 #include "ui/aura/window_tree_host.h" |
| 12 #include "ui/display/display_layout.h" | 13 #include "ui/display/display_layout.h" |
| 13 #include "ui/display/manager/chromeos/display_configurator.h" | 14 #include "ui/display/manager/chromeos/display_configurator.h" |
| 14 #include "ui/display/manager/display_manager.h" | 15 #include "ui/display/manager/display_manager.h" |
| 15 #include "ui/display/types/display_constants.h" | 16 #include "ui/display/types/display_constants.h" |
| 16 #include "ui/display/types/display_snapshot.h" | 17 #include "ui/display/types/display_snapshot.h" |
| 17 #include "ui/events/devices/device_data_manager.h" | 18 #include "ui/events/devices/device_data_manager.h" |
| 18 | 19 |
| 19 namespace ash { | 20 namespace ash { |
| 20 | 21 |
| 21 namespace { | 22 namespace { |
| 22 | 23 |
| 23 display::DisplayManager* GetDisplayManager() { | 24 display::DisplayManager* GetDisplayManager() { |
| 24 return Shell::GetInstance()->display_manager(); | 25 return Shell::GetInstance()->display_manager(); |
| 25 } | 26 } |
| 26 | 27 |
| 27 ui::TouchscreenDevice FindTouchscreenById(int id) { | 28 ui::TouchscreenDevice FindTouchscreenById(int id) { |
| 28 const std::vector<ui::TouchscreenDevice>& touchscreens = | 29 const std::vector<ui::TouchscreenDevice>& touchscreens = |
| 29 ui::DeviceDataManager::GetInstance()->GetTouchscreenDevices(); | 30 ui::DeviceDataManager::GetInstance()->GetTouchscreenDevices(); |
| 30 for (const auto& touchscreen : touchscreens) { | 31 for (const auto& touchscreen : touchscreens) { |
| 31 if (touchscreen.id == id) | 32 if (touchscreen.id == id) |
| 32 return touchscreen; | 33 return touchscreen; |
| 33 } | 34 } |
| 34 | 35 |
| 35 return ui::TouchscreenDevice(); | 36 return ui::TouchscreenDevice(); |
| 36 } | 37 } |
| 37 | 38 |
| 39 // Given an array of touch point and display point pairs, this function computes | |
| 40 // and returns the constants(defined below) using a least fit algorithm. | |
| 41 // If (xt, yt) is a touch point then its corresponding (xd, yd) would be defined | |
| 42 // by the following 2 equations: | |
| 43 // xd = xt * A + yt * B + C | |
| 44 // yd = xt * D + yt * E + F | |
| 45 // This function computes and returns A, B, C, D, E and F as a pair of | |
| 46 // SkVector4. | |
| 47 // See http://crbug.com/672293 | |
| 48 std::pair<SkVector4, SkVector4> SolveCalibrationEquation( | |
| 49 std::array<std::pair<gfx::Point, gfx::Point>, 4> touch_point_pairs, | |
| 50 const gfx::Transform& pre_calibration_tm) { | |
| 51 // If {(xt_1, yt_1), (xt_2, yt_2), (xt_3, yt_3)....} are a set of touch points | |
| 52 // received during calibration, then the |touch_point_matrix| would be defined | |
| 53 // as: | |
| 54 // |xt_1 yt_1 1 0| | |
| 55 // |xt_2 yt_2 1 0| | |
| 56 // |xt_3 yt_3 1 0| | |
| 57 // |xt_4 yt_4 1 0| | |
| 58 SkMatrix44 touch_point_matrix; | |
|
kylechar
2016/12/19 20:00:21
Can you move variables closer to where they are fi
malaykeshav
2016/12/20 09:56:04
Done
| |
| 59 SkMatrix44 touch_point_matrix_transpose; | |
| 60 | |
| 61 SkMatrix44 product_matrix; | |
| 62 SkMatrix44 product_matrix_inverse; | |
| 63 | |
| 64 // Transform the display points before solving the equation. | |
| 65 // If the calibration was performed at a resolution that is 0.5 times the | |
| 66 // current resolution, then the display points (x, y) for a given touch point | |
| 67 // now represents a display point at (2 * x, 2 * y). This and other kinds of | |
| 68 // similar tranforms can be applied using |pre_calibration_tm|. | |
| 69 for (int row = 0; row < 4; row++) | |
| 70 pre_calibration_tm.TransformPoint(&touch_point_pairs[row].first); | |
| 71 | |
| 72 // Vector of the X-coordinate of display points corresponding to each of the | |
| 73 // touch points. | |
| 74 SkVector4 display_points_x( | |
| 75 touch_point_pairs[0].first.x(), touch_point_pairs[1].first.x(), | |
| 76 touch_point_pairs[2].first.x(), touch_point_pairs[3].first.x()); | |
| 77 // Vector of the Y-coordinate of display points corresponding to each of the | |
| 78 // touch points. | |
| 79 SkVector4 display_points_y( | |
| 80 touch_point_pairs[0].first.y(), touch_point_pairs[1].first.y(), | |
| 81 touch_point_pairs[2].first.y(), touch_point_pairs[3].first.y()); | |
| 82 | |
| 83 SkVector4 result_constants_x, result_constants_y; | |
| 84 | |
| 85 // Initialize |touch_point_matrix| | |
| 86 for (int row = 0; row < 4; row++) { | |
| 87 touch_point_matrix.set(row, 0, touch_point_pairs[row].second.x()); | |
| 88 touch_point_matrix.set(row, 1, touch_point_pairs[row].second.y()); | |
| 89 touch_point_matrix.set(row, 2, 1); | |
| 90 touch_point_matrix.set(row, 3, 0); | |
| 91 } | |
| 92 touch_point_matrix_transpose = touch_point_matrix; | |
| 93 touch_point_matrix_transpose.transpose(); | |
| 94 | |
| 95 product_matrix = touch_point_matrix_transpose * touch_point_matrix; | |
| 96 | |
| 97 // Set (3, 3) = 1 so that |determinent| of the matrix is != 0 and the inverse | |
| 98 // can be calculated. | |
| 99 product_matrix.set(3, 3, 1); | |
| 100 | |
| 101 // #NOTE: If the determinent is zero then the inverse cannot be computed. The | |
| 102 // only solution is to restart touch calibration and get new points from user. | |
| 103 CHECK(product_matrix.invert(&product_matrix_inverse)); | |
|
kylechar
2016/12/19 20:00:21
Is crashing the correct thing to do if the inverse
malaykeshav
2016/12/20 09:56:04
This is an extremely rare case since we are using
| |
| 104 | |
| 105 product_matrix_inverse.set(3, 3, 0); | |
| 106 | |
| 107 product_matrix = product_matrix_inverse * touch_point_matrix_transpose; | |
| 108 | |
| 109 // Vector containing the constants [A, B, C, 0] | |
| 110 result_constants_x = product_matrix * display_points_x; | |
| 111 // Vector containing the constants [D, E, F, 0] | |
| 112 result_constants_y = product_matrix * display_points_y; | |
| 113 | |
| 114 return std::make_pair(result_constants_x, result_constants_y); | |
|
kylechar
2016/12/19 20:00:21
Why doesn't this just return the gfx::Transform yo
malaykeshav
2016/12/20 09:56:04
Done.
| |
| 115 } | |
| 116 | |
| 38 } // namespace | 117 } // namespace |
| 39 | 118 |
| 40 // This is to compute the scale ratio for the TouchEvent's radius. The | 119 // This is to compute the scale ratio for the TouchEvent's radius. The |
| 41 // configured resolution of the display is not always the same as the touch | 120 // configured resolution of the display is not always the same as the touch |
| 42 // screen's reporting resolution, e.g. the display could be set as | 121 // screen's reporting resolution, e.g. the display could be set as |
| 43 // 1920x1080 while the touchscreen is reporting touch position range at | 122 // 1920x1080 while the touchscreen is reporting touch position range at |
| 44 // 32767x32767. Touch radius is reported in the units the same as touch position | 123 // 32767x32767. Touch radius is reported in the units the same as touch position |
| 45 // so we need to scale the touch radius to be compatible with the display's | 124 // so we need to scale the touch radius to be compatible with the display's |
| 46 // resolution. We compute the scale as | 125 // resolution. We compute the scale as |
| 47 // sqrt of (display_area / touchscreen_area) | 126 // sqrt of (display_area / touchscreen_area) |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 72 auto current_size = gfx::SizeF(display.bounds_in_native().size()); | 151 auto current_size = gfx::SizeF(display.bounds_in_native().size()); |
| 73 auto touch_native_size = gfx::SizeF(touch_display.GetNativeModeSize()); | 152 auto touch_native_size = gfx::SizeF(touch_display.GetNativeModeSize()); |
| 74 #if defined(USE_OZONE) | 153 #if defined(USE_OZONE) |
| 75 auto touch_area = gfx::SizeF(touchscreen.size); | 154 auto touch_area = gfx::SizeF(touchscreen.size); |
| 76 #elif defined(USE_X11) | 155 #elif defined(USE_X11) |
| 77 // On X11 touches are reported in the framebuffer coordinate space. | 156 // On X11 touches are reported in the framebuffer coordinate space. |
| 78 auto touch_area = gfx::SizeF(framebuffer_size); | 157 auto touch_area = gfx::SizeF(framebuffer_size); |
| 79 #endif | 158 #endif |
| 80 | 159 |
| 81 gfx::Transform ctm; | 160 gfx::Transform ctm; |
| 161 gfx::Transform stored_ctm; | |
|
kylechar
2016/12/19 20:00:21
Define variable where you're going to use it.
malaykeshav
2016/12/20 09:56:04
Done.
| |
| 82 | 162 |
| 83 if (current_size.IsEmpty() || touch_native_size.IsEmpty() || | 163 if (current_size.IsEmpty() || touch_native_size.IsEmpty() || |
| 84 touch_area.IsEmpty() || touchscreen.id == ui::InputDevice::kInvalidId) | 164 touch_area.IsEmpty() || touchscreen.id == ui::InputDevice::kInvalidId) |
| 85 return ctm; | 165 return ctm; |
| 86 | 166 |
| 87 #if defined(USE_OZONE) | 167 #if defined(USE_OZONE) |
| 88 // Translate the touch so that it falls within the display bounds. | 168 // Translate the touch so that it falls within the display bounds. This should |
| 89 ctm.Translate(display.bounds_in_native().x(), display.bounds_in_native().y()); | 169 // not be performed if the displays are mirrored. |
| 170 if (display.id() == touch_display.id()) | |
| 171 ctm.Translate(display.bounds_in_native().x(), | |
| 172 display.bounds_in_native().y()); | |
| 90 #endif | 173 #endif |
| 91 | 174 |
| 92 // Take care of panel fitting only if supported. Panel fitting is emulated in | 175 // If the device is currently under calibration, then do not return any |
| 93 // software mirroring mode (display != touch_display). | 176 // transform as we want to use the raw native touch input data for calibration |
| 94 // If panel fitting is enabled then the aspect ratio is preserved and the | 177 if (is_calibrating_) |
| 95 // display is scaled acordingly. In this case blank regions would be present | 178 return ctm; |
| 96 // in order to center the displayed area. | 179 // If touch calibration data is unavailable, use naive approach. |
| 97 if (display.is_aspect_preserving_scaling() || | 180 if (!touch_display.has_touch_calibration_data()) { |
|
kylechar
2016/12/19 20:00:21
Could you split this function into something like
malaykeshav
2016/12/20 09:56:04
Splitting the uncalibrated transform and moving to
| |
| 98 display.id() != touch_display.id()) { | 181 // Take care of panel fitting only if supported. Panel fitting is emulated |
| 99 float touch_native_ar = | 182 // in software mirroring mode (display != touch_display). |
| 100 touch_native_size.width() / touch_native_size.height(); | 183 // If panel fitting is enabled then the aspect ratio is preserved and the |
| 184 // display is scaled acordingly. In this case blank regions would be present | |
| 185 // in order to center the displayed area. | |
| 186 if (display.is_aspect_preserving_scaling() || | |
| 187 display.id() != touch_display.id()) { | |
| 188 float touch_calib_ar = | |
| 189 touch_native_size.width() / touch_native_size.height(); | |
| 190 float current_ar = current_size.width() / current_size.height(); | |
| 191 | |
| 192 if (current_ar > touch_calib_ar) { // Letterboxing | |
| 193 ctm.Translate( | |
| 194 0, (1 - current_ar / touch_calib_ar) * 0.5 * current_size.height()); | |
| 195 ctm.Scale(1, current_ar / touch_calib_ar); | |
| 196 } else if (touch_calib_ar > current_ar) { // Pillarboxing | |
| 197 ctm.Translate( | |
| 198 (1 - touch_calib_ar / current_ar) * 0.5 * current_size.width(), 0); | |
| 199 ctm.Scale(touch_calib_ar / current_ar, 1); | |
| 200 } | |
| 201 } | |
| 202 // Take care of scaling between touchscreen area and display resolution. | |
| 203 ctm.Scale(current_size.width() / touch_area.width(), | |
| 204 current_size.height() / touch_area.height()); | |
| 205 return ctm; | |
| 206 } | |
| 207 // The resolution at which the touch calibration was performed. | |
| 208 auto touch_calib_size = | |
|
kylechar
2016/12/19 20:00:21
gfx::SizeF touch_calib_size(...)
malaykeshav
2016/12/20 09:56:04
Done
| |
| 209 gfx::SizeF(touch_display.GetTouchCalibrationData().bounds); | |
| 210 | |
| 211 // Any additional tranfomration that needs to be applied to the display | |
|
kylechar
2016/12/19 20:00:21
transformation
malaykeshav
2016/12/20 09:56:04
done
| |
| 212 // points, before we solve for the final transform. | |
| 213 gfx::Transform pre_transform; | |
| 214 | |
| 215 if (display.id() != touch_display.id() || | |
| 216 display.is_aspect_preserving_scaling()) { | |
| 217 // Case of displays being mirrored or in panel fitting mode. | |
| 218 // Aspect ratio of the touch display's resolution during calibration. | |
| 219 float calib_ar = touch_calib_size.width() / touch_calib_size.height(); | |
| 220 // Aspect ratio of the display that is being mirrored. | |
| 101 float current_ar = current_size.width() / current_size.height(); | 221 float current_ar = current_size.width() / current_size.height(); |
| 102 | 222 |
| 103 if (current_ar > touch_native_ar) { // Letterboxing | 223 if (current_ar < calib_ar) { |
| 104 ctm.Translate( | 224 pre_transform.Scale(current_size.height() / touch_calib_size.height(), |
| 105 0, (1 - current_ar / touch_native_ar) * 0.5 * current_size.height()); | 225 current_size.height() / touch_calib_size.height()); |
| 106 ctm.Scale(1, current_ar / touch_native_ar); | 226 pre_transform.Translate( |
| 107 } else if (touch_native_ar > current_ar) { // Pillarboxing | 227 (current_ar / calib_ar - 1.f) * touch_calib_size.width() * 0.5f, 0); |
| 108 ctm.Translate( | 228 } else { |
| 109 (1 - touch_native_ar / current_ar) * 0.5 * current_size.width(), 0); | 229 pre_transform.Scale(current_size.width() / touch_calib_size.width(), |
| 110 ctm.Scale(touch_native_ar / current_ar, 1); | 230 current_size.width() / touch_calib_size.width()); |
| 231 pre_transform.Translate( | |
| 232 0, (calib_ar / current_ar - 1.f) * touch_calib_size.height() * 0.5f); | |
| 111 } | 233 } |
| 234 } else { | |
| 235 // Case of current resolution being different from the resolution when the | |
| 236 // touch calibration was performed. | |
| 237 pre_transform.Scale(current_size.width() / touch_calib_size.width(), | |
| 238 current_size.height() / touch_calib_size.height()); | |
| 112 } | 239 } |
| 240 // Calibrate using the calibration data associated with the display. | |
| 241 std::pair<SkVector4, SkVector4> result = SolveCalibrationEquation( | |
|
kylechar
2016/12/19 20:00:21
Same as above, if this returned the gfx::Transform
malaykeshav
2016/12/20 09:56:04
done
| |
| 242 touch_display.GetTouchCalibrationData().point_pairs, pre_transform); | |
| 113 | 243 |
| 114 // Take care of scaling between touchscreen area and display resolution. | 244 // Constants [A, B, C, 0] used to calibrate the x-coordinate of touch input. |
| 115 ctm.Scale(current_size.width() / touch_area.width(), | 245 // x_new = x_old * A + y_old * B + C; |
| 116 current_size.height() / touch_area.height()); | 246 const SkVector4& x_constants = result.first; |
| 117 return ctm; | 247 // Constants [D, E, F, 0] used to calibrate the y-coordinate of touch input. |
| 248 // y_new = x_old * D + y_old * E + F; | |
| 249 const SkVector4& y_constants = result.second; | |
| 250 | |
| 251 // Create a transform matrix using the touch calibration data. | |
| 252 stored_ctm = gfx::Transform(x_constants.fData[0], x_constants.fData[1], 0, | |
| 253 x_constants.fData[2], y_constants.fData[0], | |
| 254 y_constants.fData[1], 0, y_constants.fData[2], 0, | |
| 255 0, 1, 0, 0, 0, 0, 1); | |
| 256 stored_ctm.ConcatTransform(ctm); | |
| 257 return stored_ctm; | |
| 118 } | 258 } |
| 119 | 259 |
| 120 TouchTransformerController::TouchTransformerController() { | 260 TouchTransformerController::TouchTransformerController() { |
| 121 Shell::GetInstance()->window_tree_host_manager()->AddObserver(this); | 261 Shell::GetInstance()->window_tree_host_manager()->AddObserver(this); |
| 122 } | 262 } |
| 123 | 263 |
| 124 TouchTransformerController::~TouchTransformerController() { | 264 TouchTransformerController::~TouchTransformerController() { |
| 125 Shell::GetInstance()->window_tree_host_manager()->RemoveObserver(this); | 265 Shell::GetInstance()->window_tree_host_manager()->RemoveObserver(this); |
| 126 } | 266 } |
| 127 | 267 |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 219 } | 359 } |
| 220 | 360 |
| 221 void TouchTransformerController::OnDisplaysInitialized() { | 361 void TouchTransformerController::OnDisplaysInitialized() { |
| 222 UpdateTouchTransformer(); | 362 UpdateTouchTransformer(); |
| 223 } | 363 } |
| 224 | 364 |
| 225 void TouchTransformerController::OnDisplayConfigurationChanged() { | 365 void TouchTransformerController::OnDisplayConfigurationChanged() { |
| 226 UpdateTouchTransformer(); | 366 UpdateTouchTransformer(); |
| 227 } | 367 } |
| 228 | 368 |
| 369 void TouchTransformerController::SetForCalibration(bool is_calibrating) { | |
| 370 is_calibrating_ = is_calibrating; | |
| 371 UpdateTouchTransformer(); | |
| 372 } | |
| 373 | |
| 229 } // namespace ash | 374 } // namespace ash |
| OLD | NEW |