1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
ash / display / root_window_transformers.cc [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/display/root_window_transformers.h"
#include <cmath>
#include "ash/accessibility/magnifier/fullscreen_magnifier_controller.h"
#include "ash/display/display_util.h"
#include "ash/host/root_window_transformer.h"
#include "ash/shell.h"
#include "ash/utility/transformer_util.h"
#include "base/command_line.h"
#include "base/memory/ptr_util.h"
#include "base/system/sys_info.h"
#include "ui/display/display.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/transform.h"
namespace ash {
namespace {
// TODO(oshima): Transformers should be able to adjust itself
// when the device scale factor is changed, instead of
// precalculating the transform using fixed value.
// Creates rotation transform for |root_window| to |new_rotation|. This will
// call |CreateRotationTransform()|, the |old_rotation| will implicitly be
// |display::Display::ROTATE_0|.
gfx::Transform CreateRootWindowRotationTransform(
const display::Display& display) {
gfx::SizeF size(display.GetSizeInPixel());
// Use SizeF so that the origin of translated layer will be
// aligned when scaled back at pixels.
size.Scale(1.f / display.device_scale_factor());
return CreateRotationTransform(display::Display::ROTATE_0,
display.panel_rotation(), size);
}
gfx::Transform CreateInsetsTransform(const gfx::Insets& insets,
float device_scale_factor) {
gfx::Transform transform;
if (insets.top() != 0 || insets.left() != 0) {
float x_offset = insets.left() / device_scale_factor;
float y_offset = insets.top() / device_scale_factor;
transform.Translate(x_offset, y_offset);
}
return transform;
}
// Returns a transform with rotation adjusted |insets_in_pixel|. The transform
// is applied to the root window so that |insets_in_pixel| looks correct after
// the rotation applied at the output.
gfx::Transform CreateReverseRotatedInsetsTransform(
display::Display::Rotation rotation,
const gfx::Insets& insets_in_pixel,
float device_scale_factor) {
float x_offset = 0;
float y_offset = 0;
switch (rotation) {
case display::Display::ROTATE_0:
x_offset = insets_in_pixel.left();
y_offset = insets_in_pixel.top();
break;
case display::Display::ROTATE_90:
x_offset = insets_in_pixel.top();
y_offset = insets_in_pixel.right();
break;
case display::Display::ROTATE_180:
x_offset = insets_in_pixel.right();
y_offset = insets_in_pixel.bottom();
break;
case display::Display::ROTATE_270:
x_offset = insets_in_pixel.bottom();
y_offset = insets_in_pixel.left();
break;
}
gfx::Transform transform;
if (x_offset != 0 || y_offset != 0) {
x_offset /= device_scale_factor;
y_offset /= device_scale_factor;
transform.Translate(x_offset, y_offset);
}
return transform;
}
// RootWindowTransformer for ash environment.
class AshRootWindowTransformer : public RootWindowTransformer {
public:
AshRootWindowTransformer(const display::Display& display) {
initial_root_bounds_ = gfx::Rect(display.size());
display::DisplayManager* display_manager = Shell::Get()->display_manager();
display::ManagedDisplayInfo info =
display_manager->GetDisplayInfo(display.id());
host_insets_ = info.GetOverscanInsetsInPixel();
gfx::Transform insets_and_rotation_transform =
CreateInsetsTransform(host_insets_, display.device_scale_factor()) *
CreateRootWindowRotationTransform(display);
transform_ = insets_and_rotation_transform;
insets_and_scale_transform_ = CreateReverseRotatedInsetsTransform(
display.panel_rotation(), host_insets_, display.device_scale_factor());
FullscreenMagnifierController* magnifier =
Shell::Get()->fullscreen_magnifier_controller();
if (magnifier) {
gfx::Transform magnifier_scale = magnifier->GetMagnifierTransform();
transform_ *= magnifier_scale;
insets_and_scale_transform_ *= magnifier_scale;
}
CHECK(transform_.GetInverse(&invert_transform_));
CHECK(insets_and_rotation_transform.GetInverse(
&root_window_bounds_transform_));
root_window_bounds_transform_.Scale(1.f / display.device_scale_factor(),
1.f / display.device_scale_factor());
initial_host_size_ = info.bounds_in_native().size();
}
AshRootWindowTransformer(const AshRootWindowTransformer&) = delete;
AshRootWindowTransformer& operator=(const AshRootWindowTransformer&) = delete;
// aura::RootWindowTransformer overrides:
gfx::Transform GetTransform() const override { return transform_; }
gfx::Transform GetInverseTransform() const override {
return invert_transform_;
}
gfx::Rect GetRootWindowBounds(const gfx::Size& host_size) const override {
if (base::SysInfo::IsRunningOnChromeOS())
return initial_root_bounds_;
// If we're running on linux desktop for dev purpose, the host window
// may be updated to new size. Recompute the root window bounds based
// on the host size if the host size changed.
if (initial_host_size_ == host_size)
return initial_root_bounds_;
gfx::RectF new_bounds = gfx::RectF(gfx::SizeF(host_size));
new_bounds.Inset(gfx::InsetsF(host_insets_));
new_bounds = root_window_bounds_transform_.MapRect(new_bounds);
// Root window origin will be (0,0) except during bounds changes.
// Set to exactly zero to avoid rounding issues.
// Floor the size because the bounds is no longer aligned to
// backing pixel when |root_window_scale_| is specified
// (850 height at 1.25 scale becomes 1062.5 for example.)
return gfx::Rect(gfx::ToFlooredSize(new_bounds.size()));
}
gfx::Insets GetHostInsets() const override { return host_insets_; }
gfx::Transform GetInsetsAndScaleTransform() const override {
return insets_and_scale_transform_;
}
private:
~AshRootWindowTransformer() override = default;
gfx::Transform transform_;
// The accurate representation of the inverse of the |transform_|.
// This is used to avoid computation error caused by
// |gfx::Transform::GetInverse|.
gfx::Transform invert_transform_;
// The transform of the root window bounds. This is used to calculate the size
// of the root window. It is the composition of the following transforms
// - inverse of insets. Insets position the content area within the display.
// - inverse of rotation. Rotation changes orientation of the content area.
// - inverse of device scale. Scaling up content shrinks the content area.
//
// Insets also shrink the content area but this happens prior to applying the
// transformation in GetRootWindowBounds().
//
// Magnification does not affect the window size. Content is clipped in this
// case, but the magnifier allows panning to reach clipped areas.
//
// The transforms are inverted because GetTransform() is the transform from
// root window coordinates to host coordinates, but this transform is used in
// the reverse direction (derives root window bounds from display bounds).
gfx::Transform root_window_bounds_transform_;
gfx::Insets host_insets_;
gfx::Transform insets_and_scale_transform_;
gfx::Rect initial_root_bounds_;
gfx::Size initial_host_size_;
};
// RootWindowTransformer for mirror root window. We simply copy the
// texture (bitmap) of the source display into the mirror window, so
// the root window bounds is the same as the source display's
// pixel size (excluding overscan insets).
class MirrorRootWindowTransformer : public RootWindowTransformer {
public:
MirrorRootWindowTransformer(
const display::ManagedDisplayInfo& source_display_info,
const display::ManagedDisplayInfo& mirror_display_info) {
root_bounds_ =
gfx::Rect(source_display_info.GetSizeInPixelWithPanelOrientation());
display::Display::Rotation active_root_rotation =
source_display_info.GetActiveRotation();
const bool should_undo_rotation = ShouldUndoRotationForMirror();
gfx::Transform rotation_transform;
if (should_undo_rotation) {
// Calculate the transform to undo the rotation and apply it to the
// source display. Use GetActiveRotation() because |source_bounds_|
// includes panel rotation and we only need to revert active rotation.
rotation_transform = CreateRotationTransform(
source_display_info.GetActiveRotation(), display::Display::ROTATE_0,
gfx::SizeF(root_bounds_.size()));
root_bounds_ = gfx::ToNearestRect(
rotation_transform.MapRect(gfx::RectF(root_bounds_)));
active_root_rotation = display::Display::ROTATE_0;
}
gfx::Rect mirror_display_rect =
gfx::Rect(mirror_display_info.bounds_in_native().size());
// When logical rotation is 90 or 270 degree, transpose is needed to apply
// reverse rotation to `root_bounds_` and `mirror_display_rect` to exclude
// the rotation. This is because the rotation happens at viz output and
// `transform_` needs to be calculated without the rotation.
// E.g. host native size 1600x1200. Rotation 90 degree. `transform_` needs
// to fit 1200x1600 rather than 1600x1200.
const bool need_transpose =
active_root_rotation == display::Display::ROTATE_90 ||
active_root_rotation == display::Display::ROTATE_270;
if (need_transpose) {
root_bounds_.Transpose();
mirror_display_rect.Transpose();
}
bool letterbox = root_bounds_.width() * mirror_display_rect.height() >
root_bounds_.height() * mirror_display_rect.width();
if (letterbox) {
float mirror_scale_ratio =
(static_cast<float>(root_bounds_.width()) /
static_cast<float>(mirror_display_rect.width()));
float inverted_scale = 1.0f / mirror_scale_ratio;
int margin = static_cast<int>((mirror_display_rect.height() -
root_bounds_.height() * inverted_scale) /
2);
insets_ = gfx::Insets::TLBR(0, margin, 0, margin);
transform_.Translate(0, margin);
transform_.Scale(inverted_scale, inverted_scale);
} else {
float mirror_scale_ratio =
(static_cast<float>(root_bounds_.height()) /
static_cast<float>(mirror_display_rect.height()));
float inverted_scale = 1.0f / mirror_scale_ratio;
int margin = static_cast<int>((mirror_display_rect.width() -
root_bounds_.width() * inverted_scale) /
2);
insets_ = gfx::Insets::TLBR(margin, 0, margin, 0);
transform_.Translate(margin, 0);
transform_.Scale(inverted_scale, inverted_scale);
}
}
MirrorRootWindowTransformer(const MirrorRootWindowTransformer&) = delete;
MirrorRootWindowTransformer& operator=(const MirrorRootWindowTransformer&) =
delete;
// aura::RootWindowTransformer overrides:
gfx::Transform GetTransform() const override { return transform_; }
gfx::Transform GetInverseTransform() const override {
gfx::Transform invert;
CHECK(transform_.GetInverse(&invert));
return invert;
}
gfx::Rect GetRootWindowBounds(const gfx::Size& host_size) const override {
return root_bounds_;
}
gfx::Insets GetHostInsets() const override { return insets_; }
gfx::Transform GetInsetsAndScaleTransform() const override {
return transform_;
}
private:
~MirrorRootWindowTransformer() override = default;
gfx::Transform transform_;
gfx::Rect root_bounds_;
gfx::Insets insets_;
};
class PartialBoundsRootWindowTransformer : public RootWindowTransformer {
public:
PartialBoundsRootWindowTransformer(const gfx::Rect& screen_bounds,
const display::Display& display) {
const display::DisplayManager* display_manager =
Shell::Get()->display_manager();
display::ManagedDisplayInfo display_info =
display_manager->GetDisplayInfo(display.id());
// Physical root bounds.
root_bounds_ = gfx::Rect(display_info.bounds_in_native().size());
display::Display::Rotation active_root_rotation =
display_info.GetActiveRotation();
const bool need_transpose =
active_root_rotation == display::Display::ROTATE_90 ||
active_root_rotation == display::Display::ROTATE_270;
if (need_transpose)
root_bounds_.Transpose();
// |screen_bounds| is the unified desktop logical bounds.
// Calculate the unified height scale value, and apply the same scale on the
// row physical height to get the row logical height.
display::Display unified_display =
display::Screen::GetScreen()->GetPrimaryDisplay();
const int unified_physical_height =
unified_display.GetSizeInPixel().height();
const int unified_logical_height = screen_bounds.height();
const float unified_height_scale =
static_cast<float>(unified_logical_height) / unified_physical_height;
const int row_index =
display_manager->GetMirroringDisplayRowIndexInUnifiedMatrix(
display.id());
const int row_physical_height =
display_manager->GetUnifiedDesktopRowMaxHeight(row_index);
const int row_logical_height = row_physical_height * unified_height_scale;
const float dsf = unified_display.device_scale_factor();
const float scale = root_bounds_.height() / (dsf * row_logical_height);
transform_.Scale(scale, scale);
transform_.Translate(-SkIntToScalar(display.bounds().x()),
-SkIntToScalar(display.bounds().y()));
// Scaling using physical root bounds here, because rotation is applied
// before device_scale_factor is applied.
gfx::Transform rotation = CreateRotationTransform(
display::Display::ROTATE_0, display.panel_rotation(),
gfx::SizeF(root_bounds_.size()));
CHECK((rotation * transform_).GetInverse(&invert_transform_));
}
PartialBoundsRootWindowTransformer(
const PartialBoundsRootWindowTransformer&) = delete;
PartialBoundsRootWindowTransformer& operator=(
const PartialBoundsRootWindowTransformer&) = delete;
// RootWindowTransformer:
gfx::Transform GetTransform() const override { return transform_; }
gfx::Transform GetInverseTransform() const override {
return invert_transform_;
}
gfx::Rect GetRootWindowBounds(const gfx::Size& host_size) const override {
return root_bounds_;
}
gfx::Insets GetHostInsets() const override { return gfx::Insets(); }
gfx::Transform GetInsetsAndScaleTransform() const override {
return transform_;
}
private:
~PartialBoundsRootWindowTransformer() override = default;
gfx::Transform transform_;
gfx::Transform invert_transform_;
gfx::Rect root_bounds_;
};
} // namespace
std::unique_ptr<RootWindowTransformer> CreateRootWindowTransformerForDisplay(
const display::Display& display) {
return base::WrapUnique<RootWindowTransformer>(
new AshRootWindowTransformer(display));
}
std::unique_ptr<RootWindowTransformer>
CreateRootWindowTransformerForMirroredDisplay(
const display::ManagedDisplayInfo& source_display_info,
const display::ManagedDisplayInfo& mirror_display_info) {
return base::WrapUnique<RootWindowTransformer>(
new MirrorRootWindowTransformer(source_display_info,
mirror_display_info));
}
std::unique_ptr<RootWindowTransformer>
CreateRootWindowTransformerForUnifiedDesktop(const gfx::Rect& screen_bounds,
const display::Display& display) {
return base::WrapUnique<RootWindowTransformer>(
new PartialBoundsRootWindowTransformer(screen_bounds, display));
}
} // namespace ash