• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
26 DECLARE_WINDOW_PROPERTY_TYPE(gfx::Display::Rotation);
27 
28 namespace ash {
29 namespace internal {
30 namespace {
31 
32 #if defined(OS_WIN)
33 DEFINE_WINDOW_PROPERTY_KEY(gfx::Display::Rotation, kRotationPropertyKey,
34                            gfx::Display::ROTATE_0);
35 #endif
36 
37 // Round near zero value to zero.
RoundNearZero(gfx::Transform * transform)38 void 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 
CreateRotationTransform(aura::Window * root_window,const gfx::Display & display)53 gfx::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 
CreateMagnifierTransform(aura::Window * root_window)95 gfx::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 
CreateInsetsAndScaleTransform(const gfx::Insets & insets,float device_scale_factor,float ui_scale)112 gfx::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.
127 class AshRootWindowTransformer : public aura::RootWindowTransformer {
128  public:
AshRootWindowTransformer(aura::Window * root,const gfx::Display & display)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:
GetTransform() const147   virtual gfx::Transform GetTransform() const OVERRIDE {
148     return transform_;
149   }
GetInverseTransform() const150   virtual gfx::Transform GetInverseTransform() const OVERRIDE {
151     return invert_transform_;
152   }
GetRootWindowBounds(const gfx::Size & host_size) const153   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 
GetHostInsets() const173   virtual gfx::Insets GetHostInsets() const OVERRIDE {
174     return host_insets_;
175   }
176 
177  private:
~AshRootWindowTransformer()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).
205 class MirrorRootWindowTransformer : public aura::RootWindowTransformer {
206  public:
MirrorRootWindowTransformer(const DisplayInfo & source_display_info,const DisplayInfo & mirror_display_info)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:
GetTransform() const243   virtual gfx::Transform GetTransform() const OVERRIDE {
244     return transform_;
245   }
GetInverseTransform() const246   virtual gfx::Transform GetInverseTransform() const OVERRIDE {
247     gfx::Transform invert;
248     CHECK(transform_.GetInverse(&invert));
249     return invert;
250   }
GetRootWindowBounds(const gfx::Size & host_size) const251   virtual gfx::Rect GetRootWindowBounds(
252       const gfx::Size& host_size) const OVERRIDE {
253     return root_bounds_;
254   }
GetHostInsets() const255   virtual gfx::Insets GetHostInsets() const OVERRIDE {
256     return insets_;
257   }
258 
259  private:
~MirrorRootWindowTransformer()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 
CreateRootWindowTransformerForDisplay(aura::Window * root,const gfx::Display & display)271 aura::RootWindowTransformer* CreateRootWindowTransformerForDisplay(
272     aura::Window* root,
273     const gfx::Display& display) {
274   return new AshRootWindowTransformer(root, display);
275 }
276 
CreateRootWindowTransformerForMirroredDisplay(const DisplayInfo & source_display_info,const DisplayInfo & mirror_display_info)277 aura::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