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/display/display_info.h"
10 #include "ash/display/display_manager.h"
11 #include "ash/host/root_window_transformer.h"
12 #include "ash/magnifier/magnification_controller.h"
13 #include "ash/shell.h"
14 #include "base/basictypes.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "third_party/skia/include/utils/SkMatrix44.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"
26 DECLARE_WINDOW_PROPERTY_TYPE(gfx::Display::Rotation
);
32 DEFINE_WINDOW_PROPERTY_KEY(gfx::Display::Rotation
, kRotationPropertyKey
,
33 gfx::Display::ROTATE_0
);
36 // Round near zero value to zero.
37 void 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
));
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.
52 gfx::Transform
CreateRotationTransform(aura::Window
* root_window
,
53 const gfx::Display
& display
) {
55 Shell::GetInstance()->display_manager()->GetDisplayInfo(display
.id());
57 // TODO(oshima): Add animation. (crossfade+rotation, or just cross-fade)
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());
68 gfx::Transform rotate
;
69 // The origin is (0, 0), so the translate width/height must be reduced by
71 float one_pixel
= 1.0f
/ display
.device_scale_factor();
72 switch (info
.rotation()) {
73 case gfx::Display::ROTATE_0
:
75 case gfx::Display::ROTATE_90
:
76 rotate
.Translate(display
.bounds().height() - one_pixel
, 0);
79 case gfx::Display::ROTATE_270
:
80 rotate
.Translate(0, display
.bounds().width() - one_pixel
);
83 case gfx::Display::ROTATE_180
:
84 rotate
.Translate(display
.bounds().width() - one_pixel
,
85 display
.bounds().height() - one_pixel
);
90 RoundNearZero(&rotate
);
94 gfx::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();
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());
111 gfx::Transform
CreateInsetsAndScaleTransform(const gfx::Insets
& insets
,
112 float device_scale_factor
,
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
);
120 float inverted_scale
= 1.0f
/ ui_scale
;
121 transform
.Scale(inverted_scale
, inverted_scale
);
125 // RootWindowTransformer for ash environment.
126 class AshRootWindowTransformer
: public RootWindowTransformer
{
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_
));
144 // aura::RootWindowTransformer overrides:
145 virtual gfx::Transform
GetTransform() const OVERRIDE
{
148 virtual gfx::Transform
GetInverseTransform() const OVERRIDE
{
149 return invert_transform_
;
151 virtual gfx::Rect
GetRootWindowBounds(
152 const gfx::Size
& host_size
) const OVERRIDE
{
153 gfx::Rect
bounds(host_size
);
154 bounds
.Inset(host_insets_
);
155 bounds
= ui::ConvertRectToDIP(root_window_
->layer(), bounds
);
156 gfx::RectF
new_bounds(bounds
);
157 root_window_bounds_transform_
.TransformRect(&new_bounds
);
158 // Apply |root_window_scale_| twice as the downscaling
159 // is already applied once in |SetTransformInternal()|.
160 // TODO(oshima): This is a bit ugly. Consider specifying
161 // the pseudo host resolution instead.
162 new_bounds
.Scale(root_window_ui_scale_
* root_window_ui_scale_
);
163 // Ignore the origin because RootWindow's insets are handled by
165 // Floor the size because the bounds is no longer aligned to
166 // backing pixel when |root_window_scale_| is specified
167 // (850 height at 1.25 scale becomes 1062.5 for example.)
168 return gfx::Rect(gfx::ToFlooredSize(new_bounds
.size()));
171 virtual gfx::Insets
GetHostInsets() const OVERRIDE
{
176 virtual ~AshRootWindowTransformer() {}
178 aura::Window
* root_window_
;
179 gfx::Transform transform_
;
181 // The accurate representation of the inverse of the |transform_|.
182 // This is used to avoid computation error caused by
183 // |gfx::Transform::GetInverse|.
184 gfx::Transform invert_transform_
;
186 // The transform of the root window bounds. This is used to calculate
187 // the size of root window.
188 gfx::Transform root_window_bounds_transform_
;
190 // The scale of the root window. See |display_info::ui_scale_|
192 float root_window_ui_scale_
;
194 gfx::Insets host_insets_
;
196 DISALLOW_COPY_AND_ASSIGN(AshRootWindowTransformer
);
199 // RootWindowTransformer for mirror root window. We simply copy the
200 // texture (bitmap) of the source display into the mirror window, so
201 // the root window bounds is the same as the source display's
202 // pixel size (excluding overscan insets).
203 class MirrorRootWindowTransformer
: public RootWindowTransformer
{
205 MirrorRootWindowTransformer(const DisplayInfo
& source_display_info
,
206 const DisplayInfo
& mirror_display_info
) {
207 root_bounds_
= gfx::Rect(source_display_info
.bounds_in_native().size());
208 gfx::Rect mirror_display_rect
=
209 gfx::Rect(mirror_display_info
.bounds_in_native().size());
211 bool letterbox
= root_bounds_
.width() * mirror_display_rect
.height() >
212 root_bounds_
.height() * mirror_display_rect
.width();
214 float mirror_scale_ratio
=
215 (static_cast<float>(root_bounds_
.width()) /
216 static_cast<float>(mirror_display_rect
.width()));
217 float inverted_scale
= 1.0f
/ mirror_scale_ratio
;
218 int margin
= static_cast<int>(
219 (mirror_display_rect
.height() -
220 root_bounds_
.height() * inverted_scale
) / 2);
221 insets_
.Set(0, margin
, 0, margin
);
223 transform_
.Translate(0, margin
);
224 transform_
.Scale(inverted_scale
, inverted_scale
);
226 float mirror_scale_ratio
=
227 (static_cast<float>(root_bounds_
.height()) /
228 static_cast<float>(mirror_display_rect
.height()));
229 float inverted_scale
= 1.0f
/ mirror_scale_ratio
;
230 int margin
= static_cast<int>(
231 (mirror_display_rect
.width() -
232 root_bounds_
.width() * inverted_scale
) / 2);
233 insets_
.Set(margin
, 0, margin
, 0);
235 transform_
.Translate(margin
, 0);
236 transform_
.Scale(inverted_scale
, inverted_scale
);
240 // aura::RootWindowTransformer overrides:
241 virtual gfx::Transform
GetTransform() const OVERRIDE
{
244 virtual gfx::Transform
GetInverseTransform() const OVERRIDE
{
245 gfx::Transform invert
;
246 CHECK(transform_
.GetInverse(&invert
));
249 virtual gfx::Rect
GetRootWindowBounds(
250 const gfx::Size
& host_size
) const OVERRIDE
{
253 virtual gfx::Insets
GetHostInsets() const OVERRIDE
{
258 virtual ~MirrorRootWindowTransformer() {}
260 gfx::Transform transform_
;
261 gfx::Rect root_bounds_
;
264 DISALLOW_COPY_AND_ASSIGN(MirrorRootWindowTransformer
);
269 RootWindowTransformer
* CreateRootWindowTransformerForDisplay(
271 const gfx::Display
& display
) {
272 return new AshRootWindowTransformer(root
, display
);
275 RootWindowTransformer
* CreateRootWindowTransformerForMirroredDisplay(
276 const DisplayInfo
& source_display_info
,
277 const DisplayInfo
& mirror_display_info
) {
278 return new MirrorRootWindowTransformer(source_display_info
,
279 mirror_display_info
);