summaryrefslogtreecommitdiffstats
path: root/ash/ash_root_window_transformer.cc
blob: fa8a8a3c546bd3f3164907d014bd7fe168a4deea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// 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/ash_root_window_transformer.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 "third_party/skia/include/utils/SkMatrix44.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window_property.h"
#include "ui/compositor/dip_util.h"
#include "ui/gfx/display.h"
#include "ui/gfx/size_conversions.h"
#include "ui/gfx/transform.h"

DECLARE_WINDOW_PROPERTY_TYPE(gfx::Display::Rotation);

namespace ash {
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) {
  internal::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 CreateOverscanAndUIScaleTransform(aura::RootWindow* root_window,
                                                 const gfx::Display& display) {
  internal::DisplayInfo info =
      Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id());
  gfx::Insets insets = info.GetOverscanInsetsInPixel();
  float scale = info.ui_scale();

  gfx::Transform transform;
  if (insets.top() != 0 || insets.left() != 0) {
    float device_scale_factor = ui::GetDeviceScaleFactor(root_window->layer());
    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 / scale;
  transform.Scale(inverted_scale, inverted_scale);
  return transform;
}

}  // namespace

AshRootWindowTransformer::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_));

  internal::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);
}

AshRootWindowTransformer::~AshRootWindowTransformer() {}

gfx::Transform AshRootWindowTransformer::GetTransform() const {
  return transform_;
}

gfx::Transform AshRootWindowTransformer::GetInverseTransform() const {
  return invert_transform_;
}

gfx::Rect AshRootWindowTransformer::GetRootWindowBounds(
    const gfx::Size& host_size) const {
  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()));
}

gfx::Insets AshRootWindowTransformer::GetHostInsets() const {
  return host_insets_;
}

}  // namespace ash