blob: fe591b76fe4f362a7a3be737f0ea19e9d7cba514 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// 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 <utility>
#include "base/check.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_delegate.h"
#include "ui/compositor/layer_owner.h"
#include "ui/compositor/paint_recorder.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/insets_f.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/nine_image_painter.h"
#include "ui/gfx/scoped_canvas.h"
#include "ui/views/view.h"
namespace views {
namespace {
// SolidRoundRectPainter -------------------------------------------------------
// Creates a round rect painter with a 1 pixel border. The border paints on top
// of the background.
class SolidRoundRectPainter : public Painter {
public:
SolidRoundRectPainter(SkColor bg_color,
SkColor stroke_color,
gfx::RoundedCornersF radii,
const gfx::Insets& insets,
SkBlendMode blend_mode,
bool antialias,
bool should_border_scale);
SolidRoundRectPainter(const SolidRoundRectPainter&) = delete;
SolidRoundRectPainter& operator=(const SolidRoundRectPainter&) = delete;
~SolidRoundRectPainter() override;
// Painter:
gfx::Size GetMinimumSize() const override;
void Paint(gfx::Canvas* canvas, const gfx::Size& size) override;
private:
const SkColor bg_color_;
const SkColor stroke_color_;
const gfx::RoundedCornersF radii_;
const gfx::Insets insets_;
const SkBlendMode blend_mode_;
const bool antialias_;
const bool should_border_scale_;
};
SolidRoundRectPainter::SolidRoundRectPainter(SkColor bg_color,
SkColor stroke_color,
gfx::RoundedCornersF radii,
const gfx::Insets& insets,
SkBlendMode blend_mode,
bool antialias,
bool should_border_scale)
: bg_color_(bg_color),
stroke_color_(stroke_color),
radii_(radii),
insets_(insets),
blend_mode_(blend_mode),
antialias_(antialias),
should_border_scale_(should_border_scale) {}
SolidRoundRectPainter::~SolidRoundRectPainter() = default;
gfx::Size SolidRoundRectPainter::GetMinimumSize() const {
return gfx::Size();
}
void SolidRoundRectPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) {
gfx::ScopedCanvas scoped_canvas(canvas);
const float scale = canvas->UndoDeviceScaleFactor();
gfx::Rect inset_rect(size);
inset_rect.Inset(insets_);
gfx::RectF fill_rect(gfx::ScaleToEnclosedRect(inset_rect, scale));
gfx::RectF stroke_rect = fill_rect;
cc::PaintFlags flags;
flags.setBlendMode(blend_mode_);
if (antialias_)
flags.setAntiAlias(true);
flags.setStyle(cc::PaintFlags::kFill_Style);
flags.setColor(bg_color_);
SkPath fill_path;
const SkScalar scaled_radii[8] = {
radii_.upper_left() * scale, radii_.upper_left() * scale,
radii_.upper_right() * scale, radii_.upper_right() * scale,
radii_.lower_right() * scale, radii_.lower_right() * scale,
radii_.lower_left() * scale, radii_.lower_left() * scale};
fill_path.addRoundRect(gfx::RectFToSkRect(fill_rect), scaled_radii);
canvas->DrawPath(fill_path, flags);
if (stroke_color_ != SK_ColorTRANSPARENT) {
const float stroke_width = should_border_scale_ ? scale : 1.0f;
const float stroke_inset = stroke_width / 2;
stroke_rect.Inset(gfx::InsetsF(stroke_inset));
flags.setStyle(cc::PaintFlags::kStroke_Style);
flags.setStrokeWidth(stroke_width);
flags.setColor(stroke_color_);
SkPath stroke_path;
SkScalar stroke_radii[8] = {};
for (int i = 0; i < 8; i++) {
stroke_radii[i] = scaled_radii[i] - stroke_width / 2;
}
stroke_path.addRoundRect(gfx::RectFToSkRect(stroke_rect), stroke_radii);
canvas->DrawPath(stroke_path, flags);
}
}
// SolidFocusPainter -----------------------------------------------------------
class SolidFocusPainter : public Painter {
public:
SolidFocusPainter(SkColor color, int thickness, const gfx::InsetsF& insets);
SolidFocusPainter(const SolidFocusPainter&) = delete;
SolidFocusPainter& operator=(const SolidFocusPainter&) = delete;
~SolidFocusPainter() override;
// Painter:
gfx::Size GetMinimumSize() const override;
void Paint(gfx::Canvas* canvas, const gfx::Size& size) override;
private:
const SkColor color_;
const int thickness_;
const gfx::InsetsF insets_;
};
SolidFocusPainter::SolidFocusPainter(SkColor color,
int thickness,
const gfx::InsetsF& insets)
: color_(color), thickness_(thickness), insets_(insets) {}
SolidFocusPainter::~SolidFocusPainter() = default;
gfx::Size SolidFocusPainter::GetMinimumSize() const {
return gfx::Size();
}
void SolidFocusPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) {
gfx::RectF rect((gfx::Rect(size)));
rect.Inset(insets_);
canvas->DrawSolidFocusRect(rect, color_, thickness_);
}
// 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(const ImagePainter&) = delete;
ImagePainter& operator=(const ImagePainter&) = delete;
~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_;
};
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() = default;
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));
}
class PaintedLayer : public ui::LayerOwner, public ui::LayerDelegate {
public:
explicit PaintedLayer(std::unique_ptr<Painter> painter);
PaintedLayer(const PaintedLayer&) = delete;
PaintedLayer& operator=(const PaintedLayer&) = delete;
~PaintedLayer() override;
// LayerDelegate:
void OnPaintLayer(const ui::PaintContext& context) override;
void OnDeviceScaleFactorChanged(float old_device_scale_factor,
float new_device_scale_factor) override;
private:
std::unique_ptr<Painter> painter_;
};
PaintedLayer::PaintedLayer(std::unique_ptr<Painter> painter)
: painter_(std::move(painter)) {
SetLayer(std::make_unique<ui::Layer>(ui::LAYER_TEXTURED));
layer()->set_delegate(this);
}
PaintedLayer::~PaintedLayer() = default;
void PaintedLayer::OnPaintLayer(const ui::PaintContext& context) {
ui::PaintRecorder recorder(context, layer()->size());
painter_->Paint(recorder.canvas(), layer()->size());
}
void PaintedLayer::OnDeviceScaleFactorChanged(float old_device_scale_factor,
float new_device_scale_factor) {}
} // namespace
// Painter --------------------------------------------------------------------
Painter::Painter() = default;
Painter::~Painter() = default;
// 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
std::unique_ptr<Painter> Painter::CreateSolidRoundRectPainter(
SkColor color,
float radius,
const gfx::Insets& insets,
SkBlendMode blend_mode,
bool antialias) {
return std::make_unique<SolidRoundRectPainter>(
color, SK_ColorTRANSPARENT, gfx::RoundedCornersF(radius), insets,
blend_mode, antialias, false);
}
// static
std::unique_ptr<Painter> Painter::CreateSolidRoundRectPainterWithVariableRadius(
SkColor color,
gfx::RoundedCornersF radii,
const gfx::Insets& insets,
SkBlendMode blend_mode,
bool antialias) {
return std::make_unique<SolidRoundRectPainter>(
color, SK_ColorTRANSPARENT, radii, insets, blend_mode, antialias, false);
}
// static
std::unique_ptr<Painter> Painter::CreateRoundRectWith1PxBorderPainter(
SkColor bg_color,
SkColor stroke_color,
float radius,
SkBlendMode blend_mode,
bool antialias,
bool should_border_scale) {
return std::make_unique<SolidRoundRectPainter>(
bg_color, stroke_color, gfx::RoundedCornersF(radius), gfx::Insets(),
blend_mode, antialias, should_border_scale);
}
// static
std::unique_ptr<Painter> Painter::CreateImagePainter(
const gfx::ImageSkia& image,
const gfx::Insets& insets) {
return std::make_unique<ImagePainter>(image, insets);
}
// static
std::unique_ptr<Painter> Painter::CreateImageGridPainter(
const int image_ids[]) {
return std::make_unique<ImagePainter>(image_ids);
}
// static
std::unique_ptr<Painter> Painter::CreateSolidFocusPainter(
SkColor color,
const gfx::Insets& insets) {
// Before Canvas::DrawSolidFocusRect correctly inset the rect's bounds based
// on the thickness, callers had to add 1 to the bottom and right insets.
// Subtract that here so it works the same way with the new
// Canvas::DrawSolidFocusRect.
const gfx::InsetsF corrected_insets(insets - gfx::Insets::TLBR(0, 0, 1, 1));
return std::make_unique<SolidFocusPainter>(color, 1, corrected_insets);
}
// static
std::unique_ptr<Painter> Painter::CreateSolidFocusPainter(
SkColor color,
int thickness,
const gfx::InsetsF& insets) {
return std::make_unique<SolidFocusPainter>(color, thickness, insets);
}
// static
std::unique_ptr<ui::LayerOwner> Painter::CreatePaintedLayer(
std::unique_ptr<Painter> painter) {
return std::make_unique<PaintedLayer>(std::move(painter));
}
} // namespace views