Home | History | Annotate | Download | only in display
      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 
     26 DECLARE_WINDOW_PROPERTY_TYPE(gfx::Display::Rotation);
     27 
     28 namespace ash {
     29 namespace internal {
     30 namespace {
     31 
     32 DEFINE_WINDOW_PROPERTY_KEY(gfx::Display::Rotation, kRotationPropertyKey,
     33                            gfx::Display::ROTATE_0);
     34 
     35 // Round near zero value to zero.
     36 void 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 
     51 gfx::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 
     93 gfx::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 
    110 gfx::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 
    124 gfx::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.
    135 class 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. This is used to expand the
    199   // area of the root window (useful in HighDPI display).
    200   // Note that this should not be confused with the device scale
    201   // factor, which specfies the pixel density of the display.
    202   float root_window_ui_scale_;
    203 
    204   gfx::Insets host_insets_;
    205 
    206   DISALLOW_COPY_AND_ASSIGN(AshRootWindowTransformer);
    207 };
    208 
    209 // RootWindowTransformer for mirror root window. We simply copy the
    210 // texture (bitmap) of the source display into the mirror window, so
    211 // the root window bounds is the same as the source display's
    212 // pixel size (excluding overscan insets).
    213 class MirrorRootWindowTransformer : public aura::RootWindowTransformer {
    214  public:
    215   MirrorRootWindowTransformer(const DisplayInfo& source_display_info,
    216                               const DisplayInfo& mirror_display_info) {
    217     root_bounds_ = gfx::Rect(source_display_info.bounds_in_pixel().size());
    218     gfx::Rect mirror_display_rect =
    219         gfx::Rect(mirror_display_info.bounds_in_pixel().size());
    220 
    221     bool letterbox = root_bounds_.width() * mirror_display_rect.height() >
    222         root_bounds_.height() * mirror_display_rect.width();
    223     if (letterbox) {
    224       float mirror_scale_ratio =
    225           (static_cast<float>(root_bounds_.width()) /
    226            static_cast<float>(mirror_display_rect.width()));
    227       float inverted_scale = 1.0f / mirror_scale_ratio;
    228       int margin = static_cast<int>(
    229           (mirror_display_rect.height() -
    230            root_bounds_.height() * inverted_scale) / 2);
    231       insets_.Set(0, margin, 0, margin);
    232 
    233       transform_.Translate(0,  margin);
    234       transform_.Scale(inverted_scale, inverted_scale);
    235     } else {
    236       float mirror_scale_ratio =
    237           (static_cast<float>(root_bounds_.height()) /
    238            static_cast<float>(mirror_display_rect.height()));
    239       float inverted_scale = 1.0f / mirror_scale_ratio;
    240       int margin = static_cast<int>(
    241           (mirror_display_rect.width() -
    242            root_bounds_.width() * inverted_scale) / 2);
    243       insets_.Set(margin, 0, margin, 0);
    244 
    245       transform_.Translate(margin, 0);
    246       transform_.Scale(inverted_scale, inverted_scale);
    247     }
    248   }
    249 
    250   // aura::RootWindowTransformer overrides:
    251   virtual gfx::Transform GetTransform() const OVERRIDE {
    252     return transform_;
    253   }
    254   virtual gfx::Transform GetInverseTransform() const OVERRIDE {
    255     gfx::Transform invert;
    256     CHECK(transform_.GetInverse(&invert));
    257     return invert;
    258   }
    259   virtual gfx::Rect GetRootWindowBounds(
    260       const gfx::Size& host_size) const OVERRIDE {
    261     return root_bounds_;
    262   }
    263   virtual gfx::Insets GetHostInsets() const OVERRIDE {
    264     return insets_;
    265   }
    266 
    267  private:
    268   virtual ~MirrorRootWindowTransformer() {}
    269 
    270   gfx::Transform transform_;
    271   gfx::Rect root_bounds_;
    272   gfx::Insets insets_;
    273 
    274   DISALLOW_COPY_AND_ASSIGN(MirrorRootWindowTransformer);
    275 };
    276 
    277 }  // namespace
    278 
    279 aura::RootWindowTransformer* CreateRootWindowTransformerForDisplay(
    280     aura::RootWindow* root,
    281     const gfx::Display& display) {
    282   return new AshRootWindowTransformer(root, display);
    283 }
    284 
    285 aura::RootWindowTransformer* CreateRootWindowTransformerForMirroredDisplay(
    286     const DisplayInfo& source_display_info,
    287     const DisplayInfo& mirror_display_info) {
    288   return new MirrorRootWindowTransformer(source_display_info,
    289                                          mirror_display_info);
    290 }
    291 
    292 }  // namespace internal
    293 }  // namespace ash
    294