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
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294
  295
  296
  297
  298
  299
  300
  301
  302
  303
  304
  305
  306
  307
  308
  309
  310
  311
  312
  313
  314
  315
  316
  317
  318
  319
  320
  321
  322
  323
  324
  325
  326
  327
  328
  329
  330
  331
  332
  333
  334
  335
  336
  337
  338
  339
  340
  341
  342
  343
  344
  345
  346
  347
  348
  349
  350
  351
  352
  353
  354
  355
  356
  357
  358
  359
  360
  361
  362
  363
  364
  365
  366
  367
  368
  369
  370
  371
  372
  373
  374
  375
  376
  377
  378
  379
  380
  381
  382
  383
  384
  385
  386
  387
  388
  389
  390
  391
  392
  393
  394
  395
  396

ash / display / root_window_transformers.cc [blame]

// Copyright 2013 The Chromium Authors
// 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/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_));
    new_bounds = root_window_bounds_transform_.MapRect(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()));
      root_bounds_ = gfx::ToNearestRect(
          rotation_transform.MapRect(gfx::RectF(root_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<float>(root_bounds_.width()) /
           static_cast<float>(mirror_display_rect.width()));
      float inverted_scale = 1.0f / mirror_scale_ratio;
      int margin = static_cast<int>((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<float>(root_bounds_.height()) /
           static_cast<float>(mirror_display_rect.height()));
      float inverted_scale = 1.0f / mirror_scale_ratio;
      int margin = static_cast<int>((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<float>(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<RootWindowTransformer> CreateRootWindowTransformerForDisplay(
    const display::Display& display) {
  return base::WrapUnique<RootWindowTransformer>(
      new AshRootWindowTransformer(display));
}

std::unique_ptr<RootWindowTransformer>
CreateRootWindowTransformerForMirroredDisplay(
    const display::ManagedDisplayInfo& source_display_info,
    const display::ManagedDisplayInfo& mirror_display_info) {
  return base::WrapUnique<RootWindowTransformer>(
      new MirrorRootWindowTransformer(source_display_info,
                                      mirror_display_info));
}

std::unique_ptr<RootWindowTransformer>
CreateRootWindowTransformerForUnifiedDesktop(const gfx::Rect& screen_bounds,
                                             const display::Display& display) {
  return base::WrapUnique<RootWindowTransformer>(
      new PartialBoundsRootWindowTransformer(screen_bounds, display));
}

}  // namespace ash