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. 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 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 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 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: 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). 205 class 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 271 aura::RootWindowTransformer* CreateRootWindowTransformerForDisplay( 272 aura::Window* root, 273 const gfx::Display& display) { 274 return new AshRootWindowTransformer(root, display); 275 } 276 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