// 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 #include "ash/accessibility/magnifier/fullscreen_magnifier_controller.h" #include "ash/display/display_util.h" #include "ash/host/root_window_transformer.h" #include "ash/shell.h" #include "ash/utility/transformer_util.h" #include "base/command_line.h" #include "base/memory/ptr_util.h" #include "base/system/sys_info.h" #include "ui/display/display.h" #include "ui/display/manager/display_manager.h" #include "ui/display/screen.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/geometry/transform.h" namespace ash { namespace { // TODO(oshima): Transformers should be able to adjust itself // when the device scale factor is changed, instead of // precalculating the transform using fixed value. // Creates rotation transform for |root_window| to |new_rotation|. This will // call |CreateRotationTransform()|, the |old_rotation| will implicitly be // |display::Display::ROTATE_0|. gfx::Transform CreateRootWindowRotationTransform( const display::Display& display) { gfx::SizeF size(display.GetSizeInPixel()); // Use SizeF so that the origin of translated layer will be // aligned when scaled back at pixels. size.Scale(1.f / display.device_scale_factor()); return CreateRotationTransform(display::Display::ROTATE_0, display.panel_rotation(), size); } gfx::Transform CreateInsetsTransform(const gfx::Insets& insets, float device_scale_factor) { 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); } return transform; } // Returns a transform with rotation adjusted |insets_in_pixel|. The transform // is applied to the root window so that |insets_in_pixel| looks correct after // the rotation applied at the output. gfx::Transform CreateReverseRotatedInsetsTransform( display::Display::Rotation rotation, const gfx::Insets& insets_in_pixel, float device_scale_factor) { float x_offset = 0; float y_offset = 0; switch (rotation) { case display::Display::ROTATE_0: x_offset = insets_in_pixel.left(); y_offset = insets_in_pixel.top(); break; case display::Display::ROTATE_90: x_offset = insets_in_pixel.top(); y_offset = insets_in_pixel.right(); break; case display::Display::ROTATE_180: x_offset = insets_in_pixel.right(); y_offset = insets_in_pixel.bottom(); break; case display::Display::ROTATE_270: x_offset = insets_in_pixel.bottom(); y_offset = insets_in_pixel.left(); break; } gfx::Transform transform; if (x_offset != 0 || y_offset != 0) { x_offset /= device_scale_factor; y_offset /= device_scale_factor; transform.Translate(x_offset, y_offset); } return transform; } // RootWindowTransformer for ash environment. class AshRootWindowTransformer : public RootWindowTransformer { public: AshRootWindowTransformer(const display::Display& display) { initial_root_bounds_ = gfx::Rect(display.size()); display::DisplayManager* display_manager = Shell::Get()->display_manager(); display::ManagedDisplayInfo info = display_manager->GetDisplayInfo(display.id()); host_insets_ = info.GetOverscanInsetsInPixel(); gfx::Transform insets_and_rotation_transform = CreateInsetsTransform(host_insets_, display.device_scale_factor()) * CreateRootWindowRotationTransform(display); transform_ = insets_and_rotation_transform; insets_and_scale_transform_ = CreateReverseRotatedInsetsTransform( display.panel_rotation(), host_insets_, display.device_scale_factor()); FullscreenMagnifierController* magnifier = Shell::Get()->fullscreen_magnifier_controller(); if (magnifier) { gfx::Transform magnifier_scale = magnifier->GetMagnifierTransform(); transform_ *= magnifier_scale; insets_and_scale_transform_ *= magnifier_scale; } CHECK(transform_.GetInverse(&invert_transform_)); CHECK(insets_and_rotation_transform.GetInverse( &root_window_bounds_transform_)); root_window_bounds_transform_.Scale(1.f / display.device_scale_factor(), 1.f / display.device_scale_factor()); initial_host_size_ = info.bounds_in_native().size(); } AshRootWindowTransformer(const AshRootWindowTransformer&) = delete; AshRootWindowTransformer& operator=(const AshRootWindowTransformer&) = delete; // aura::RootWindowTransformer overrides: gfx::Transform GetTransform() const override { return transform_; } gfx::Transform GetInverseTransform() const override { return invert_transform_; } gfx::Rect GetRootWindowBounds(const gfx::Size& host_size) const override { if (base::SysInfo::IsRunningOnChromeOS()) return initial_root_bounds_; // If we're running on linux desktop for dev purpose, the host window // may be updated to new size. Recompute the root window bounds based // on the host size if the host size changed. if (initial_host_size_ == host_size) return initial_root_bounds_; gfx::RectF new_bounds = gfx::RectF(gfx::SizeF(host_size)); new_bounds.Inset(gfx::InsetsF(host_insets_)); root_window_bounds_transform_.TransformRect(&new_bounds); // Root window origin will be (0,0) except during bounds changes. // Set to exactly zero to avoid rounding issues. // 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())); } gfx::Insets GetHostInsets() const override { return host_insets_; } gfx::Transform GetInsetsAndScaleTransform() const override { return insets_and_scale_transform_; } private: ~AshRootWindowTransformer() override = default; 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 the root window. It is the composition of the following transforms // - inverse of insets. Insets position the content area within the display. // - inverse of rotation. Rotation changes orientation of the content area. // - inverse of device scale. Scaling up content shrinks the content area. // // Insets also shrink the content area but this happens prior to applying the // transformation in GetRootWindowBounds(). // // Magnification does not affect the window size. Content is clipped in this // case, but the magnifier allows panning to reach clipped areas. // // The transforms are inverted because GetTransform() is the transform from // root window coordinates to host coordinates, but this transform is used in // the reverse direction (derives root window bounds from display bounds). gfx::Transform root_window_bounds_transform_; gfx::Insets host_insets_; gfx::Transform insets_and_scale_transform_; gfx::Rect initial_root_bounds_; gfx::Size initial_host_size_; }; // 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 RootWindowTransformer { public: MirrorRootWindowTransformer( const display::ManagedDisplayInfo& source_display_info, const display::ManagedDisplayInfo& mirror_display_info) { root_bounds_ = gfx::Rect(source_display_info.GetSizeInPixelWithPanelOrientation()); display::Display::Rotation active_root_rotation = source_display_info.GetActiveRotation(); const bool should_undo_rotation = ShouldUndoRotationForMirror(); gfx::Transform rotation_transform; if (should_undo_rotation) { // Calculate the transform to undo the rotation and apply it to the // source display. Use GetActiveRotation() because |source_bounds_| // includes panel rotation and we only need to revert active rotation. rotation_transform = CreateRotationTransform( source_display_info.GetActiveRotation(), display::Display::ROTATE_0, gfx::SizeF(root_bounds_.size())); gfx::RectF rotated_bounds(root_bounds_); rotation_transform.TransformRect(&rotated_bounds); root_bounds_ = gfx::ToNearestRect(rotated_bounds); active_root_rotation = display::Display::ROTATE_0; } gfx::Rect mirror_display_rect = gfx::Rect(mirror_display_info.bounds_in_native().size()); // When logical rotation is 90 or 270 degree, transpose is needed to apply // reverse rotation to `root_bounds_` and `mirror_display_rect` to exclude // the rotation. This is because the rotation happens at viz output and // `transform_` needs to be calculated without the rotation. // E.g. host native size 1600x1200. Rotation 90 degree. `transform_` needs // to fit 1200x1600 rather than 1600x1200. const bool need_transpose = active_root_rotation == display::Display::ROTATE_90 || active_root_rotation == display::Display::ROTATE_270; if (need_transpose) { root_bounds_.Transpose(); mirror_display_rect.Transpose(); } bool letterbox = root_bounds_.width() * mirror_display_rect.height() > root_bounds_.height() * mirror_display_rect.width(); if (letterbox) { float mirror_scale_ratio = (static_cast(root_bounds_.width()) / static_cast(mirror_display_rect.width())); float inverted_scale = 1.0f / mirror_scale_ratio; int margin = static_cast((mirror_display_rect.height() - root_bounds_.height() * inverted_scale) / 2); insets_ = gfx::Insets::TLBR(0, margin, 0, margin); transform_.Translate(0, margin); transform_.Scale(inverted_scale, inverted_scale); } else { float mirror_scale_ratio = (static_cast(root_bounds_.height()) / static_cast(mirror_display_rect.height())); float inverted_scale = 1.0f / mirror_scale_ratio; int margin = static_cast((mirror_display_rect.width() - root_bounds_.width() * inverted_scale) / 2); insets_ = gfx::Insets::TLBR(margin, 0, margin, 0); transform_.Translate(margin, 0); transform_.Scale(inverted_scale, inverted_scale); } } MirrorRootWindowTransformer(const MirrorRootWindowTransformer&) = delete; MirrorRootWindowTransformer& operator=(const MirrorRootWindowTransformer&) = delete; // aura::RootWindowTransformer overrides: gfx::Transform GetTransform() const override { return transform_; } gfx::Transform GetInverseTransform() const override { gfx::Transform invert; CHECK(transform_.GetInverse(&invert)); return invert; } gfx::Rect GetRootWindowBounds(const gfx::Size& host_size) const override { return root_bounds_; } gfx::Insets GetHostInsets() const override { return insets_; } gfx::Transform GetInsetsAndScaleTransform() const override { return transform_; } private: ~MirrorRootWindowTransformer() override = default; gfx::Transform transform_; gfx::Rect root_bounds_; gfx::Insets insets_; }; class PartialBoundsRootWindowTransformer : public RootWindowTransformer { public: PartialBoundsRootWindowTransformer(const gfx::Rect& screen_bounds, const display::Display& display) { const display::DisplayManager* display_manager = Shell::Get()->display_manager(); display::ManagedDisplayInfo display_info = display_manager->GetDisplayInfo(display.id()); // Physical root bounds. root_bounds_ = gfx::Rect(display_info.bounds_in_native().size()); display::Display::Rotation active_root_rotation = display_info.GetActiveRotation(); const bool need_transpose = active_root_rotation == display::Display::ROTATE_90 || active_root_rotation == display::Display::ROTATE_270; if (need_transpose) root_bounds_.Transpose(); // |screen_bounds| is the unified desktop logical bounds. // Calculate the unified height scale value, and apply the same scale on the // row physical height to get the row logical height. display::Display unified_display = display::Screen::GetScreen()->GetPrimaryDisplay(); const int unified_physical_height = unified_display.GetSizeInPixel().height(); const int unified_logical_height = screen_bounds.height(); const float unified_height_scale = static_cast(unified_logical_height) / unified_physical_height; const int row_index = display_manager->GetMirroringDisplayRowIndexInUnifiedMatrix( display.id()); const int row_physical_height = display_manager->GetUnifiedDesktopRowMaxHeight(row_index); const int row_logical_height = row_physical_height * unified_height_scale; const float dsf = unified_display.device_scale_factor(); const float scale = root_bounds_.height() / (dsf * row_logical_height); transform_.Scale(scale, scale); transform_.Translate(-SkIntToScalar(display.bounds().x()), -SkIntToScalar(display.bounds().y())); // Scaling using physical root bounds here, because rotation is applied // before device_scale_factor is applied. gfx::Transform rotation = CreateRotationTransform( display::Display::ROTATE_0, display.panel_rotation(), gfx::SizeF(root_bounds_.size())); CHECK((rotation * transform_).GetInverse(&invert_transform_)); } PartialBoundsRootWindowTransformer( const PartialBoundsRootWindowTransformer&) = delete; PartialBoundsRootWindowTransformer& operator=( const PartialBoundsRootWindowTransformer&) = delete; // RootWindowTransformer: gfx::Transform GetTransform() const override { return transform_; } gfx::Transform GetInverseTransform() const override { return invert_transform_; } gfx::Rect GetRootWindowBounds(const gfx::Size& host_size) const override { return root_bounds_; } gfx::Insets GetHostInsets() const override { return gfx::Insets(); } gfx::Transform GetInsetsAndScaleTransform() const override { return transform_; } private: ~PartialBoundsRootWindowTransformer() override = default; gfx::Transform transform_; gfx::Transform invert_transform_; gfx::Rect root_bounds_; }; } // namespace std::unique_ptr CreateRootWindowTransformerForDisplay( const display::Display& display) { return base::WrapUnique( new AshRootWindowTransformer(display)); } std::unique_ptr CreateRootWindowTransformerForMirroredDisplay( const display::ManagedDisplayInfo& source_display_info, const display::ManagedDisplayInfo& mirror_display_info) { return base::WrapUnique( new MirrorRootWindowTransformer(source_display_info, mirror_display_info)); } std::unique_ptr CreateRootWindowTransformerForUnifiedDesktop(const gfx::Rect& screen_bounds, const display::Display& display) { return base::WrapUnique( new PartialBoundsRootWindowTransformer(screen_bounds, display)); } } // namespace ash