Home | History | Annotate | Download | only in views
      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/views/painter.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "third_party/skia/include/effects/SkGradientShader.h"
     10 #include "ui/base/resource/resource_bundle.h"
     11 #include "ui/gfx/canvas.h"
     12 #include "ui/gfx/image/image.h"
     13 #include "ui/gfx/image/image_skia.h"
     14 #include "ui/gfx/image/image_skia_operations.h"
     15 #include "ui/gfx/insets.h"
     16 #include "ui/gfx/point.h"
     17 #include "ui/gfx/rect.h"
     18 
     19 
     20 namespace views {
     21 
     22 namespace {
     23 
     24 // GradientPainter ------------------------------------------------------------
     25 
     26 class GradientPainter : public Painter {
     27  public:
     28   GradientPainter(bool horizontal,
     29                   SkColor* colors,
     30                   SkScalar* pos,
     31                   size_t count);
     32   virtual ~GradientPainter();
     33 
     34   // Painter:
     35   virtual gfx::Size GetMinimumSize() const OVERRIDE;
     36   virtual void Paint(gfx::Canvas* canvas, const gfx::Size& size) OVERRIDE;
     37 
     38  private:
     39   // If |horizontal_| is true then the gradient is painted horizontally.
     40   bool horizontal_;
     41   // The gradient colors.
     42   scoped_ptr<SkColor[]> colors_;
     43   // The relative positions of the corresponding gradient colors.
     44   scoped_ptr<SkScalar[]> pos_;
     45   // The number of elements in |colors_| and |pos_|.
     46   size_t count_;
     47 
     48   DISALLOW_COPY_AND_ASSIGN(GradientPainter);
     49 };
     50 
     51 GradientPainter::GradientPainter(bool horizontal,
     52                                  SkColor* colors,
     53                                  SkScalar* pos,
     54                                  size_t count)
     55     : horizontal_(horizontal),
     56       colors_(new SkColor[count]),
     57       pos_(new SkScalar[count]),
     58       count_(count) {
     59   for (size_t i = 0; i < count_; ++i) {
     60     pos_[i] = pos[i];
     61     colors_[i] = colors[i];
     62   }
     63 }
     64 
     65 GradientPainter::~GradientPainter() {
     66 }
     67 
     68 gfx::Size GradientPainter::GetMinimumSize() const {
     69   return gfx::Size();
     70 }
     71 
     72 void GradientPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) {
     73   SkPaint paint;
     74   SkPoint p[2];
     75   p[0].iset(0, 0);
     76   if (horizontal_)
     77     p[1].iset(size.width(), 0);
     78   else
     79     p[1].iset(0, size.height());
     80 
     81   skia::RefPtr<SkShader> s = skia::AdoptRef(SkGradientShader::CreateLinear(
     82       p, colors_.get(), pos_.get(), count_, SkShader::kClamp_TileMode, NULL));
     83   paint.setStyle(SkPaint::kFill_Style);
     84   paint.setShader(s.get());
     85 
     86   canvas->sk_canvas()->drawRectCoords(SkIntToScalar(0), SkIntToScalar(0),
     87                                       SkIntToScalar(size.width()),
     88                                       SkIntToScalar(size.height()), paint);
     89 }
     90 
     91 
     92 // ImagePainter ---------------------------------------------------------------
     93 
     94 // ImagePainter stores and paints nine images as a scalable grid.
     95 class VIEWS_EXPORT ImagePainter : public Painter {
     96  public:
     97   // Constructs an ImagePainter with the specified image resource ids.
     98   // See CreateImageGridPainter()'s comment regarding image ID count and order.
     99   explicit ImagePainter(const int image_ids[]);
    100 
    101   // Constructs an ImagePainter with the specified image and insets.
    102   ImagePainter(const gfx::ImageSkia& image, const gfx::Insets& insets);
    103 
    104   virtual ~ImagePainter();
    105 
    106   // Returns true if the images are empty.
    107   bool IsEmpty() const;
    108 
    109   // Painter:
    110   virtual gfx::Size GetMinimumSize() const OVERRIDE;
    111   virtual void Paint(gfx::Canvas* canvas, const gfx::Size& size) OVERRIDE;
    112 
    113  private:
    114   // Stretches the given image over the specified canvas area.
    115   static void Fill(gfx::Canvas* c,
    116                    const gfx::ImageSkia& i,
    117                    int x,
    118                    int y,
    119                    int w,
    120                    int h);
    121 
    122   // Images are numbered as depicted below.
    123   //  ____________________
    124   // |__i0__|__i1__|__i2__|
    125   // |__i3__|__i4__|__i5__|
    126   // |__i6__|__i7__|__i8__|
    127   gfx::ImageSkia images_[9];
    128 
    129   DISALLOW_COPY_AND_ASSIGN(ImagePainter);
    130 };
    131 
    132 ImagePainter::ImagePainter(const int image_ids[]) {
    133   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    134   for (size_t i = 0; i < 9; ++i)
    135     if (image_ids[i] != 0)
    136       images_[i] = *rb.GetImageSkiaNamed(image_ids[i]);
    137 }
    138 
    139 ImagePainter::ImagePainter(const gfx::ImageSkia& image,
    140                            const gfx::Insets& insets) {
    141   DCHECK_GE(image.width(), insets.width());
    142   DCHECK_GE(image.height(), insets.height());
    143 
    144   // Extract subsets of the original image to match the |images_| format.
    145   const int x[] =
    146       { 0, insets.left(), image.width() - insets.right(), image.width() };
    147   const int y[] =
    148       { 0, insets.top(), image.height() - insets.bottom(), image.height() };
    149 
    150   for (size_t j = 0; j < 3; ++j) {
    151     for (size_t i = 0; i < 3; ++i) {
    152       images_[i + j * 3] = gfx::ImageSkiaOperations::ExtractSubset(image,
    153           gfx::Rect(x[i], y[j], x[i + 1] - x[i], y[j + 1] - y[j]));
    154     }
    155   }
    156 }
    157 
    158 ImagePainter::~ImagePainter() {
    159 }
    160 
    161 bool ImagePainter::IsEmpty() const {
    162   return images_[0].isNull();
    163 }
    164 
    165 gfx::Size ImagePainter::GetMinimumSize() const {
    166   return IsEmpty() ? gfx::Size() : gfx::Size(
    167       images_[0].width() + images_[1].width() + images_[2].width(),
    168       images_[0].height() + images_[3].height() + images_[6].height());
    169 }
    170 
    171 void ImagePainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) {
    172   if (IsEmpty())
    173     return;
    174 
    175   // In case the corners and edges don't all have the same width/height, we draw
    176   // the center first, and extend it out in all directions to the edges of the
    177   // images with the smallest widths/heights.  This way there will be no
    178   // unpainted areas, though some corners or edges might overlap the center.
    179   int w = size.width();
    180   int i0w = images_[0].width();
    181   int i2w = images_[2].width();
    182   int i3w = images_[3].width();
    183   int i5w = images_[5].width();
    184   int i6w = images_[6].width();
    185   int i8w = images_[8].width();
    186   int i4x = std::min(std::min(i0w, i3w), i6w);
    187   int i4w = w - i4x - std::min(std::min(i2w, i5w), i8w);
    188   int h = size.height();
    189   int i0h = images_[0].height();
    190   int i1h = images_[1].height();
    191   int i2h = images_[2].height();
    192   int i6h = images_[6].height();
    193   int i7h = images_[7].height();
    194   int i8h = images_[8].height();
    195   int i4y = std::min(std::min(i0h, i1h), i2h);
    196   int i4h = h - i4y - std::min(std::min(i6h, i7h), i8h);
    197   if (!images_[4].isNull())
    198     Fill(canvas, images_[4], i4x, i4y, i4w, i4h);
    199   canvas->DrawImageInt(images_[0], 0, 0);
    200   Fill(canvas, images_[1], i0w, 0, w - i0w - i2w, i1h);
    201   canvas->DrawImageInt(images_[2], w - i2w, 0);
    202   Fill(canvas, images_[3], 0, i0h, i3w, h - i0h - i6h);
    203   Fill(canvas, images_[5], w - i5w, i2h, i5w, h - i2h - i8h);
    204   canvas->DrawImageInt(images_[6], 0, h - i6h);
    205   Fill(canvas, images_[7], i6w, h - i7h, w - i6w - i8w, i7h);
    206   canvas->DrawImageInt(images_[8], w - i8w, h - i8h);
    207 }
    208 
    209 // static
    210 void ImagePainter::Fill(gfx::Canvas* c,
    211                         const gfx::ImageSkia& i,
    212                         int x,
    213                         int y,
    214                         int w,
    215                         int h) {
    216   c->DrawImageInt(i, 0, 0, i.width(), i.height(), x, y, w, h, false);
    217 }
    218 
    219 }  // namespace
    220 
    221 
    222 // Painter --------------------------------------------------------------------
    223 
    224 Painter::Painter() {
    225 }
    226 
    227 Painter::~Painter() {
    228 }
    229 
    230 // static
    231 void Painter::PaintPainterAt(gfx::Canvas* canvas,
    232                              Painter* painter,
    233                              const gfx::Rect& rect) {
    234   DCHECK(canvas && painter);
    235   canvas->Save();
    236   canvas->Translate(rect.OffsetFromOrigin());
    237   painter->Paint(canvas, rect.size());
    238   canvas->Restore();
    239 }
    240 
    241 // static
    242 Painter* Painter::CreateHorizontalGradient(SkColor c1, SkColor c2) {
    243   SkColor colors[2];
    244   colors[0] = c1;
    245   colors[1] = c2;
    246   SkScalar pos[] = {0, 1};
    247   return new GradientPainter(true, colors, pos, 2);
    248 }
    249 
    250 // static
    251 Painter* Painter::CreateVerticalGradient(SkColor c1, SkColor c2) {
    252   SkColor colors[2];
    253   colors[0] = c1;
    254   colors[1] = c2;
    255   SkScalar pos[] = {0, 1};
    256   return new GradientPainter(false, colors, pos, 2);
    257 }
    258 
    259 // static
    260 Painter* Painter::CreateVerticalMultiColorGradient(SkColor* colors,
    261                                                    SkScalar* pos,
    262                                                    size_t count) {
    263   return new GradientPainter(false, colors, pos, count);
    264 }
    265 
    266 // static
    267 Painter* Painter::CreateImagePainter(const gfx::ImageSkia& image,
    268                                      const gfx::Insets& insets) {
    269   return new ImagePainter(image, insets);
    270 }
    271 
    272 // static
    273 Painter* Painter::CreateImageGridPainter(const int image_ids[]) {
    274   return new ImagePainter(image_ids);
    275 }
    276 
    277 
    278 // HorizontalPainter ----------------------------------------------------------
    279 
    280 HorizontalPainter::HorizontalPainter(const int image_resource_names[]) {
    281   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    282   for (int i = 0; i < 3; ++i)
    283     images_[i] = rb.GetImageNamed(image_resource_names[i]).ToImageSkia();
    284   DCHECK_EQ(images_[LEFT]->height(), images_[CENTER]->height());
    285   DCHECK_EQ(images_[LEFT]->height(), images_[RIGHT]->height());
    286 }
    287 
    288 HorizontalPainter::~HorizontalPainter() {
    289 }
    290 
    291 gfx::Size HorizontalPainter::GetMinimumSize() const {
    292   return gfx::Size(
    293       images_[LEFT]->width() + images_[CENTER]->width() +
    294           images_[RIGHT]->width(), images_[LEFT]->height());
    295 }
    296 
    297 void HorizontalPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) {
    298   if (size.width() < GetMinimumSize().width())
    299     return;  // No room to paint.
    300 
    301   canvas->DrawImageInt(*images_[LEFT], 0, 0);
    302   canvas->DrawImageInt(*images_[RIGHT], size.width() - images_[RIGHT]->width(),
    303                        0);
    304   canvas->TileImageInt(
    305       *images_[CENTER], images_[LEFT]->width(), 0,
    306       size.width() - images_[LEFT]->width() - images_[RIGHT]->width(),
    307       images_[LEFT]->height());
    308 }
    309 
    310 }  // namespace views
    311