Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(990)

Side by Side Diff: ash/touch/touch_transformer_controller.cc

Issue 2617043003: Move TouchTransformerController to ui/display/. (Closed)
Patch Set: Add comments. Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 "ash/touch/touch_transformer_controller.h"
6
7 #include "ash/display/window_tree_host_manager.h"
8 #include "ash/host/ash_window_tree_host.h"
9 #include "ash/root_window_controller.h"
10 #include "ash/shell.h"
11 #include "third_party/skia/include/core/SkMatrix44.h"
12 #include "ui/aura/window_tree_host.h"
13 #include "ui/display/display_layout.h"
14 #include "ui/display/manager/chromeos/display_configurator.h"
15 #include "ui/display/manager/display_manager.h"
16 #include "ui/display/types/display_constants.h"
17 #include "ui/display/types/display_snapshot.h"
18 #include "ui/events/devices/device_data_manager.h"
19
20 namespace ash {
21
22 namespace {
23
24 display::DisplayManager* GetDisplayManager() {
25 return Shell::GetInstance()->display_manager();
26 }
27
28 ui::TouchscreenDevice FindTouchscreenById(int id) {
29 const std::vector<ui::TouchscreenDevice>& touchscreens =
30 ui::DeviceDataManager::GetInstance()->GetTouchscreenDevices();
31 for (const auto& touchscreen : touchscreens) {
32 if (touchscreen.id == id)
33 return touchscreen;
34 }
35
36 return ui::TouchscreenDevice();
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 A, B, C, D, E and F and sets |ctm| with the calibrated
46 // transform matrix. In case the computation fails, the function will return
47 // false.
48 // See http://crbug.com/672293
49 bool GetCalibratedTransform(
50 std::array<std::pair<gfx::Point, gfx::Point>, 4> touch_point_pairs,
51 const gfx::Transform& pre_calibration_tm,
52 gfx::Transform* ctm) {
53 // Transform the display points before solving the equation.
54 // If the calibration was performed at a resolution that is 0.5 times the
55 // current resolution, then the display points (x, y) for a given touch point
56 // now represents a display point at (2 * x, 2 * y). This and other kinds of
57 // similar tranforms can be applied using |pre_calibration_tm|.
58 for (int row = 0; row < 4; row++)
59 pre_calibration_tm.TransformPoint(&touch_point_pairs[row].first);
60
61 // Vector of the X-coordinate of display points corresponding to each of the
62 // touch points.
63 SkVector4 display_points_x(
64 touch_point_pairs[0].first.x(), touch_point_pairs[1].first.x(),
65 touch_point_pairs[2].first.x(), touch_point_pairs[3].first.x());
66 // Vector of the Y-coordinate of display points corresponding to each of the
67 // touch points.
68 SkVector4 display_points_y(
69 touch_point_pairs[0].first.y(), touch_point_pairs[1].first.y(),
70 touch_point_pairs[2].first.y(), touch_point_pairs[3].first.y());
71
72 // Initialize |touch_point_matrix|
73 // If {(xt_1, yt_1), (xt_2, yt_2), (xt_3, yt_3)....} are a set of touch points
74 // received during calibration, then the |touch_point_matrix| would be defined
75 // as:
76 // |xt_1 yt_1 1 0|
77 // |xt_2 yt_2 1 0|
78 // |xt_3 yt_3 1 0|
79 // |xt_4 yt_4 1 0|
80 SkMatrix44 touch_point_matrix;
81 for (int row = 0; row < 4; row++) {
82 touch_point_matrix.set(row, 0, touch_point_pairs[row].second.x());
83 touch_point_matrix.set(row, 1, touch_point_pairs[row].second.y());
84 touch_point_matrix.set(row, 2, 1);
85 touch_point_matrix.set(row, 3, 0);
86 }
87 SkMatrix44 touch_point_matrix_transpose(touch_point_matrix);
88 touch_point_matrix_transpose.transpose();
89
90 SkMatrix44 product_matrix = touch_point_matrix_transpose * touch_point_matrix;
91
92 // Set (3, 3) = 1 so that |determinent| of the matrix is != 0 and the inverse
93 // can be calculated.
94 product_matrix.set(3, 3, 1);
95
96 SkMatrix44 product_matrix_inverse;
97
98 // NOTE: If the determinent is zero then the inverse cannot be computed. The
99 // only solution is to restart touch calibration and get new points from user.
100 if (!product_matrix.invert(&product_matrix_inverse)) {
101 NOTREACHED() << "Touch Calibration failed. Determinent is zero.";
102 return false;
103 }
104
105 product_matrix_inverse.set(3, 3, 0);
106
107 product_matrix = product_matrix_inverse * touch_point_matrix_transpose;
108
109 // Constants [A, B, C, 0] used to calibrate the x-coordinate of touch input.
110 // x_new = x_old * A + y_old * B + C;
111 SkVector4 x_constants = product_matrix * display_points_x;
112 // Constants [D, E, F, 0] used to calibrate the y-coordinate of touch input.
113 // y_new = x_old * D + y_old * E + F;
114 SkVector4 y_constants = product_matrix * display_points_y;
115
116 // Create a transform matrix using the touch calibration data.
117 ctm->ConcatTransform(gfx::Transform(
118 x_constants.fData[0], x_constants.fData[1], 0, x_constants.fData[2],
119 y_constants.fData[0], y_constants.fData[1], 0, y_constants.fData[2], 0, 0,
120 1, 0, 0, 0, 0, 1));
121 return true;
122 }
123
124 // Returns an uncalibrated touch transform.
125 gfx::Transform GetUncalibratedTransform(
126 const gfx::Transform& tm,
127 const display::ManagedDisplayInfo& display,
128 const display::ManagedDisplayInfo& touch_display,
129 const gfx::SizeF& touch_area,
130 const gfx::SizeF& touch_native_size) {
131 gfx::SizeF current_size(display.bounds_in_native().size());
132 gfx::Transform ctm(tm);
133 // Take care of panel fitting only if supported. Panel fitting is emulated
134 // in software mirroring mode (display != touch_display).
135 // If panel fitting is enabled then the aspect ratio is preserved and the
136 // display is scaled acordingly. In this case blank regions would be present
137 // in order to center the displayed area.
138 if (display.is_aspect_preserving_scaling() ||
139 display.id() != touch_display.id()) {
140 float touch_calib_ar =
141 touch_native_size.width() / touch_native_size.height();
142 float current_ar = current_size.width() / current_size.height();
143
144 if (current_ar > touch_calib_ar) { // Letterboxing
145 ctm.Translate(
146 0, (1 - current_ar / touch_calib_ar) * 0.5 * current_size.height());
147 ctm.Scale(1, current_ar / touch_calib_ar);
148 } else if (touch_calib_ar > current_ar) { // Pillarboxing
149 ctm.Translate(
150 (1 - touch_calib_ar / current_ar) * 0.5 * current_size.width(), 0);
151 ctm.Scale(touch_calib_ar / current_ar, 1);
152 }
153 }
154 // Take care of scaling between touchscreen area and display resolution.
155 ctm.Scale(current_size.width() / touch_area.width(),
156 current_size.height() / touch_area.height());
157 return ctm;
158 }
159
160 } // namespace
161
162 // This is to compute the scale ratio for the TouchEvent's radius. The
163 // configured resolution of the display is not always the same as the touch
164 // screen's reporting resolution, e.g. the display could be set as
165 // 1920x1080 while the touchscreen is reporting touch position range at
166 // 32767x32767. Touch radius is reported in the units the same as touch position
167 // so we need to scale the touch radius to be compatible with the display's
168 // resolution. We compute the scale as
169 // sqrt of (display_area / touchscreen_area)
170 double TouchTransformerController::GetTouchResolutionScale(
171 const display::ManagedDisplayInfo& touch_display,
172 const ui::TouchscreenDevice& touch_device) const {
173 if (touch_device.id == ui::InputDevice::kInvalidId ||
174 touch_device.size.IsEmpty() ||
175 touch_display.bounds_in_native().size().IsEmpty())
176 return 1.0;
177
178 double display_area = touch_display.bounds_in_native().size().GetArea();
179 double touch_area = touch_device.size.GetArea();
180 double ratio = std::sqrt(display_area / touch_area);
181
182 VLOG(2) << "Display size: "
183 << touch_display.bounds_in_native().size().ToString()
184 << ", Touchscreen size: " << touch_device.size.ToString()
185 << ", Touch radius scale ratio: " << ratio;
186 return ratio;
187 }
188
189 gfx::Transform TouchTransformerController::GetTouchTransform(
190 const display::ManagedDisplayInfo& display,
191 const display::ManagedDisplayInfo& touch_display,
192 const ui::TouchscreenDevice& touchscreen,
193 const gfx::Size& framebuffer_size) const {
194 auto current_size = gfx::SizeF(display.bounds_in_native().size());
195 auto touch_native_size = gfx::SizeF(touch_display.GetNativeModeSize());
196 #if defined(USE_OZONE)
197 auto touch_area = gfx::SizeF(touchscreen.size);
198 #elif defined(USE_X11)
199 // On X11 touches are reported in the framebuffer coordinate space.
200 auto touch_area = gfx::SizeF(framebuffer_size);
201 #endif
202
203 gfx::Transform ctm;
204
205 if (current_size.IsEmpty() || touch_native_size.IsEmpty() ||
206 touch_area.IsEmpty() || touchscreen.id == ui::InputDevice::kInvalidId)
207 return ctm;
208
209 #if defined(USE_OZONE)
210 // Translate the touch so that it falls within the display bounds. This
211 // should not be performed if the displays are mirrored.
212 if (display.id() == touch_display.id()) {
213 ctm.Translate(display.bounds_in_native().x(),
214 display.bounds_in_native().y());
215 }
216 #endif
217
218 // If touch calibration data is unavailable, use naive approach.
219 if (!touch_display.has_touch_calibration_data()) {
220 return GetUncalibratedTransform(ctm, display, touch_display, touch_area,
221 touch_native_size);
222 }
223
224 // The resolution at which the touch calibration was performed.
225 gfx::SizeF touch_calib_size(touch_display.GetTouchCalibrationData().bounds);
226
227 // Any additional transfomration that needs to be applied to the display
228 // points, before we solve for the final transform.
229 gfx::Transform pre_transform;
230
231 if (display.id() != touch_display.id() ||
232 display.is_aspect_preserving_scaling()) {
233 // Case of displays being mirrored or in panel fitting mode.
234 // Aspect ratio of the touch display's resolution during calibration.
235 float calib_ar = touch_calib_size.width() / touch_calib_size.height();
236 // Aspect ratio of the display that is being mirrored.
237 float current_ar = current_size.width() / current_size.height();
238
239 if (current_ar < calib_ar) {
240 pre_transform.Scale(current_size.height() / touch_calib_size.height(),
241 current_size.height() / touch_calib_size.height());
242 pre_transform.Translate(
243 (current_ar / calib_ar - 1.f) * touch_calib_size.width() * 0.5f, 0);
244 } else {
245 pre_transform.Scale(current_size.width() / touch_calib_size.width(),
246 current_size.width() / touch_calib_size.width());
247 pre_transform.Translate(
248 0, (calib_ar / current_ar - 1.f) * touch_calib_size.height() * 0.5f);
249 }
250 } else {
251 // Case of current resolution being different from the resolution when the
252 // touch calibration was performed.
253 pre_transform.Scale(current_size.width() / touch_calib_size.width(),
254 current_size.height() / touch_calib_size.height());
255 }
256 // Solve for coefficients and compute transform matrix.
257 gfx::Transform stored_ctm;
258 if (!GetCalibratedTransform(
259 touch_display.GetTouchCalibrationData().point_pairs, pre_transform,
260 &stored_ctm)) {
261 // TODO(malaykeshav): This can be checked at the calibration step before
262 // storing the calibration associated data. This will allow us to explicitly
263 // inform the user with proper UX.
264
265 // Clear stored calibration data.
266 GetDisplayManager()->ClearTouchCalibrationData(touch_display.id());
267
268 // Return uncalibrated transform.
269 return GetUncalibratedTransform(ctm, display, touch_display, touch_area,
270 touch_native_size);
271 }
272
273 stored_ctm.ConcatTransform(ctm);
274 return stored_ctm;
275 }
276
277 TouchTransformerController::TouchTransformerController() {
278 Shell::GetInstance()->window_tree_host_manager()->AddObserver(this);
279 }
280
281 TouchTransformerController::~TouchTransformerController() {
282 Shell::GetInstance()->window_tree_host_manager()->RemoveObserver(this);
283 }
284
285 void TouchTransformerController::UpdateTouchRadius(
286 const display::ManagedDisplayInfo& display) const {
287 ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance();
288 for (const auto& device_id : display.input_devices()) {
289 device_manager->UpdateTouchRadiusScale(
290 device_id,
291 GetTouchResolutionScale(display, FindTouchscreenById(device_id)));
292 }
293 }
294
295 void TouchTransformerController::UpdateTouchTransform(
296 int64_t target_display_id,
297 const display::ManagedDisplayInfo& touch_display,
298 const display::ManagedDisplayInfo& target_display) const {
299 ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance();
300 gfx::Size fb_size =
301 Shell::GetInstance()->display_configurator()->framebuffer_size();
302 for (const auto& device_id : touch_display.input_devices()) {
303 device_manager->UpdateTouchInfoForDisplay(
304 target_display_id, device_id,
305 GetTouchTransform(target_display, touch_display,
306 FindTouchscreenById(device_id), fb_size));
307 }
308 }
309
310 void TouchTransformerController::UpdateTouchTransformer() const {
311 ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance();
312 device_manager->ClearTouchDeviceAssociations();
313
314 // Display IDs and display::ManagedDisplayInfo for mirror or extended mode.
315 int64_t display1_id = display::kInvalidDisplayId;
316 int64_t display2_id = display::kInvalidDisplayId;
317 display::ManagedDisplayInfo display1;
318 display::ManagedDisplayInfo display2;
319 // Display ID and display::ManagedDisplayInfo for single display mode.
320 int64_t single_display_id = display::kInvalidDisplayId;
321 display::ManagedDisplayInfo single_display;
322
323 WindowTreeHostManager* window_tree_host_manager =
324 Shell::GetInstance()->window_tree_host_manager();
325 display::DisplayManager* display_manager = GetDisplayManager();
326 if (display_manager->num_connected_displays() == 0) {
327 return;
328 } else if (display_manager->num_connected_displays() == 1 ||
329 display_manager->IsInUnifiedMode()) {
330 single_display_id = display_manager->first_display_id();
331 DCHECK(single_display_id != display::kInvalidDisplayId);
332 single_display = display_manager->GetDisplayInfo(single_display_id);
333 UpdateTouchRadius(single_display);
334 } else {
335 display::DisplayIdList list = display_manager->GetCurrentDisplayIdList();
336 display1_id = list[0];
337 display2_id = list[1];
338 DCHECK(display1_id != display::kInvalidDisplayId &&
339 display2_id != display::kInvalidDisplayId);
340 display1 = display_manager->GetDisplayInfo(display1_id);
341 display2 = display_manager->GetDisplayInfo(display2_id);
342 UpdateTouchRadius(display1);
343 UpdateTouchRadius(display2);
344 }
345
346 if (display_manager->IsInMirrorMode()) {
347 int64_t primary_display_id =
348 window_tree_host_manager->GetPrimaryDisplayId();
349 if (GetDisplayManager()->SoftwareMirroringEnabled()) {
350 // In extended but software mirroring mode, there is a WindowTreeHost for
351 // each display, but all touches are forwarded to the primary root
352 // window's WindowTreeHost.
353 display::ManagedDisplayInfo target_display =
354 primary_display_id == display1_id ? display1 : display2;
355 UpdateTouchTransform(target_display.id(), display1, target_display);
356 UpdateTouchTransform(target_display.id(), display2, target_display);
357 } else {
358 // In mirror mode, there is just one WindowTreeHost and two displays. Make
359 // the WindowTreeHost accept touch events from both displays.
360 UpdateTouchTransform(primary_display_id, display1, display1);
361 UpdateTouchTransform(primary_display_id, display2, display2);
362 }
363 return;
364 }
365
366 if (display_manager->num_connected_displays() > 1) {
367 // In actual extended mode, each display is associated with one
368 // WindowTreeHost.
369 UpdateTouchTransform(display1_id, display1, display1);
370 UpdateTouchTransform(display2_id, display2, display2);
371 return;
372 }
373
374 // Single display mode. The WindowTreeHost has one associated display id.
375 UpdateTouchTransform(single_display_id, single_display, single_display);
376 }
377
378 void TouchTransformerController::OnDisplaysInitialized() {
379 UpdateTouchTransformer();
380 }
381
382 void TouchTransformerController::OnDisplayConfigurationChanged() {
383 UpdateTouchTransformer();
384 }
385
386 } // namespace ash
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698