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