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.
5 #include "ash/display/root_window_transformers.h"
9 #include "ash/ash_switches.h"
10 #include "ash/display/display_info.h"
11 #include "ash/display/display_manager.h"
12 #include "ash/host/root_window_transformer.h"
13 #include "ash/magnifier/magnification_controller.h"
14 #include "ash/shell.h"
15 #include "base/basictypes.h"
16 #include "base/command_line.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "third_party/skia/include/utils/SkMatrix44.h"
19 #include "ui/aura/window_event_dispatcher.h"
20 #include "ui/aura/window_property.h"
21 #include "ui/compositor/dip_util.h"
22 #include "ui/gfx/display.h"
23 #include "ui/gfx/geometry/insets.h"
24 #include "ui/gfx/geometry/size_conversions.h"
25 #include "ui/gfx/transform.h"
26 #include "ui/gfx/transform.h"
28 DECLARE_WINDOW_PROPERTY_TYPE(gfx::Display::Rotation
);
34 DEFINE_WINDOW_PROPERTY_KEY(gfx::Display::Rotation
, kRotationPropertyKey
,
35 gfx::Display::ROTATE_0
);
38 // Round near zero value to zero.
39 void RoundNearZero(gfx::Transform
* transform
) {
40 const float kEpsilon
= 0.001f
;
41 SkMatrix44
& matrix
= transform
->matrix();
42 for (int x
= 0; x
< 4; ++x
) {
43 for (int y
= 0; y
< 4; ++y
) {
44 if (std::abs(SkMScalarToFloat(matrix
.get(x
, y
))) < kEpsilon
)
45 matrix
.set(x
, y
, SkFloatToMScalar(0.0f
));
50 // TODO(oshima): Transformers should be able to adjust itself
51 // when the device scale factor is changed, instead of
52 // precalculating the transform using fixed value.
54 gfx::Transform
CreateRotationTransform(aura::Window
* root_window
,
55 const gfx::Display
& display
) {
57 Shell::GetInstance()->display_manager()->GetDisplayInfo(display
.id());
59 // TODO(oshima): Add animation. (crossfade+rotation, or just cross-fade)
61 // Windows 8 bots refused to resize the host window, and
62 // updating the transform results in incorrectly resizing
63 // the root window. Don't apply the transform unless
64 // necessary so that unit tests pass on win8 bots.
65 if (info
.GetActiveRotation() ==
66 root_window
->GetProperty(kRotationPropertyKey
)) {
67 return gfx::Transform();
69 root_window
->SetProperty(kRotationPropertyKey
, info
.GetActiveRotation());
72 gfx::Transform rotate
;
73 // The origin is (0, 0), so the translate width/height must be reduced by
75 float one_pixel
= 1.0f
/ display
.device_scale_factor();
76 switch (info
.GetActiveRotation()) {
77 case gfx::Display::ROTATE_0
:
79 case gfx::Display::ROTATE_90
:
80 rotate
.Translate(display
.bounds().height() - one_pixel
, 0);
83 case gfx::Display::ROTATE_270
:
84 rotate
.Translate(0, display
.bounds().width() - one_pixel
);
87 case gfx::Display::ROTATE_180
:
88 rotate
.Translate(display
.bounds().width() - one_pixel
,
89 display
.bounds().height() - one_pixel
);
94 RoundNearZero(&rotate
);
98 gfx::Transform
CreateMagnifierTransform(aura::Window
* root_window
) {
99 MagnificationController
* magnifier
=
100 Shell::GetInstance()->magnification_controller();
101 float magnifier_scale
= 1.f
;
102 gfx::Point magnifier_offset
;
103 if (magnifier
&& magnifier
->IsEnabled()) {
104 magnifier_scale
= magnifier
->GetScale();
105 magnifier_offset
= magnifier
->GetWindowPosition();
107 gfx::Transform transform
;
108 if (magnifier_scale
!= 1.f
) {
109 transform
.Scale(magnifier_scale
, magnifier_scale
);
110 transform
.Translate(-magnifier_offset
.x(), -magnifier_offset
.y());
115 gfx::Transform
CreateInsetsAndScaleTransform(const gfx::Insets
& insets
,
116 float device_scale_factor
,
118 gfx::Transform transform
;
119 if (insets
.top() != 0 || insets
.left() != 0) {
120 float x_offset
= insets
.left() / device_scale_factor
;
121 float y_offset
= insets
.top() / device_scale_factor
;
122 transform
.Translate(x_offset
, y_offset
);
124 float inverted_scale
= 1.0f
/ ui_scale
;
125 transform
.Scale(inverted_scale
, inverted_scale
);
129 gfx::Transform
CreateMirrorTransform(const gfx::Display
& display
) {
130 gfx::Transform transform
;
131 transform
.matrix().set3x3(-1, 0, 0,
134 transform
.Translate(-display
.size().width(), 0);
138 // RootWindowTransformer for ash environment.
139 class AshRootWindowTransformer
: public RootWindowTransformer
{
141 AshRootWindowTransformer(aura::Window
* root
,
142 const gfx::Display
& display
)
143 : root_window_(root
) {
144 DisplayManager
* display_manager
= Shell::GetInstance()->display_manager();
145 DisplayInfo info
= display_manager
->GetDisplayInfo(display
.id());
146 host_insets_
= info
.GetOverscanInsetsInPixel();
147 root_window_ui_scale_
= info
.GetEffectiveUIScale();
148 // In unified mode, the scaling happen when mirroring, so don't apply
149 // scaling to the root window.
150 const float apply_ui_scale
=
151 display_manager
->IsInUnifiedMode() ? 1.0f
: root_window_ui_scale_
;
153 root_window_bounds_transform_
=
154 CreateInsetsAndScaleTransform(
155 host_insets_
, display
.device_scale_factor(), apply_ui_scale
) *
156 CreateRotationTransform(root
, display
);
157 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
158 switches::kAshEnableMirroredScreen
)) {
159 // Apply the tranform that flips the screen image horizontally so that
160 // the screen looks normal when reflected on a mirror.
161 root_window_bounds_transform_
=
162 root_window_bounds_transform_
* CreateMirrorTransform(display
);
164 transform_
= root_window_bounds_transform_
* CreateMagnifierTransform(root
);
166 CHECK(transform_
.GetInverse(&invert_transform_
));
169 // aura::RootWindowTransformer overrides:
170 gfx::Transform
GetTransform() const override
{ return transform_
; }
171 gfx::Transform
GetInverseTransform() const override
{
172 return invert_transform_
;
174 gfx::Rect
GetRootWindowBounds(const gfx::Size
& host_size
) const override
{
175 gfx::Rect
bounds(host_size
);
176 bounds
.Inset(host_insets_
);
177 bounds
= ui::ConvertRectToDIP(root_window_
->layer(), bounds
);
178 gfx::RectF
new_bounds(bounds
);
179 root_window_bounds_transform_
.TransformRect(&new_bounds
);
180 if (Shell::GetInstance()->display_manager()->IsInUnifiedMode()) {
181 new_bounds
.Scale(root_window_ui_scale_
);
183 // Apply |root_window_scale_| twice as the downscaling
184 // is already applied once in |SetTransformInternal()|.
185 // TODO(oshima): This is a bit ugly. Consider specifying
186 // the pseudo host resolution instead.
187 new_bounds
.Scale(root_window_ui_scale_
* root_window_ui_scale_
);
189 // Ignore the origin because RootWindow's insets are handled by
191 // Floor the size because the bounds is no longer aligned to
192 // backing pixel when |root_window_scale_| is specified
193 // (850 height at 1.25 scale becomes 1062.5 for example.)
194 return gfx::Rect(gfx::ToFlooredSize(new_bounds
.size()));
197 gfx::Insets
GetHostInsets() const override
{ return host_insets_
; }
200 ~AshRootWindowTransformer() override
{}
202 aura::Window
* root_window_
;
203 gfx::Transform transform_
;
205 // The accurate representation of the inverse of the |transform_|.
206 // This is used to avoid computation error caused by
207 // |gfx::Transform::GetInverse|.
208 gfx::Transform invert_transform_
;
210 // The transform of the root window bounds. This is used to calculate
211 // the size of root window.
212 gfx::Transform root_window_bounds_transform_
;
214 // The scale of the root window. See |display_info::ui_scale_|
216 float root_window_ui_scale_
;
218 gfx::Insets host_insets_
;
220 DISALLOW_COPY_AND_ASSIGN(AshRootWindowTransformer
);
223 // RootWindowTransformer for mirror root window. We simply copy the
224 // texture (bitmap) of the source display into the mirror window, so
225 // the root window bounds is the same as the source display's
226 // pixel size (excluding overscan insets).
227 class MirrorRootWindowTransformer
: public RootWindowTransformer
{
229 MirrorRootWindowTransformer(const DisplayInfo
& source_display_info
,
230 const DisplayInfo
& mirror_display_info
) {
231 root_bounds_
= gfx::Rect(source_display_info
.bounds_in_native().size());
232 gfx::Rect mirror_display_rect
=
233 gfx::Rect(mirror_display_info
.bounds_in_native().size());
235 bool letterbox
= root_bounds_
.width() * mirror_display_rect
.height() >
236 root_bounds_
.height() * mirror_display_rect
.width();
238 float mirror_scale_ratio
=
239 (static_cast<float>(root_bounds_
.width()) /
240 static_cast<float>(mirror_display_rect
.width()));
241 float inverted_scale
= 1.0f
/ mirror_scale_ratio
;
242 int margin
= static_cast<int>(
243 (mirror_display_rect
.height() -
244 root_bounds_
.height() * inverted_scale
) / 2);
245 insets_
.Set(0, margin
, 0, margin
);
247 transform_
.Translate(0, margin
);
248 transform_
.Scale(inverted_scale
, inverted_scale
);
250 float mirror_scale_ratio
=
251 (static_cast<float>(root_bounds_
.height()) /
252 static_cast<float>(mirror_display_rect
.height()));
253 float inverted_scale
= 1.0f
/ mirror_scale_ratio
;
254 int margin
= static_cast<int>(
255 (mirror_display_rect
.width() -
256 root_bounds_
.width() * inverted_scale
) / 2);
257 insets_
.Set(margin
, 0, margin
, 0);
259 transform_
.Translate(margin
, 0);
260 transform_
.Scale(inverted_scale
, inverted_scale
);
264 // aura::RootWindowTransformer overrides:
265 gfx::Transform
GetTransform() const override
{ return transform_
; }
266 gfx::Transform
GetInverseTransform() const override
{
267 gfx::Transform invert
;
268 CHECK(transform_
.GetInverse(&invert
));
271 gfx::Rect
GetRootWindowBounds(const gfx::Size
& host_size
) const override
{
274 gfx::Insets
GetHostInsets() const override
{ return insets_
; }
277 ~MirrorRootWindowTransformer() override
{}
279 gfx::Transform transform_
;
280 gfx::Rect root_bounds_
;
283 DISALLOW_COPY_AND_ASSIGN(MirrorRootWindowTransformer
);
286 class PartialBoundsRootWindowTransformer
: public RootWindowTransformer
{
288 PartialBoundsRootWindowTransformer(const gfx::Rect
& screen_bounds
,
289 const gfx::Display
& display
) {
290 DisplayInfo display_info
=
291 Shell::GetInstance()->display_manager()->GetDisplayInfo(display
.id());
292 root_bounds_
= gfx::Rect(display_info
.bounds_in_native().size());
293 transform_
.Translate(-SkIntToMScalar(display
.bounds().x()),
294 -SkIntToMScalar(display
.bounds().y()));
296 root_bounds_
.height() / static_cast<float>(screen_bounds
.height());
297 transform_
.Scale(scale
, scale
);
300 // RootWindowTransformer:
301 gfx::Transform
GetTransform() const override
{ return transform_
; }
302 gfx::Transform
GetInverseTransform() const override
{
303 gfx::Transform invert
;
304 CHECK(transform_
.GetInverse(&invert
));
307 gfx::Rect
GetRootWindowBounds(const gfx::Size
& host_size
) const override
{
310 gfx::Insets
GetHostInsets() const override
{ return gfx::Insets(); }
313 gfx::Transform transform_
;
314 gfx::Rect root_bounds_
;
316 DISALLOW_COPY_AND_ASSIGN(PartialBoundsRootWindowTransformer
);
321 RootWindowTransformer
* CreateRootWindowTransformerForDisplay(
323 const gfx::Display
& display
) {
324 return new AshRootWindowTransformer(root
, display
);
327 RootWindowTransformer
* CreateRootWindowTransformerForMirroredDisplay(
328 const DisplayInfo
& source_display_info
,
329 const DisplayInfo
& mirror_display_info
) {
330 return new MirrorRootWindowTransformer(source_display_info
,
331 mirror_display_info
);
334 RootWindowTransformer
* CreateRootWindowTransformerForUnifiedDesktop(
335 const gfx::Rect
& screen_bounds
,
336 const gfx::Display
& display
) {
337 return new PartialBoundsRootWindowTransformer(screen_bounds
, display
);