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 #if defined(OS_WIN)
     33 DEFINE_WINDOW_PROPERTY_KEY(gfx::Display::Rotation, kRotationPropertyKey,
     34                            gfx::Display::ROTATE_0);
     35 #endif
     36 
     37 // Round near zero value to zero.
     38 void RoundNearZero(gfx::Transform* transform) {
     39   const float kEpsilon = 0.001f;
     40   SkMatrix44& matrix = transform->matrix();
     41   for (int x = 0; x < 4; ++x) {
     42     for (int y = 0; y < 4; ++y) {
     43       if (std::abs(SkMScalarToFloat(matrix.get(x, y))) < kEpsilon)
     44         matrix.set(x, y, SkFloatToMScalar(0.0f));
     45     }
     46   }
     47 }
     48 
     49 // TODO(oshima): Transformers should be able to adjust itself
     50 // when the device scale factor is changed, instead of
     51 // precalculating the transform using fixed value.
     52 
     53 gfx::Transform CreateRotationTransform(aura::Window* root_window,
     54                                        const gfx::Display& display) {
     55   DisplayInfo info =
     56       Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id());
     57 
     58   // TODO(oshima): Add animation. (crossfade+rotation, or just cross-fade)
     59 #if defined(OS_WIN)
     60   // Windows 8 bots refused to resize the host window, and
     61   // updating the transform results in incorrectly resizing
     62   // the root window. Don't apply the transform unless
     63   // necessary so that unit tests pass on win8 bots.
     64   if (info.rotation() == root_window->GetProperty(kRotationPropertyKey))
     65     return gfx::Transform();
     66   root_window->SetProperty(kRotationPropertyKey, info.rotation());
     67 #endif
     68 
     69   gfx::Transform rotate;
     70   // The origin is (0, 0), so the translate width/height must be reduced by
     71   // 1 pixel.
     72   float one_pixel = 1.0f / display.device_scale_factor();
     73   switch (info.rotation()) {
     74     case gfx::Display::ROTATE_0:
     75       break;
     76     case gfx::Display::ROTATE_90:
     77       rotate.Translate(display.bounds().height() - one_pixel, 0);
     78       rotate.Rotate(90);
     79       break;
     80     case gfx::Display::ROTATE_270:
     81       rotate.Translate(0, display.bounds().width() - one_pixel);
     82       rotate.Rotate(270);
     83       break;
     84     case gfx::Display::ROTATE_180:
     85       rotate.Translate(display.bounds().width() - one_pixel,
     86                        display.bounds().height() - one_pixel);
     87       rotate.Rotate(180);
     88       break;
     89   }
     90 
     91   RoundNearZero(&rotate);
     92   return rotate;
     93 }
     94 
     95 gfx::Transform CreateMagnifierTransform(aura::Window* root_window) {
     96   MagnificationController* magnifier =
     97       Shell::GetInstance()->magnification_controller();
     98   float magnifier_scale = 1.f;
     99   gfx::Point magnifier_offset;
    100   if (magnifier && magnifier->IsEnabled()) {
    101     magnifier_scale = magnifier->GetScale();
    102     magnifier_offset = magnifier->GetWindowPosition();
    103   }
    104   gfx::Transform transform;
    105   if (magnifier_scale != 1.f) {
    106     transform.Scale(magnifier_scale, magnifier_scale);
    107     transform.Translate(-magnifier_offset.x(), -magnifier_offset.y());
    108   }
    109   return transform;
    110 }
    111 
    112 gfx::Transform CreateInsetsAndScaleTransform(const gfx::Insets& insets,
    113                                              float device_scale_factor,
    114                                              float ui_scale) {
    115   gfx::Transform transform;
    116   if (insets.top() != 0 || insets.left() != 0) {
    117     float x_offset = insets.left() / device_scale_factor;
    118     float y_offset = insets.top() / device_scale_factor;
    119     transform.Translate(x_offset, y_offset);
    120   }
    121   float inverted_scale = 1.0f / ui_scale;
    122   transform.Scale(inverted_scale, inverted_scale);
    123   return transform;
    124 }
    125 
    126 // RootWindowTransformer for ash environment.
    127 class AshRootWindowTransformer : public aura::RootWindowTransformer {
    128  public:
    129   AshRootWindowTransformer(aura::Window* root,
    130                            const gfx::Display& display)
    131       : root_window_(root) {
    132     DisplayInfo info = Shell::GetInstance()->display_manager()->
    133         GetDisplayInfo(display.id());
    134     host_insets_ = info.GetOverscanInsetsInPixel();
    135     root_window_ui_scale_ = info.GetEffectiveUIScale();
    136     root_window_bounds_transform_ =
    137         CreateInsetsAndScaleTransform(host_insets_,
    138                                       display.device_scale_factor(),
    139                                       root_window_ui_scale_) *
    140         CreateRotationTransform(root, display);
    141     transform_ = root_window_bounds_transform_ * CreateMagnifierTransform(root);
    142     CHECK(transform_.GetInverse(&invert_transform_));
    143 
    144   }
    145 
    146   // aura::RootWindowTransformer overrides:
    147   virtual gfx::Transform GetTransform() const OVERRIDE {
    148     return transform_;
    149   }
    150   virtual gfx::Transform GetInverseTransform() const OVERRIDE {
    151     return invert_transform_;
    152   }
    153   virtual gfx::Rect GetRootWindowBounds(
    154       const gfx::Size& host_size) const OVERRIDE {
    155     gfx::Rect bounds(host_size);
    156     bounds.Inset(host_insets_);
    157     bounds = ui::ConvertRectToDIP(root_window_->layer(), bounds);
    158     gfx::RectF new_bounds(bounds);
    159     root_window_bounds_transform_.TransformRect(&new_bounds);
    160     // Apply |root_window_scale_| twice as the downscaling
    161     // is already applied once in |SetTransformInternal()|.
    162     // TODO(oshima): This is a bit ugly. Consider specifying
    163     // the pseudo host resolution instead.
    164     new_bounds.Scale(root_window_ui_scale_ * root_window_ui_scale_);
    165     // Ignore the origin because RootWindow's insets are handled by
    166     // the transform.
    167     // Floor the size because the bounds is no longer aligned to
    168     // backing pixel when |root_window_scale_| is specified
    169     // (850 height at 1.25 scale becomes 1062.5 for example.)
    170     return gfx::Rect(gfx::ToFlooredSize(new_bounds.size()));
    171   }
    172 
    173   virtual gfx::Insets GetHostInsets() const OVERRIDE {
    174     return host_insets_;
    175   }
    176 
    177  private:
    178   virtual ~AshRootWindowTransformer() {}
    179 
    180   aura::Window* root_window_;
    181   gfx::Transform transform_;
    182 
    183   // The accurate representation of the inverse of the |transform_|.
    184   // This is used to avoid computation error caused by
    185   // |gfx::Transform::GetInverse|.
    186   gfx::Transform invert_transform_;
    187 
    188   // The transform of the root window bounds. This is used to calculate
    189   // the size of root window.
    190   gfx::Transform root_window_bounds_transform_;
    191 
    192   // The scale of the root window. See |display_info::ui_scale_|
    193   // for more info.
    194   float root_window_ui_scale_;
    195 
    196   gfx::Insets host_insets_;
    197 
    198   DISALLOW_COPY_AND_ASSIGN(AshRootWindowTransformer);
    199 };
    200 
    201 // RootWindowTransformer for mirror root window. We simply copy the
    202 // texture (bitmap) of the source display into the mirror window, so
    203 // the root window bounds is the same as the source display's
    204 // pixel size (excluding overscan insets).
    205 class MirrorRootWindowTransformer : public aura::RootWindowTransformer {
    206  public:
    207   MirrorRootWindowTransformer(const DisplayInfo& source_display_info,
    208                               const DisplayInfo& mirror_display_info) {
    209     root_bounds_ = gfx::Rect(source_display_info.bounds_in_native().size());
    210     gfx::Rect mirror_display_rect =
    211         gfx::Rect(mirror_display_info.bounds_in_native().size());
    212 
    213     bool letterbox = root_bounds_.width() * mirror_display_rect.height() >
    214         root_bounds_.height() * mirror_display_rect.width();
    215     if (letterbox) {
    216       float mirror_scale_ratio =
    217           (static_cast<float>(root_bounds_.width()) /
    218            static_cast<float>(mirror_display_rect.width()));
    219       float inverted_scale = 1.0f / mirror_scale_ratio;
    220       int margin = static_cast<int>(
    221           (mirror_display_rect.height() -
    222            root_bounds_.height() * inverted_scale) / 2);
    223       insets_.Set(0, margin, 0, margin);
    224 
    225       transform_.Translate(0,  margin);
    226       transform_.Scale(inverted_scale, inverted_scale);
    227     } else {
    228       float mirror_scale_ratio =
    229           (static_cast<float>(root_bounds_.height()) /
    230            static_cast<float>(mirror_display_rect.height()));
    231       float inverted_scale = 1.0f / mirror_scale_ratio;
    232       int margin = static_cast<int>(
    233           (mirror_display_rect.width() -
    234            root_bounds_.width() * inverted_scale) / 2);
    235       insets_.Set(margin, 0, margin, 0);
    236 
    237       transform_.Translate(margin, 0);
    238       transform_.Scale(inverted_scale, inverted_scale);
    239     }
    240   }
    241 
    242   // aura::RootWindowTransformer overrides:
    243   virtual gfx::Transform GetTransform() const OVERRIDE {
    244     return transform_;
    245   }
    246   virtual gfx::Transform GetInverseTransform() const OVERRIDE {
    247     gfx::Transform invert;
    248     CHECK(transform_.GetInverse(&invert));
    249     return invert;
    250   }
    251   virtual gfx::Rect GetRootWindowBounds(
    252       const gfx::Size& host_size) const OVERRIDE {
    253     return root_bounds_;
    254   }
    255   virtual gfx::Insets GetHostInsets() const OVERRIDE {
    256     return insets_;
    257   }
    258 
    259  private:
    260   virtual ~MirrorRootWindowTransformer() {}
    261 
    262   gfx::Transform transform_;
    263   gfx::Rect root_bounds_;
    264   gfx::Insets insets_;
    265 
    266   DISALLOW_COPY_AND_ASSIGN(MirrorRootWindowTransformer);
    267 };
    268 
    269 }  // namespace
    270 
    271 aura::RootWindowTransformer* CreateRootWindowTransformerForDisplay(
    272     aura::Window* root,
    273     const gfx::Display& display) {
    274   return new AshRootWindowTransformer(root, display);
    275 }
    276 
    277 aura::RootWindowTransformer* CreateRootWindowTransformerForMirroredDisplay(
    278     const DisplayInfo& source_display_info,
    279     const DisplayInfo& mirror_display_info) {
    280   return new MirrorRootWindowTransformer(source_display_info,
    281                                          mirror_display_info);
    282 }
    283 
    284 }  // namespace internal
    285 }  // namespace ash
    286