Index: ash/display/root_window_transformers.cc |
diff --git a/ash/display/root_window_transformers.cc b/ash/display/root_window_transformers.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..da5c3975e2c58afdfaa36bcd502e61ea54d5ae43 |
--- /dev/null |
+++ b/ash/display/root_window_transformers.cc |
@@ -0,0 +1,276 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "ash/display/root_window_transformers.h" |
+ |
+#include <cmath> |
+ |
+#include "ash/display/display_info.h" |
+#include "ash/display/display_manager.h" |
+#include "ash/magnifier/magnification_controller.h" |
+#include "ash/shell.h" |
+#include "base/basictypes.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "third_party/skia/include/utils/SkMatrix44.h" |
+#include "ui/aura/root_window.h" |
+#include "ui/aura/root_window_transformer.h" |
+#include "ui/aura/window_property.h" |
+#include "ui/compositor/dip_util.h" |
+#include "ui/gfx/display.h" |
+#include "ui/gfx/insets.h" |
+#include "ui/gfx/size_conversions.h" |
+#include "ui/gfx/transform.h" |
+#include "ui/gfx/transform.h" |
+ |
+DECLARE_WINDOW_PROPERTY_TYPE(gfx::Display::Rotation); |
+ |
+namespace ash { |
+namespace internal { |
+namespace { |
+ |
+DEFINE_WINDOW_PROPERTY_KEY(gfx::Display::Rotation, kRotationPropertyKey, |
+ gfx::Display::ROTATE_0); |
+ |
+// Round near zero value to zero. |
+void RoundNearZero(gfx::Transform* transform) { |
+ const float kEpsilon = 0.001f; |
+ SkMatrix44& matrix = transform->matrix(); |
+ for (int x = 0; x < 4; ++x) { |
+ for (int y = 0; y < 4; ++y) { |
+ if (std::abs(SkMScalarToFloat(matrix.get(x, y))) < kEpsilon) |
+ matrix.set(x, y, SkFloatToMScalar(0.0f)); |
+ } |
+ } |
+} |
+ |
+gfx::Transform CreateRotationTransform(aura::RootWindow* root_window, |
+ const gfx::Display& display) { |
+ DisplayInfo info = |
+ Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id()); |
+ |
+ // TODO(oshima): Add animation. (crossfade+rotation, or just cross-fade) |
+#if defined(OS_WIN) |
+ // Windows 8 bots refused to resize the host window, and |
+ // updating the transform results in incorrectly resizing |
+ // the root window. Don't apply the transform unless |
+ // necessary so that unit tests pass on win8 bots. |
+ if (info.rotation() == root_window->GetProperty(kRotationPropertyKey)) |
+ return gfx::Transform(); |
+ root_window->SetProperty(kRotationPropertyKey, info.rotation()); |
+#endif |
+ |
+ gfx::Transform rotate; |
+ // The origin is (0, 0), so the translate width/height must be reduced by |
+ // 1 pixel. |
+ float one_pixel = 1.0f / display.device_scale_factor(); |
+ switch (info.rotation()) { |
+ case gfx::Display::ROTATE_0: |
+ break; |
+ case gfx::Display::ROTATE_90: |
+ rotate.Translate(display.bounds().height() - one_pixel, 0); |
+ rotate.Rotate(90); |
+ break; |
+ case gfx::Display::ROTATE_270: |
+ rotate.Translate(0, display.bounds().width() - one_pixel); |
+ rotate.Rotate(270); |
+ break; |
+ case gfx::Display::ROTATE_180: |
+ rotate.Translate(display.bounds().width() - one_pixel, |
+ display.bounds().height() - one_pixel); |
+ rotate.Rotate(180); |
+ break; |
+ } |
+ |
+ RoundNearZero(&rotate); |
+ return rotate; |
+} |
+ |
+gfx::Transform CreateMagnifierTransform(aura::RootWindow* root_window) { |
+ MagnificationController* magnifier = |
+ Shell::GetInstance()->magnification_controller(); |
+ float magnifier_scale = 1.f; |
+ gfx::Point magnifier_offset; |
+ if (magnifier && magnifier->IsEnabled()) { |
+ magnifier_scale = magnifier->GetScale(); |
+ magnifier_offset = magnifier->GetWindowPosition(); |
+ } |
+ gfx::Transform transform; |
+ if (magnifier_scale != 1.f) { |
+ transform.Scale(magnifier_scale, magnifier_scale); |
+ transform.Translate(-magnifier_offset.x(), -magnifier_offset.y()); |
+ } |
+ return transform; |
+} |
+ |
+gfx::Transform CreateInsetsAndScaleTransform(const gfx::Insets& insets, |
+ float device_scale_factor, |
+ float ui_scale) { |
+ gfx::Transform transform; |
+ if (insets.top() != 0 || insets.left() != 0) { |
+ float x_offset = insets.left() / device_scale_factor; |
+ float y_offset = insets.top() / device_scale_factor; |
+ transform.Translate(x_offset, y_offset); |
+ } |
+ float inverted_scale = 1.0f / ui_scale; |
+ transform.Scale(inverted_scale, inverted_scale); |
+ return transform; |
+} |
+ |
+gfx::Transform CreateOverscanAndUIScaleTransform(aura::RootWindow* root_window, |
+ const gfx::Display& display) { |
+ DisplayInfo info = |
+ Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id()); |
+ return CreateInsetsAndScaleTransform( |
+ info.GetOverscanInsetsInPixel(), |
+ ui::GetDeviceScaleFactor(root_window->layer()), |
+ info.ui_scale()); |
+} |
+ |
+// RootWindowTransformer for ash environment. |
+class AshRootWindowTransformer : public aura::RootWindowTransformer { |
+ public: |
+ AshRootWindowTransformer(aura::RootWindow* root, |
+ const gfx::Display& display) |
+ : root_window_(root) { |
+ root_window_bounds_transform_ = |
+ CreateOverscanAndUIScaleTransform(root, display) * |
+ CreateRotationTransform(root, display); |
+ transform_ = root_window_bounds_transform_ * CreateMagnifierTransform(root); |
+ CHECK(transform_.GetInverse(&invert_transform_)); |
+ |
+ DisplayInfo info = Shell::GetInstance()->display_manager()-> |
+ GetDisplayInfo(display.id()); |
+ root_window_ui_scale_ = info.ui_scale(); |
+ host_insets_ = info.GetOverscanInsetsInPixel(); |
+ MagnificationController* magnifier = |
+ Shell::GetInstance()->magnification_controller(); |
+ |
+ bool scaled = (root_window_ui_scale_ != 1.f) || |
+ (magnifier && magnifier->GetScale() != 1.f); |
+ root_window_->layer()->SetForceRenderSurface(scaled); |
+ } |
+ |
+ // aura::RootWindowTransformer overrides: |
+ virtual gfx::Transform GetTransform() const OVERRIDE { |
+ return transform_; |
+ } |
+ virtual gfx::Transform GetInverseTransform() const OVERRIDE { |
+ return invert_transform_; |
+ } |
+ virtual gfx::Rect GetRootWindowBounds( |
+ const gfx::Size& host_size) const OVERRIDE { |
+ gfx::Rect bounds(host_size); |
+ bounds.Inset(host_insets_); |
+ bounds = ui::ConvertRectToDIP(root_window_->layer(), bounds); |
+ gfx::RectF new_bounds(bounds); |
+ root_window_bounds_transform_.TransformRect(&new_bounds); |
+ // Apply |root_window_scale_| twice as the downscaling |
+ // is already applied once in |SetTransformInternal()|. |
+ // TODO(oshima): This is a bit ugly. Consider specifying |
+ // the pseudo host resolution instead. |
+ new_bounds.Scale(root_window_ui_scale_ * root_window_ui_scale_); |
+ // Ignore the origin because RootWindow's insets are handled by |
+ // the transform. |
+ // Floor the size because the bounds is no longer aligned to |
+ // backing pixel when |root_window_scale_| is specified |
+ // (850 height at 1.25 scale becomes 1062.5 for example.) |
+ return gfx::Rect(gfx::ToFlooredSize(new_bounds.size())); |
+ } |
+ |
+ virtual gfx::Insets GetHostInsets() const OVERRIDE { |
+ return host_insets_; |
+ } |
+ |
+ private: |
+ virtual ~AshRootWindowTransformer() {} |
+ |
+ aura::RootWindow* root_window_; |
+ gfx::Transform transform_; |
+ |
+ // The accurate representation of the inverse of the |transform_|. |
+ // This is used to avoid computation error caused by |
+ // |gfx::Transform::GetInverse|. |
+ gfx::Transform invert_transform_; |
+ |
+ // The transform of the root window bounds. This is used to calculate |
+ // the size of root window. |
+ gfx::Transform root_window_bounds_transform_; |
+ |
+ // The scale of the root window. This is used to expand the |
+ // area of the root window (useful in HighDPI display). |
+ // Note that this should not be confused with the device scale |
+ // factor, which specfies the pixel density of the display. |
+ float root_window_ui_scale_; |
+ |
+ gfx::Insets host_insets_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(AshRootWindowTransformer); |
+}; |
+ |
+// RootWindowTransformer for mirror root window. We simply copy the |
+// texture (bitmap) of the source display into the mirror window, so |
+// the root window bounds is the same as the source display's |
+// pixel size (excluding overscan insets). |
+class MirrorRootWindowTransformer : public aura::RootWindowTransformer { |
+ public: |
+ MirrorRootWindowTransformer(const DisplayInfo& source_display_info, |
+ const DisplayInfo& mirror_display_info) { |
+ root_bounds_ = gfx::Rect(source_display_info.bounds_in_pixel().size()); |
+ gfx::Rect mirror_display_rect = |
+ gfx::Rect(mirror_display_info.bounds_in_pixel().size()); |
+ |
+ // TODO(oshima): Insets & scale has to be adjusted so that |
+ // 1) it does letterbox/pillarbox to adjust aspect ratio |
+ // 2) visible area excluding insets are correctly mapped |
+ // to the other display's visible area. |
+ float mirror_scale_ratio = |
+ (static_cast<float>(root_bounds_.width()) / |
+ static_cast<float>(mirror_display_rect.width())); |
+ float inverted_scale = 1.0f / mirror_scale_ratio; |
+ transform_.Scale(inverted_scale, inverted_scale); |
+ } |
+ |
+ // aura::RootWindowTransformer overrides: |
+ virtual gfx::Transform GetTransform() const OVERRIDE { |
+ return transform_; |
+ } |
+ virtual gfx::Transform GetInverseTransform() const OVERRIDE { |
+ gfx::Transform invert; |
+ CHECK(transform_.GetInverse(&invert)); |
+ return invert; |
+ } |
+ virtual gfx::Rect GetRootWindowBounds( |
+ const gfx::Size& host_size) const OVERRIDE { |
+ return root_bounds_; |
+ } |
+ virtual gfx::Insets GetHostInsets() const OVERRIDE { |
+ return gfx::Insets(); |
+ } |
+ |
+ private: |
+ virtual ~MirrorRootWindowTransformer() {} |
+ |
+ gfx::Transform transform_; |
+ gfx::Rect root_bounds_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(MirrorRootWindowTransformer); |
+}; |
+ |
+} // namespace |
+ |
+aura::RootWindowTransformer* CreateRootWindowTransformerForDisplay( |
+ aura::RootWindow* root, |
+ const gfx::Display& display) { |
+ return new AshRootWindowTransformer(root, display); |
+} |
+ |
+aura::RootWindowTransformer* CreateRootWindowTransformerForMirroredDisplay( |
+ const DisplayInfo& source_display_info, |
+ const DisplayInfo& mirror_display_info) { |
+ return new MirrorRootWindowTransformer(source_display_info, |
+ mirror_display_info); |
+} |
+ |
+} // namespace internal |
+} // namespace ash |