Home | History | Annotate | Download | only in core
      1 // Copyright (c) 2012 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 "ui/wm/core/image_grid.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "third_party/skia/include/core/SkColor.h"
     10 #include "third_party/skia/include/core/SkXfermode.h"
     11 #include "ui/compositor/dip_util.h"
     12 #include "ui/gfx/canvas.h"
     13 #include "ui/gfx/image/image.h"
     14 #include "ui/gfx/image/image_skia_operations.h"
     15 #include "ui/gfx/rect.h"
     16 #include "ui/gfx/rect_conversions.h"
     17 #include "ui/gfx/size.h"
     18 #include "ui/gfx/size_conversions.h"
     19 #include "ui/gfx/transform.h"
     20 
     21 using std::max;
     22 using std::min;
     23 
     24 namespace wm {
     25 namespace {
     26 
     27 // Sets the scaling for the transform applied to a layer.  The left, top,
     28 // right and bottom layers are stretched to the height or width of the
     29 // center image.
     30 
     31 void ScaleWidth(gfx::Size center, ui::Layer* layer, gfx::Transform& transform) {
     32   float layer_width = layer->bounds().width() * layer->device_scale_factor();
     33   float scale = static_cast<float>(center.width()) / layer_width;
     34   transform.Scale(scale, 1.0);
     35 }
     36 
     37 void ScaleHeight(gfx::Size center,
     38                  ui::Layer* layer,
     39                  gfx::Transform& transform) {
     40   float layer_height = layer->bounds().height() * layer->device_scale_factor();
     41   float scale = static_cast<float>(center.height()) / layer_height;
     42   transform.Scale(1.0, scale);
     43 }
     44 
     45 // Returns the dimensions of |image| if non-NULL or gfx::Size(0, 0) otherwise.
     46 gfx::Size GetImageSize(const gfx::Image* image) {
     47   return image ? gfx::Size(image->ToImageSkia()->width(),
     48                            image->ToImageSkia()->height())
     49                : gfx::Size();
     50 }
     51 
     52 // Returns true if |layer|'s bounds don't fit within |size|.
     53 bool LayerExceedsSize(const ui::Layer* layer, const gfx::Size& size) {
     54   return layer->bounds().width() > size.width() ||
     55          layer->bounds().height() > size.height();
     56 }
     57 
     58 }  // namespace
     59 
     60 gfx::RectF ImageGrid::TestAPI::GetTransformedLayerBounds(
     61     const ui::Layer& layer) {
     62   gfx::RectF bounds = layer.bounds();
     63   layer.transform().TransformRect(&bounds);
     64   return bounds;
     65 }
     66 
     67 ImageGrid::ImageGrid()
     68     : layer_(new ui::Layer(ui::LAYER_NOT_DRAWN)),
     69       top_image_height_(0),
     70       bottom_image_height_(0),
     71       left_image_width_(0),
     72       right_image_width_(0),
     73       base_top_row_height_(0),
     74       base_bottom_row_height_(0),
     75       base_left_column_width_(0),
     76       base_right_column_width_(0) {
     77 }
     78 
     79 ImageGrid::~ImageGrid() {
     80 }
     81 
     82 void ImageGrid::SetImages(const gfx::Image* top_left_image,
     83                           const gfx::Image* top_image,
     84                           const gfx::Image* top_right_image,
     85                           const gfx::Image* left_image,
     86                           const gfx::Image* center_image,
     87                           const gfx::Image* right_image,
     88                           const gfx::Image* bottom_left_image,
     89                           const gfx::Image* bottom_image,
     90                           const gfx::Image* bottom_right_image) {
     91   SetImage(top_left_image, &top_left_layer_, &top_left_painter_, NONE);
     92   SetImage(top_image, &top_layer_, &top_painter_, HORIZONTAL);
     93   SetImage(top_right_image, &top_right_layer_, &top_right_painter_, NONE);
     94   SetImage(left_image, &left_layer_, &left_painter_, VERTICAL);
     95   SetImage(center_image, &center_layer_, &center_painter_, NONE);
     96   SetImage(right_image, &right_layer_, &right_painter_, VERTICAL);
     97   SetImage(bottom_left_image, &bottom_left_layer_, &bottom_left_painter_, NONE);
     98   SetImage(bottom_image, &bottom_layer_, &bottom_painter_, HORIZONTAL);
     99   SetImage(
    100       bottom_right_image, &bottom_right_layer_, &bottom_right_painter_, NONE);
    101 
    102   top_image_height_ = GetImageSize(top_image).height();
    103   bottom_image_height_ = GetImageSize(bottom_image).height();
    104   left_image_width_ = GetImageSize(left_image).width();
    105   right_image_width_ = GetImageSize(right_image).width();
    106 
    107   base_top_row_height_ = max(GetImageSize(top_left_image).height(),
    108                              max(GetImageSize(top_image).height(),
    109                                  GetImageSize(top_right_image).height()));
    110   base_bottom_row_height_ = max(GetImageSize(bottom_left_image).height(),
    111                                 max(GetImageSize(bottom_image).height(),
    112                                     GetImageSize(bottom_right_image).height()));
    113   base_left_column_width_ = max(GetImageSize(top_left_image).width(),
    114                                 max(GetImageSize(left_image).width(),
    115                                     GetImageSize(bottom_left_image).width()));
    116   base_right_column_width_ = max(GetImageSize(top_right_image).width(),
    117                                  max(GetImageSize(right_image).width(),
    118                                      GetImageSize(bottom_right_image).width()));
    119 
    120   // Invalidate previous |size_| so calls to SetSize() will recompute it.
    121   size_.SetSize(0, 0);
    122 }
    123 
    124 void ImageGrid::SetSize(const gfx::Size& size) {
    125   if (size_ == size)
    126     return;
    127 
    128   size_ = size;
    129 
    130   gfx::Rect updated_bounds = layer_->bounds();
    131   updated_bounds.set_size(size);
    132   layer_->SetBounds(updated_bounds);
    133 
    134   // Calculate the available amount of space for corner images on all sides of
    135   // the grid.  If the images don't fit, we need to clip them.
    136   const int left = min(base_left_column_width_, size_.width() / 2);
    137   const int right = min(base_right_column_width_, size_.width() - left);
    138   const int top = min(base_top_row_height_, size_.height() / 2);
    139   const int bottom = min(base_bottom_row_height_, size_.height() - top);
    140 
    141   // The remaining space goes to the center image.
    142   int center_width = std::max(size.width() - left - right, 0);
    143   int center_height = std::max(size.height() - top - bottom, 0);
    144 
    145   // At non-integer scale factors, the ratio of dimensions in DIP is not
    146   // necessarily the same as the ratio in physical pixels due to rounding.  Set
    147   // the transform on each of the layers based on dimensions in pixels.
    148   gfx::Size center_size_in_pixels = gfx::ToFlooredSize(gfx::ScaleSize(
    149       gfx::Size(center_width, center_height), layer_->device_scale_factor()));
    150 
    151   if (top_layer_.get()) {
    152     if (center_width > 0) {
    153       gfx::Transform transform;
    154       transform.Translate(left, 0);
    155       ScaleWidth(center_size_in_pixels, top_layer_.get(), transform);
    156       top_layer_->SetTransform(transform);
    157     }
    158     top_layer_->SetVisible(center_width > 0);
    159   }
    160   if (bottom_layer_.get()) {
    161     if (center_width > 0) {
    162       gfx::Transform transform;
    163       transform.Translate(
    164           left, size.height() - bottom_layer_->bounds().height());
    165       ScaleWidth(center_size_in_pixels, bottom_layer_.get(), transform);
    166       bottom_layer_->SetTransform(transform);
    167     }
    168     bottom_layer_->SetVisible(center_width > 0);
    169   }
    170   if (left_layer_.get()) {
    171     if (center_height > 0) {
    172       gfx::Transform transform;
    173       transform.Translate(0, top);
    174       ScaleHeight(center_size_in_pixels, left_layer_.get(), transform);
    175       left_layer_->SetTransform(transform);
    176     }
    177     left_layer_->SetVisible(center_height > 0);
    178   }
    179   if (right_layer_.get()) {
    180     if (center_height > 0) {
    181       gfx::Transform transform;
    182       transform.Translate(
    183           size.width() - right_layer_->bounds().width(), top);
    184       ScaleHeight(center_size_in_pixels, right_layer_.get(), transform);
    185       right_layer_->SetTransform(transform);
    186     }
    187     right_layer_->SetVisible(center_height > 0);
    188   }
    189 
    190   if (top_left_layer_.get()) {
    191     // No transformation needed; it should be at (0, 0) and unscaled.
    192     top_left_painter_->SetClipRect(
    193         LayerExceedsSize(top_left_layer_.get(), gfx::Size(left, top)) ?
    194             gfx::Rect(gfx::Rect(0, 0, left, top)) :
    195             gfx::Rect(),
    196         top_left_layer_.get());
    197   }
    198   if (top_right_layer_.get()) {
    199     gfx::Transform transform;
    200     transform.Translate(size.width() - top_right_layer_->bounds().width(), 0.0);
    201     top_right_layer_->SetTransform(transform);
    202     top_right_painter_->SetClipRect(
    203         LayerExceedsSize(top_right_layer_.get(), gfx::Size(right, top)) ?
    204             gfx::Rect(top_right_layer_->bounds().width() - right, 0,
    205                       right, top) :
    206             gfx::Rect(),
    207         top_right_layer_.get());
    208   }
    209   if (bottom_left_layer_.get()) {
    210     gfx::Transform transform;
    211     transform.Translate(
    212         0.0, size.height() - bottom_left_layer_->bounds().height());
    213     bottom_left_layer_->SetTransform(transform);
    214     bottom_left_painter_->SetClipRect(
    215         LayerExceedsSize(bottom_left_layer_.get(), gfx::Size(left, bottom)) ?
    216             gfx::Rect(0, bottom_left_layer_->bounds().height() - bottom,
    217                       left, bottom) :
    218             gfx::Rect(),
    219         bottom_left_layer_.get());
    220   }
    221   if (bottom_right_layer_.get()) {
    222     gfx::Transform transform;
    223     transform.Translate(
    224         size.width() - bottom_right_layer_->bounds().width(),
    225         size.height() - bottom_right_layer_->bounds().height());
    226     bottom_right_layer_->SetTransform(transform);
    227     bottom_right_painter_->SetClipRect(
    228         LayerExceedsSize(bottom_right_layer_.get(), gfx::Size(right, bottom)) ?
    229             gfx::Rect(bottom_right_layer_->bounds().width() - right,
    230                       bottom_right_layer_->bounds().height() - bottom,
    231                       right, bottom) :
    232             gfx::Rect(),
    233         bottom_right_layer_.get());
    234   }
    235 
    236   if (center_layer_.get()) {
    237     if (center_width > 0 && center_height > 0) {
    238       gfx::Transform transform;
    239       transform.Translate(left, top);
    240       transform.Scale(center_width / center_layer_->bounds().width(),
    241                       center_height / center_layer_->bounds().height());
    242       center_layer_->SetTransform(transform);
    243     }
    244     center_layer_->SetVisible(center_width > 0 && center_height > 0);
    245   }
    246 }
    247 
    248 void ImageGrid::SetContentBounds(const gfx::Rect& content_bounds) {
    249 
    250   SetSize(gfx::Size(
    251       content_bounds.width() + left_image_width_ + right_image_width_,
    252       content_bounds.height() + top_image_height_ +
    253       bottom_image_height_));
    254   layer_->SetBounds(
    255       gfx::Rect(content_bounds.x() - left_image_width_,
    256                 content_bounds.y() - top_image_height_,
    257                 layer_->bounds().width(),
    258                 layer_->bounds().height()));
    259 }
    260 
    261 void ImageGrid::ImagePainter::SetClipRect(const gfx::Rect& clip_rect,
    262                                           ui::Layer* layer) {
    263   if (clip_rect != clip_rect_) {
    264     clip_rect_ = clip_rect;
    265     layer->SchedulePaint(layer->bounds());
    266   }
    267 }
    268 
    269 void ImageGrid::ImagePainter::OnPaintLayer(gfx::Canvas* canvas) {
    270   if (!clip_rect_.IsEmpty())
    271     canvas->ClipRect(clip_rect_);
    272   canvas->DrawImageInt(image_, 0, 0);
    273 }
    274 
    275 void ImageGrid::ImagePainter::OnDelegatedFrameDamage(
    276     const gfx::Rect& damage_rect_in_dip) {
    277 }
    278 
    279 void ImageGrid::ImagePainter::OnDeviceScaleFactorChanged(
    280     float device_scale_factor) {
    281   // Redrawing will take care of scale factor change.
    282 }
    283 
    284 base::Closure ImageGrid::ImagePainter::PrepareForLayerBoundsChange() {
    285   return base::Closure();
    286 }
    287 
    288 void ImageGrid::SetImage(const gfx::Image* image,
    289                          scoped_ptr<ui::Layer>* layer_ptr,
    290                          scoped_ptr<ImagePainter>* painter_ptr,
    291                          ImageType type) {
    292   // Minimum width (for HORIZONTAL) or height (for VERTICAL) of the
    293   // |image| so that layers are scaled property if the device scale
    294   // factor is non integral.
    295   const int kMinimumSize = 20;
    296 
    297   // Clean out old layers and painters.
    298   if (layer_ptr->get())
    299     layer_->Remove(layer_ptr->get());
    300   layer_ptr->reset();
    301   painter_ptr->reset();
    302 
    303   // If we're not using an image, we're done.
    304   if (!image)
    305     return;
    306 
    307   gfx::ImageSkia image_skia = image->AsImageSkia();
    308   switch (type) {
    309     case HORIZONTAL:
    310       if (image_skia.width() < kMinimumSize) {
    311         image_skia = gfx::ImageSkiaOperations::CreateResizedImage(
    312             image_skia,
    313             skia::ImageOperations::RESIZE_GOOD,
    314             gfx::Size(kMinimumSize, image_skia.height()));
    315       }
    316       break;
    317     case VERTICAL:
    318       if (image_skia.height() < kMinimumSize) {
    319         image_skia = gfx::ImageSkiaOperations::CreateResizedImage(
    320             image_skia,
    321             skia::ImageOperations::RESIZE_GOOD,
    322             gfx::Size(image_skia.width(), kMinimumSize));
    323       }
    324       break;
    325     case NONE:
    326       break;
    327   }
    328 
    329   // Set up the new layer and painter.
    330   layer_ptr->reset(new ui::Layer(ui::LAYER_TEXTURED));
    331 
    332   const gfx::Size size = image_skia.size();
    333   layer_ptr->get()->SetBounds(gfx::Rect(0, 0, size.width(), size.height()));
    334 
    335   painter_ptr->reset(new ImagePainter(image_skia));
    336   layer_ptr->get()->set_delegate(painter_ptr->get());
    337   layer_ptr->get()->SetFillsBoundsOpaquely(false);
    338   layer_ptr->get()->SetVisible(true);
    339   layer_->Add(layer_ptr->get());
    340 }
    341 
    342 }  // namespace wm
    343