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