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