root_window_transformers.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
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 26DECLARE_WINDOW_PROPERTY_TYPE(gfx::Display::Rotation); 27 28namespace ash { 29namespace internal { 30namespace { 31 32DEFINE_WINDOW_PROPERTY_KEY(gfx::Display::Rotation, kRotationPropertyKey, 33 gfx::Display::ROTATE_0); 34 35// Round near zero value to zero. 36void RoundNearZero(gfx::Transform* transform) { 37 const float kEpsilon = 0.001f; 38 SkMatrix44& matrix = transform->matrix(); 39 for (int x = 0; x < 4; ++x) { 40 for (int y = 0; y < 4; ++y) { 41 if (std::abs(SkMScalarToFloat(matrix.get(x, y))) < kEpsilon) 42 matrix.set(x, y, SkFloatToMScalar(0.0f)); 43 } 44 } 45} 46 47// TODO(oshima): Transformers should be able to adjust itself 48// when the device scale factor is changed, instead of 49// precalculating the transform using fixed value. 50 51gfx::Transform CreateRotationTransform(aura::RootWindow* root_window, 52 const gfx::Display& display) { 53 DisplayInfo info = 54 Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id()); 55 56 // TODO(oshima): Add animation. (crossfade+rotation, or just cross-fade) 57#if defined(OS_WIN) 58 // Windows 8 bots refused to resize the host window, and 59 // updating the transform results in incorrectly resizing 60 // the root window. Don't apply the transform unless 61 // necessary so that unit tests pass on win8 bots. 62 if (info.rotation() == root_window->GetProperty(kRotationPropertyKey)) 63 return gfx::Transform(); 64 root_window->SetProperty(kRotationPropertyKey, info.rotation()); 65#endif 66 67 gfx::Transform rotate; 68 // The origin is (0, 0), so the translate width/height must be reduced by 69 // 1 pixel. 70 float one_pixel = 1.0f / display.device_scale_factor(); 71 switch (info.rotation()) { 72 case gfx::Display::ROTATE_0: 73 break; 74 case gfx::Display::ROTATE_90: 75 rotate.Translate(display.bounds().height() - one_pixel, 0); 76 rotate.Rotate(90); 77 break; 78 case gfx::Display::ROTATE_270: 79 rotate.Translate(0, display.bounds().width() - one_pixel); 80 rotate.Rotate(270); 81 break; 82 case gfx::Display::ROTATE_180: 83 rotate.Translate(display.bounds().width() - one_pixel, 84 display.bounds().height() - one_pixel); 85 rotate.Rotate(180); 86 break; 87 } 88 89 RoundNearZero(&rotate); 90 return rotate; 91} 92 93gfx::Transform CreateMagnifierTransform(aura::RootWindow* root_window) { 94 MagnificationController* magnifier = 95 Shell::GetInstance()->magnification_controller(); 96 float magnifier_scale = 1.f; 97 gfx::Point magnifier_offset; 98 if (magnifier && magnifier->IsEnabled()) { 99 magnifier_scale = magnifier->GetScale(); 100 magnifier_offset = magnifier->GetWindowPosition(); 101 } 102 gfx::Transform transform; 103 if (magnifier_scale != 1.f) { 104 transform.Scale(magnifier_scale, magnifier_scale); 105 transform.Translate(-magnifier_offset.x(), -magnifier_offset.y()); 106 } 107 return transform; 108} 109 110gfx::Transform CreateInsetsAndScaleTransform(const gfx::Insets& insets, 111 float device_scale_factor, 112 float ui_scale) { 113 gfx::Transform transform; 114 if (insets.top() != 0 || insets.left() != 0) { 115 float x_offset = insets.left() / device_scale_factor; 116 float y_offset = insets.top() / device_scale_factor; 117 transform.Translate(x_offset, y_offset); 118 } 119 float inverted_scale = 1.0f / ui_scale; 120 transform.Scale(inverted_scale, inverted_scale); 121 return transform; 122} 123 124gfx::Transform CreateOverscanAndUIScaleTransform(aura::RootWindow* root_window, 125 const gfx::Display& display) { 126 DisplayInfo info = 127 Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id()); 128 return CreateInsetsAndScaleTransform( 129 info.GetOverscanInsetsInPixel(), 130 ui::GetDeviceScaleFactor(root_window->layer()), 131 info.ui_scale()); 132} 133 134// RootWindowTransformer for ash environment. 135class AshRootWindowTransformer : public aura::RootWindowTransformer { 136 public: 137 AshRootWindowTransformer(aura::RootWindow* root, 138 const gfx::Display& display) 139 : root_window_(root) { 140 root_window_bounds_transform_ = 141 CreateOverscanAndUIScaleTransform(root, display) * 142 CreateRotationTransform(root, display); 143 transform_ = root_window_bounds_transform_ * CreateMagnifierTransform(root); 144 CHECK(transform_.GetInverse(&invert_transform_)); 145 146 DisplayInfo info = Shell::GetInstance()->display_manager()-> 147 GetDisplayInfo(display.id()); 148 root_window_ui_scale_ = info.ui_scale(); 149 host_insets_ = info.GetOverscanInsetsInPixel(); 150 } 151 152 // aura::RootWindowTransformer overrides: 153 virtual gfx::Transform GetTransform() const OVERRIDE { 154 return transform_; 155 } 156 virtual gfx::Transform GetInverseTransform() const OVERRIDE { 157 return invert_transform_; 158 } 159 virtual gfx::Rect GetRootWindowBounds( 160 const gfx::Size& host_size) const OVERRIDE { 161 gfx::Rect bounds(host_size); 162 bounds.Inset(host_insets_); 163 bounds = ui::ConvertRectToDIP(root_window_->layer(), bounds); 164 gfx::RectF new_bounds(bounds); 165 root_window_bounds_transform_.TransformRect(&new_bounds); 166 // Apply |root_window_scale_| twice as the downscaling 167 // is already applied once in |SetTransformInternal()|. 168 // TODO(oshima): This is a bit ugly. Consider specifying 169 // the pseudo host resolution instead. 170 new_bounds.Scale(root_window_ui_scale_ * root_window_ui_scale_); 171 // Ignore the origin because RootWindow's insets are handled by 172 // the transform. 173 // Floor the size because the bounds is no longer aligned to 174 // backing pixel when |root_window_scale_| is specified 175 // (850 height at 1.25 scale becomes 1062.5 for example.) 176 return gfx::Rect(gfx::ToFlooredSize(new_bounds.size())); 177 } 178 179 virtual gfx::Insets GetHostInsets() const OVERRIDE { 180 return host_insets_; 181 } 182 183 private: 184 virtual ~AshRootWindowTransformer() {} 185 186 aura::RootWindow* root_window_; 187 gfx::Transform transform_; 188 189 // The accurate representation of the inverse of the |transform_|. 190 // This is used to avoid computation error caused by 191 // |gfx::Transform::GetInverse|. 192 gfx::Transform invert_transform_; 193 194 // The transform of the root window bounds. This is used to calculate 195 // the size of root window. 196 gfx::Transform root_window_bounds_transform_; 197 198 // The scale of the root window. See |display_info::ui_scale_| 199 // for more info. 200 float root_window_ui_scale_; 201 202 gfx::Insets host_insets_; 203 204 DISALLOW_COPY_AND_ASSIGN(AshRootWindowTransformer); 205}; 206 207// RootWindowTransformer for mirror root window. We simply copy the 208// texture (bitmap) of the source display into the mirror window, so 209// the root window bounds is the same as the source display's 210// pixel size (excluding overscan insets). 211class MirrorRootWindowTransformer : public aura::RootWindowTransformer { 212 public: 213 MirrorRootWindowTransformer(const DisplayInfo& source_display_info, 214 const DisplayInfo& mirror_display_info) { 215 root_bounds_ = gfx::Rect(source_display_info.bounds_in_native().size()); 216 gfx::Rect mirror_display_rect = 217 gfx::Rect(mirror_display_info.bounds_in_native().size()); 218 219 bool letterbox = root_bounds_.width() * mirror_display_rect.height() > 220 root_bounds_.height() * mirror_display_rect.width(); 221 if (letterbox) { 222 float mirror_scale_ratio = 223 (static_cast<float>(root_bounds_.width()) / 224 static_cast<float>(mirror_display_rect.width())); 225 float inverted_scale = 1.0f / mirror_scale_ratio; 226 int margin = static_cast<int>( 227 (mirror_display_rect.height() - 228 root_bounds_.height() * inverted_scale) / 2); 229 insets_.Set(0, margin, 0, margin); 230 231 transform_.Translate(0, margin); 232 transform_.Scale(inverted_scale, inverted_scale); 233 } else { 234 float mirror_scale_ratio = 235 (static_cast<float>(root_bounds_.height()) / 236 static_cast<float>(mirror_display_rect.height())); 237 float inverted_scale = 1.0f / mirror_scale_ratio; 238 int margin = static_cast<int>( 239 (mirror_display_rect.width() - 240 root_bounds_.width() * inverted_scale) / 2); 241 insets_.Set(margin, 0, margin, 0); 242 243 transform_.Translate(margin, 0); 244 transform_.Scale(inverted_scale, inverted_scale); 245 } 246 } 247 248 // aura::RootWindowTransformer overrides: 249 virtual gfx::Transform GetTransform() const OVERRIDE { 250 return transform_; 251 } 252 virtual gfx::Transform GetInverseTransform() const OVERRIDE { 253 gfx::Transform invert; 254 CHECK(transform_.GetInverse(&invert)); 255 return invert; 256 } 257 virtual gfx::Rect GetRootWindowBounds( 258 const gfx::Size& host_size) const OVERRIDE { 259 return root_bounds_; 260 } 261 virtual gfx::Insets GetHostInsets() const OVERRIDE { 262 return insets_; 263 } 264 265 private: 266 virtual ~MirrorRootWindowTransformer() {} 267 268 gfx::Transform transform_; 269 gfx::Rect root_bounds_; 270 gfx::Insets insets_; 271 272 DISALLOW_COPY_AND_ASSIGN(MirrorRootWindowTransformer); 273}; 274 275} // namespace 276 277aura::RootWindowTransformer* CreateRootWindowTransformerForDisplay( 278 aura::RootWindow* root, 279 const gfx::Display& display) { 280 return new AshRootWindowTransformer(root, display); 281} 282 283aura::RootWindowTransformer* CreateRootWindowTransformerForMirroredDisplay( 284 const DisplayInfo& source_display_info, 285 const DisplayInfo& mirror_display_info) { 286 return new MirrorRootWindowTransformer(source_display_info, 287 mirror_display_info); 288} 289 290} // namespace internal 291} // namespace ash 292