root_window_transformers.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
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/display/display_info.h"
10#include "ash/display/display_manager.h"
11#include "ash/magnifier/magnification_controller.h"
12#include "ash/shell.h"
13#include "base/basictypes.h"
14#include "base/memory/scoped_ptr.h"
15#include "third_party/skia/include/utils/SkMatrix44.h"
16#include "ui/aura/root_window.h"
17#include "ui/aura/root_window_transformer.h"
18#include "ui/aura/window_property.h"
19#include "ui/compositor/dip_util.h"
20#include "ui/gfx/display.h"
21#include "ui/gfx/insets.h"
22#include "ui/gfx/size_conversions.h"
23#include "ui/gfx/transform.h"
24#include "ui/gfx/transform.h"
25
26DECLARE_WINDOW_PROPERTY_TYPE(gfx::Display::Rotation);
27
28namespace ash {
29namespace internal {
30namespace {
31
32DEFINE_WINDOW_PROPERTY_KEY(gfx::Display::Rotation, kRotationPropertyKey,
33                           gfx::Display::ROTATE_0);
34
35// Round near zero value to zero.
36void RoundNearZero(gfx::Transform* transform) {
37  const float kEpsilon = 0.001f;
38  SkMatrix44& matrix = transform->matrix();
39  for (int x = 0; x < 4; ++x) {
40    for (int y = 0; y < 4; ++y) {
41      if (std::abs(SkMScalarToFloat(matrix.get(x, y))) < kEpsilon)
42        matrix.set(x, y, SkFloatToMScalar(0.0f));
43    }
44  }
45}
46
47// TODO(oshima): Transformers should be able to adjust itself
48// when the device scale factor is changed, instead of
49// precalculating the transform using fixed value.
50
51gfx::Transform CreateRotationTransform(aura::RootWindow* root_window,
52                                       const gfx::Display& display) {
53  DisplayInfo info =
54      Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id());
55
56  // TODO(oshima): Add animation. (crossfade+rotation, or just cross-fade)
57#if defined(OS_WIN)
58  // Windows 8 bots refused to resize the host window, and
59  // updating the transform results in incorrectly resizing
60  // the root window. Don't apply the transform unless
61  // necessary so that unit tests pass on win8 bots.
62  if (info.rotation() == root_window->GetProperty(kRotationPropertyKey))
63    return gfx::Transform();
64  root_window->SetProperty(kRotationPropertyKey, info.rotation());
65#endif
66
67  gfx::Transform rotate;
68  // The origin is (0, 0), so the translate width/height must be reduced by
69  // 1 pixel.
70  float one_pixel = 1.0f / display.device_scale_factor();
71  switch (info.rotation()) {
72    case gfx::Display::ROTATE_0:
73      break;
74    case gfx::Display::ROTATE_90:
75      rotate.Translate(display.bounds().height() - one_pixel, 0);
76      rotate.Rotate(90);
77      break;
78    case gfx::Display::ROTATE_270:
79      rotate.Translate(0, display.bounds().width() - one_pixel);
80      rotate.Rotate(270);
81      break;
82    case gfx::Display::ROTATE_180:
83      rotate.Translate(display.bounds().width() - one_pixel,
84                       display.bounds().height() - one_pixel);
85      rotate.Rotate(180);
86      break;
87  }
88
89  RoundNearZero(&rotate);
90  return rotate;
91}
92
93gfx::Transform CreateMagnifierTransform(aura::RootWindow* root_window) {
94  MagnificationController* magnifier =
95      Shell::GetInstance()->magnification_controller();
96  float magnifier_scale = 1.f;
97  gfx::Point magnifier_offset;
98  if (magnifier && magnifier->IsEnabled()) {
99    magnifier_scale = magnifier->GetScale();
100    magnifier_offset = magnifier->GetWindowPosition();
101  }
102  gfx::Transform transform;
103  if (magnifier_scale != 1.f) {
104    transform.Scale(magnifier_scale, magnifier_scale);
105    transform.Translate(-magnifier_offset.x(), -magnifier_offset.y());
106  }
107  return transform;
108}
109
110gfx::Transform CreateInsetsAndScaleTransform(const gfx::Insets& insets,
111                                             float device_scale_factor,
112                                             float ui_scale) {
113  gfx::Transform transform;
114  if (insets.top() != 0 || insets.left() != 0) {
115    float x_offset = insets.left() / device_scale_factor;
116    float y_offset = insets.top() / device_scale_factor;
117    transform.Translate(x_offset, y_offset);
118  }
119  float inverted_scale = 1.0f / ui_scale;
120  transform.Scale(inverted_scale, inverted_scale);
121  return transform;
122}
123
124gfx::Transform CreateOverscanAndUIScaleTransform(aura::RootWindow* root_window,
125                                                 const gfx::Display& display) {
126  DisplayInfo info =
127      Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id());
128  return CreateInsetsAndScaleTransform(
129      info.GetOverscanInsetsInPixel(),
130      ui::GetDeviceScaleFactor(root_window->layer()),
131      info.ui_scale());
132}
133
134// RootWindowTransformer for ash environment.
135class AshRootWindowTransformer : public aura::RootWindowTransformer {
136 public:
137  AshRootWindowTransformer(aura::RootWindow* root,
138                           const gfx::Display& display)
139      : root_window_(root) {
140    root_window_bounds_transform_ =
141        CreateOverscanAndUIScaleTransform(root, display) *
142        CreateRotationTransform(root, display);
143    transform_ = root_window_bounds_transform_ * CreateMagnifierTransform(root);
144    CHECK(transform_.GetInverse(&invert_transform_));
145
146    DisplayInfo info = Shell::GetInstance()->display_manager()->
147        GetDisplayInfo(display.id());
148    root_window_ui_scale_ = info.ui_scale();
149    host_insets_ = info.GetOverscanInsetsInPixel();
150  }
151
152  // aura::RootWindowTransformer overrides:
153  virtual gfx::Transform GetTransform() const OVERRIDE {
154    return transform_;
155  }
156  virtual gfx::Transform GetInverseTransform() const OVERRIDE {
157    return invert_transform_;
158  }
159  virtual gfx::Rect GetRootWindowBounds(
160      const gfx::Size& host_size) const OVERRIDE {
161    gfx::Rect bounds(host_size);
162    bounds.Inset(host_insets_);
163    bounds = ui::ConvertRectToDIP(root_window_->layer(), bounds);
164    gfx::RectF new_bounds(bounds);
165    root_window_bounds_transform_.TransformRect(&new_bounds);
166    // Apply |root_window_scale_| twice as the downscaling
167    // is already applied once in |SetTransformInternal()|.
168    // TODO(oshima): This is a bit ugly. Consider specifying
169    // the pseudo host resolution instead.
170    new_bounds.Scale(root_window_ui_scale_ * root_window_ui_scale_);
171    // Ignore the origin because RootWindow's insets are handled by
172    // the transform.
173    // Floor the size because the bounds is no longer aligned to
174    // backing pixel when |root_window_scale_| is specified
175    // (850 height at 1.25 scale becomes 1062.5 for example.)
176    return gfx::Rect(gfx::ToFlooredSize(new_bounds.size()));
177  }
178
179  virtual gfx::Insets GetHostInsets() const OVERRIDE {
180    return host_insets_;
181  }
182
183 private:
184  virtual ~AshRootWindowTransformer() {}
185
186  aura::RootWindow* root_window_;
187  gfx::Transform transform_;
188
189  // The accurate representation of the inverse of the |transform_|.
190  // This is used to avoid computation error caused by
191  // |gfx::Transform::GetInverse|.
192  gfx::Transform invert_transform_;
193
194  // The transform of the root window bounds. This is used to calculate
195  // the size of root window.
196  gfx::Transform root_window_bounds_transform_;
197
198  // The scale of the root window. See |display_info::ui_scale_|
199  // for more info.
200  float root_window_ui_scale_;
201
202  gfx::Insets host_insets_;
203
204  DISALLOW_COPY_AND_ASSIGN(AshRootWindowTransformer);
205};
206
207// RootWindowTransformer for mirror root window. We simply copy the
208// texture (bitmap) of the source display into the mirror window, so
209// the root window bounds is the same as the source display's
210// pixel size (excluding overscan insets).
211class MirrorRootWindowTransformer : public aura::RootWindowTransformer {
212 public:
213  MirrorRootWindowTransformer(const DisplayInfo& source_display_info,
214                              const DisplayInfo& mirror_display_info) {
215    root_bounds_ = gfx::Rect(source_display_info.bounds_in_native().size());
216    gfx::Rect mirror_display_rect =
217        gfx::Rect(mirror_display_info.bounds_in_native().size());
218
219    bool letterbox = root_bounds_.width() * mirror_display_rect.height() >
220        root_bounds_.height() * mirror_display_rect.width();
221    if (letterbox) {
222      float mirror_scale_ratio =
223          (static_cast<float>(root_bounds_.width()) /
224           static_cast<float>(mirror_display_rect.width()));
225      float inverted_scale = 1.0f / mirror_scale_ratio;
226      int margin = static_cast<int>(
227          (mirror_display_rect.height() -
228           root_bounds_.height() * inverted_scale) / 2);
229      insets_.Set(0, margin, 0, margin);
230
231      transform_.Translate(0,  margin);
232      transform_.Scale(inverted_scale, inverted_scale);
233    } else {
234      float mirror_scale_ratio =
235          (static_cast<float>(root_bounds_.height()) /
236           static_cast<float>(mirror_display_rect.height()));
237      float inverted_scale = 1.0f / mirror_scale_ratio;
238      int margin = static_cast<int>(
239          (mirror_display_rect.width() -
240           root_bounds_.width() * inverted_scale) / 2);
241      insets_.Set(margin, 0, margin, 0);
242
243      transform_.Translate(margin, 0);
244      transform_.Scale(inverted_scale, inverted_scale);
245    }
246  }
247
248  // aura::RootWindowTransformer overrides:
249  virtual gfx::Transform GetTransform() const OVERRIDE {
250    return transform_;
251  }
252  virtual gfx::Transform GetInverseTransform() const OVERRIDE {
253    gfx::Transform invert;
254    CHECK(transform_.GetInverse(&invert));
255    return invert;
256  }
257  virtual gfx::Rect GetRootWindowBounds(
258      const gfx::Size& host_size) const OVERRIDE {
259    return root_bounds_;
260  }
261  virtual gfx::Insets GetHostInsets() const OVERRIDE {
262    return insets_;
263  }
264
265 private:
266  virtual ~MirrorRootWindowTransformer() {}
267
268  gfx::Transform transform_;
269  gfx::Rect root_bounds_;
270  gfx::Insets insets_;
271
272  DISALLOW_COPY_AND_ASSIGN(MirrorRootWindowTransformer);
273};
274
275}  // namespace
276
277aura::RootWindowTransformer* CreateRootWindowTransformerForDisplay(
278    aura::RootWindow* root,
279    const gfx::Display& display) {
280  return new AshRootWindowTransformer(root, display);
281}
282
283aura::RootWindowTransformer* CreateRootWindowTransformerForMirroredDisplay(
284    const DisplayInfo& source_display_info,
285    const DisplayInfo& mirror_display_info) {
286  return new MirrorRootWindowTransformer(source_display_info,
287                                         mirror_display_info);
288}
289
290}  // namespace internal
291}  // namespace ash
292