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/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" 25 26 DECLARE_WINDOW_PROPERTY_TYPE(gfx::Display::Rotation); 27 28 namespace ash { 29 namespace { 30 31 #if defined(OS_WIN) 32 DEFINE_WINDOW_PROPERTY_KEY(gfx::Display::Rotation, kRotationPropertyKey, 33 gfx::Display::ROTATE_0); 34 #endif 35 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)); 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 52 gfx::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 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(); 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 111 gfx::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. 126 class AshRootWindowTransformer : public 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 // aura::RootWindowTransformer overrides: 145 virtual gfx::Transform GetTransform() const OVERRIDE { 146 return transform_; 147 } 148 virtual gfx::Transform GetInverseTransform() const OVERRIDE { 149 return invert_transform_; 150 } 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 164 // the transform. 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())); 169 } 170 171 virtual gfx::Insets GetHostInsets() const OVERRIDE { 172 return host_insets_; 173 } 174 175 private: 176 virtual ~AshRootWindowTransformer() {} 177 178 aura::Window* root_window_; 179 gfx::Transform transform_; 180 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_; 185 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_; 189 190 // The scale of the root window. See |display_info::ui_scale_| 191 // for more info. 192 float root_window_ui_scale_; 193 194 gfx::Insets host_insets_; 195 196 DISALLOW_COPY_AND_ASSIGN(AshRootWindowTransformer); 197 }; 198 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 { 204 public: 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()); 210 211 bool letterbox = root_bounds_.width() * mirror_display_rect.height() > 212 root_bounds_.height() * mirror_display_rect.width(); 213 if (letterbox) { 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); 222 223 transform_.Translate(0, margin); 224 transform_.Scale(inverted_scale, inverted_scale); 225 } else { 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); 234 235 transform_.Translate(margin, 0); 236 transform_.Scale(inverted_scale, inverted_scale); 237 } 238 } 239 240 // aura::RootWindowTransformer overrides: 241 virtual gfx::Transform GetTransform() const OVERRIDE { 242 return transform_; 243 } 244 virtual gfx::Transform GetInverseTransform() const OVERRIDE { 245 gfx::Transform invert; 246 CHECK(transform_.GetInverse(&invert)); 247 return invert; 248 } 249 virtual gfx::Rect GetRootWindowBounds( 250 const gfx::Size& host_size) const OVERRIDE { 251 return root_bounds_; 252 } 253 virtual gfx::Insets GetHostInsets() const OVERRIDE { 254 return insets_; 255 } 256 257 private: 258 virtual ~MirrorRootWindowTransformer() {} 259 260 gfx::Transform transform_; 261 gfx::Rect root_bounds_; 262 gfx::Insets insets_; 263 264 DISALLOW_COPY_AND_ASSIGN(MirrorRootWindowTransformer); 265 }; 266 267 } // namespace 268 269 RootWindowTransformer* CreateRootWindowTransformerForDisplay( 270 aura::Window* root, 271 const gfx::Display& display) { 272 return new AshRootWindowTransformer(root, display); 273 } 274 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); 280 } 281 282 } // namespace ash 283