text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "ui/views/painter.h"
  
  #include <memory>
  
  #include "base/logging.h"
  #include "base/macros.h"
  #include "base/memory/ptr_util.h"
  #include "third_party/skia/include/effects/SkGradientShader.h"
  #include "ui/base/resource/resource_bundle.h"
  #include "ui/gfx/canvas.h"
  #include "ui/gfx/geometry/insets.h"
  #include "ui/gfx/geometry/point.h"
  #include "ui/gfx/geometry/rect_f.h"
  #include "ui/gfx/geometry/size.h"
  #include "ui/gfx/geometry/size_f.h"
  #include "ui/gfx/image/image.h"
  #include "ui/gfx/image/image_skia.h"
  #include "ui/gfx/image/image_skia_operations.h"
  #include "ui/gfx/nine_image_painter.h"
  #include "ui/views/view.h"
  
  namespace views {
  
  namespace {
  
  // SolidRoundRectPainter -------------------------------------------------------
  
  class SolidRoundRectPainter : public Painter {
   public:
    SolidRoundRectPainter(SkColor color, float radius);
    ~SolidRoundRectPainter() override;
  
    // Painter:
    gfx::Size GetMinimumSize() const override;
    void Paint(gfx::Canvas* canvas, const gfx::Size& size) override;
  
   private:
    const SkColor color_;
    const float radius_;
  
    DISALLOW_COPY_AND_ASSIGN(SolidRoundRectPainter);
  };
  
  SolidRoundRectPainter::SolidRoundRectPainter(SkColor color, float radius)
      : color_(color), radius_(radius) {}
  
  SolidRoundRectPainter::~SolidRoundRectPainter() {}
  
  gfx::Size SolidRoundRectPainter::GetMinimumSize() const {
    return gfx::Size();
  }
  
  void SolidRoundRectPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) {
    gfx::RectF rect((gfx::SizeF(size)));
    SkPaint paint;
    paint.setAntiAlias(true);
    paint.setColor(color_);
    canvas->DrawRoundRect(rect, radius_, paint);
  }
  
  // DashedFocusPainter ----------------------------------------------------------
  
  class DashedFocusPainter : public Painter {
   public:
    explicit DashedFocusPainter(const gfx::Insets& insets);
    ~DashedFocusPainter() override;
  
    // Painter:
    gfx::Size GetMinimumSize() const override;
    void Paint(gfx::Canvas* canvas, const gfx::Size& size) override;
  
   private:
    const gfx::Insets insets_;
  
    DISALLOW_COPY_AND_ASSIGN(DashedFocusPainter);
  };
  
  DashedFocusPainter::DashedFocusPainter(const gfx::Insets& insets)
      : insets_(insets) {
  }
  
  DashedFocusPainter::~DashedFocusPainter() {
  }
  
  gfx::Size DashedFocusPainter::GetMinimumSize() const {
    return gfx::Size();
  }
  
  void DashedFocusPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) {
    gfx::Rect rect(size);
    rect.Inset(insets_);
    canvas->DrawFocusRect(rect);
  }
  
  // SolidFocusPainter -----------------------------------------------------------
  
  class SolidFocusPainter : public Painter {
   public:
    SolidFocusPainter(SkColor color, const gfx::Insets& insets);
    ~SolidFocusPainter() override;
  
    // Painter:
    gfx::Size GetMinimumSize() const override;
    void Paint(gfx::Canvas* canvas, const gfx::Size& size) override;
  
   private:
    const SkColor color_;
    const gfx::Insets insets_;
  
    DISALLOW_COPY_AND_ASSIGN(SolidFocusPainter);
  };
  
  SolidFocusPainter::SolidFocusPainter(SkColor color,
                                       const gfx::Insets& insets)
      : color_(color),
        insets_(insets) {
  }
  
  SolidFocusPainter::~SolidFocusPainter() {
  }
  
  gfx::Size SolidFocusPainter::GetMinimumSize() const {
    return gfx::Size();
  }
  
  void SolidFocusPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) {
    gfx::Rect rect(size);
    rect.Inset(insets_);
    canvas->DrawSolidFocusRect(rect, color_);
  }
  
  // GradientPainter ------------------------------------------------------------
  
  class GradientPainter : public Painter {
   public:
    GradientPainter(bool horizontal,
                    SkColor* colors,
                    SkScalar* pos,
                    size_t count);
    ~GradientPainter() override;
  
    // Painter:
    gfx::Size GetMinimumSize() const override;
    void Paint(gfx::Canvas* canvas, const gfx::Size& size) override;
  
   private:
    // If |horizontal_| is true then the gradient is painted horizontally.
    bool horizontal_;
    // The gradient colors.
    std::unique_ptr<SkColor[]> colors_;
    // The relative positions of the corresponding gradient colors.
    std::unique_ptr<SkScalar[]> pos_;
    // The number of elements in |colors_| and |pos_|.
    size_t count_;
  
    DISALLOW_COPY_AND_ASSIGN(GradientPainter);
  };
  
  GradientPainter::GradientPainter(bool horizontal,
                                   SkColor* colors,
                                   SkScalar* pos,
                                   size_t count)
      : horizontal_(horizontal),
        colors_(new SkColor[count]),
        pos_(new SkScalar[count]),
        count_(count) {
    for (size_t i = 0; i < count_; ++i) {
      pos_[i] = pos[i];
      colors_[i] = colors[i];
    }
  }
  
  GradientPainter::~GradientPainter() {
  }
  
  gfx::Size GradientPainter::GetMinimumSize() const {
    return gfx::Size();
  }
  
  void GradientPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) {
    SkPaint paint;
    SkPoint p[2];
    p[0].iset(0, 0);
    if (horizontal_)
      p[1].iset(size.width(), 0);
    else
      p[1].iset(0, size.height());
  
    paint.setShader(SkGradientShader::MakeLinear(
        p, colors_.get(), pos_.get(), count_, SkShader::kClamp_TileMode));
    paint.setStyle(SkPaint::kFill_Style);
  
    canvas->sk_canvas()->drawRectCoords(SkIntToScalar(0), SkIntToScalar(0),
                                        SkIntToScalar(size.width()),
                                        SkIntToScalar(size.height()), paint);
  }
  
  // ImagePainter ---------------------------------------------------------------
  
  // ImagePainter stores and paints nine images as a scalable grid.
  class ImagePainter : public Painter {
   public:
    // Constructs an ImagePainter with the specified image resource ids.
    // See CreateImageGridPainter()'s comment regarding image ID count and order.
    explicit ImagePainter(const int image_ids[]);
  
    // Constructs an ImagePainter with the specified image and insets.
    ImagePainter(const gfx::ImageSkia& image, const gfx::Insets& insets);
  
    ~ImagePainter() override;
  
    // Painter:
    gfx::Size GetMinimumSize() const override;
    void Paint(gfx::Canvas* canvas, const gfx::Size& size) override;
  
   private:
    std::unique_ptr<gfx::NineImagePainter> nine_painter_;
  
    DISALLOW_COPY_AND_ASSIGN(ImagePainter);
  };
  
  ImagePainter::ImagePainter(const int image_ids[])
      : nine_painter_(ui::CreateNineImagePainter(image_ids)) {
  }
  
  ImagePainter::ImagePainter(const gfx::ImageSkia& image,
                             const gfx::Insets& insets)
      : nine_painter_(new gfx::NineImagePainter(image, insets)) {
  }
  
  ImagePainter::~ImagePainter() {
  }
  
  gfx::Size ImagePainter::GetMinimumSize() const {
    return nine_painter_->GetMinimumSize();
  }
  
  void ImagePainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) {
    nine_painter_->Paint(canvas, gfx::Rect(size));
  }
  
  }  // namespace
  
  
  // Painter --------------------------------------------------------------------
  
  Painter::Painter() {
  }
  
  Painter::~Painter() {
  }
  
  // static
  void Painter::PaintPainterAt(gfx::Canvas* canvas,
                               Painter* painter,
                               const gfx::Rect& rect) {
    DCHECK(canvas);
    DCHECK(painter);
    canvas->Save();
    canvas->Translate(rect.OffsetFromOrigin());
    painter->Paint(canvas, rect.size());
    canvas->Restore();
  }
  
  // static
  void Painter::PaintFocusPainter(View* view,
                                  gfx::Canvas* canvas,
                                  Painter* focus_painter) {
    if (focus_painter && view->HasFocus())
      PaintPainterAt(canvas, focus_painter, view->GetLocalBounds());
  }
  
  // static
  Painter* Painter::CreateSolidRoundRectPainter(SkColor color, float radius) {
    return new SolidRoundRectPainter(color, radius);
  }
  
  // static
  Painter* Painter::CreateHorizontalGradient(SkColor c1, SkColor c2) {
    SkColor colors[2];
    colors[0] = c1;
    colors[1] = c2;
    SkScalar pos[] = {0, 1};
    return new GradientPainter(true, colors, pos, 2);
  }
  
  // static
  Painter* Painter::CreateVerticalGradient(SkColor c1, SkColor c2) {
    SkColor colors[2];
    colors[0] = c1;
    colors[1] = c2;
    SkScalar pos[] = {0, 1};
    return new GradientPainter(false, colors, pos, 2);
  }
  
  // static
  Painter* Painter::CreateVerticalMultiColorGradient(SkColor* colors,
                                                     SkScalar* pos,
                                                     size_t count) {
    return new GradientPainter(false, colors, pos, count);
  }
  
  // static
  Painter* Painter::CreateImagePainter(const gfx::ImageSkia& image,
                                       const gfx::Insets& insets) {
    return new ImagePainter(image, insets);
  }
  
  // static
  Painter* Painter::CreateImageGridPainter(const int image_ids[]) {
    return new ImagePainter(image_ids);
  }
  
  // static
  std::unique_ptr<Painter> Painter::CreateDashedFocusPainter() {
    return base::WrapUnique(new DashedFocusPainter(gfx::Insets()));
  }
  
  // static
  std::unique_ptr<Painter> Painter::CreateDashedFocusPainterWithInsets(
      const gfx::Insets& insets) {
    return base::WrapUnique(new DashedFocusPainter(insets));
  }
  
  // static
  std::unique_ptr<Painter> Painter::CreateSolidFocusPainter(
      SkColor color,
      const gfx::Insets& insets) {
    return base::WrapUnique(new SolidFocusPainter(color, insets));
  }
  
  // HorizontalPainter ----------------------------------------------------------
  
  HorizontalPainter::HorizontalPainter(const int image_resource_names[]) {
    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    for (int i = 0; i < 3; ++i)
      images_[i] = rb.GetImageNamed(image_resource_names[i]).ToImageSkia();
    DCHECK_EQ(images_[LEFT]->height(), images_[CENTER]->height());
    DCHECK_EQ(images_[LEFT]->height(), images_[RIGHT]->height());
  }
  
  HorizontalPainter::~HorizontalPainter() {
  }
  
  gfx::Size HorizontalPainter::GetMinimumSize() const {
    return gfx::Size(
        images_[LEFT]->width() + images_[CENTER]->width() +
            images_[RIGHT]->width(), images_[LEFT]->height());
  }
  
  void HorizontalPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) {
    if (size.width() < GetMinimumSize().width())
      return;  // No room to paint.
  
    canvas->DrawImageInt(*images_[LEFT], 0, 0);
    canvas->DrawImageInt(*images_[RIGHT], size.width() - images_[RIGHT]->width(),
                         0);
    canvas->TileImageInt(
        *images_[CENTER], images_[LEFT]->width(), 0,
        size.width() - images_[LEFT]->width() - images_[RIGHT]->width(),
        images_[LEFT]->height());
  }
  
  }  // namespace views
  