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 <utility>
8
9 #include "base/logging.h"
10 #include "ui/compositor/layer.h"
11 #include "ui/compositor/layer_delegate.h"
12 #include "ui/compositor/layer_owner.h"
13 #include "ui/compositor/paint_recorder.h"
14 #include "ui/gfx/canvas.h"
15 #include "ui/gfx/geometry/insets.h"
16 #include "ui/gfx/geometry/insets_f.h"
17 #include "ui/gfx/geometry/rect_f.h"
18 #include "ui/gfx/geometry/size.h"
19 #include "ui/gfx/nine_image_painter.h"
20 #include "ui/gfx/scoped_canvas.h"
21 #include "ui/views/view.h"
22
23 namespace views {
24
25 namespace {
26
27 // SolidRoundRectPainter -------------------------------------------------------
28
29 // Creates a round rect painter with a 1 pixel border. The border paints on top
30 // of the background.
31 class SolidRoundRectPainter : public Painter {
32 public:
33 SolidRoundRectPainter(SkColor bg_color,
34 SkColor stroke_color,
35 float radius,
36 const gfx::Insets& insets,
37 SkBlendMode blend_mode,
38 bool antialias);
39 ~SolidRoundRectPainter() override;
40
41 // Painter:
42 gfx::Size GetMinimumSize() const override;
43 void Paint(gfx::Canvas* canvas, const gfx::Size& size) override;
44
45 private:
46 const SkColor bg_color_;
47 const SkColor stroke_color_;
48 const float radius_;
49 const gfx::Insets insets_;
50 const SkBlendMode blend_mode_;
51 const bool antialias_;
52
53 DISALLOW_COPY_AND_ASSIGN(SolidRoundRectPainter);
54 };
55
SolidRoundRectPainter(SkColor bg_color,SkColor stroke_color,float radius,const gfx::Insets & insets,SkBlendMode blend_mode,bool antialias)56 SolidRoundRectPainter::SolidRoundRectPainter(SkColor bg_color,
57 SkColor stroke_color,
58 float radius,
59 const gfx::Insets& insets,
60 SkBlendMode blend_mode,
61 bool antialias)
62 : bg_color_(bg_color),
63 stroke_color_(stroke_color),
64 radius_(radius),
65 insets_(insets),
66 blend_mode_(blend_mode),
67 antialias_(antialias) {}
68
69 SolidRoundRectPainter::~SolidRoundRectPainter() = default;
70
GetMinimumSize() const71 gfx::Size SolidRoundRectPainter::GetMinimumSize() const {
72 return gfx::Size();
73 }
74
Paint(gfx::Canvas * canvas,const gfx::Size & size)75 void SolidRoundRectPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) {
76 gfx::ScopedCanvas scoped_canvas(canvas);
77 const float scale = canvas->UndoDeviceScaleFactor();
78
79 gfx::Rect inset_rect(size);
80 inset_rect.Inset(insets_);
81 gfx::RectF fill_rect(gfx::ScaleToEnclosingRect(inset_rect, scale));
82 gfx::RectF stroke_rect = fill_rect;
83 float scaled_radius = radius_ * scale;
84
85 cc::PaintFlags flags;
86 flags.setBlendMode(blend_mode_);
87 if (antialias_)
88 flags.setAntiAlias(true);
89 flags.setStyle(cc::PaintFlags::kFill_Style);
90 flags.setColor(bg_color_);
91 canvas->DrawRoundRect(fill_rect, scaled_radius, flags);
92
93 if (stroke_color_ != SK_ColorTRANSPARENT) {
94 constexpr float kStrokeWidth = 1.0f;
95 stroke_rect.Inset(gfx::InsetsF(kStrokeWidth / 2));
96 scaled_radius -= kStrokeWidth / 2;
97 flags.setStyle(cc::PaintFlags::kStroke_Style);
98 flags.setStrokeWidth(kStrokeWidth);
99 flags.setColor(stroke_color_);
100 canvas->DrawRoundRect(stroke_rect, scaled_radius, flags);
101 }
102 }
103
104 // SolidFocusPainter -----------------------------------------------------------
105
106 class SolidFocusPainter : public Painter {
107 public:
108 SolidFocusPainter(SkColor color, int thickness, const gfx::InsetsF& insets);
109 ~SolidFocusPainter() override;
110
111 // Painter:
112 gfx::Size GetMinimumSize() const override;
113 void Paint(gfx::Canvas* canvas, const gfx::Size& size) override;
114
115 private:
116 const SkColor color_;
117 const int thickness_;
118 const gfx::InsetsF insets_;
119
120 DISALLOW_COPY_AND_ASSIGN(SolidFocusPainter);
121 };
122
SolidFocusPainter(SkColor color,int thickness,const gfx::InsetsF & insets)123 SolidFocusPainter::SolidFocusPainter(SkColor color,
124 int thickness,
125 const gfx::InsetsF& insets)
126 : color_(color), thickness_(thickness), insets_(insets) {}
127
128 SolidFocusPainter::~SolidFocusPainter() = default;
129
GetMinimumSize() const130 gfx::Size SolidFocusPainter::GetMinimumSize() const {
131 return gfx::Size();
132 }
133
Paint(gfx::Canvas * canvas,const gfx::Size & size)134 void SolidFocusPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) {
135 gfx::RectF rect((gfx::Rect(size)));
136 rect.Inset(insets_);
137 canvas->DrawSolidFocusRect(rect, color_, thickness_);
138 }
139
140 // ImagePainter ---------------------------------------------------------------
141
142 // ImagePainter stores and paints nine images as a scalable grid.
143 class ImagePainter : public Painter {
144 public:
145 // Constructs an ImagePainter with the specified image resource ids.
146 // See CreateImageGridPainter()'s comment regarding image ID count and order.
147 explicit ImagePainter(const int image_ids[]);
148
149 // Constructs an ImagePainter with the specified image and insets.
150 ImagePainter(const gfx::ImageSkia& image, const gfx::Insets& insets);
151
152 ~ImagePainter() override;
153
154 // Painter:
155 gfx::Size GetMinimumSize() const override;
156 void Paint(gfx::Canvas* canvas, const gfx::Size& size) override;
157
158 private:
159 std::unique_ptr<gfx::NineImagePainter> nine_painter_;
160
161 DISALLOW_COPY_AND_ASSIGN(ImagePainter);
162 };
163
ImagePainter(const int image_ids[])164 ImagePainter::ImagePainter(const int image_ids[])
165 : nine_painter_(ui::CreateNineImagePainter(image_ids)) {}
166
ImagePainter(const gfx::ImageSkia & image,const gfx::Insets & insets)167 ImagePainter::ImagePainter(const gfx::ImageSkia& image,
168 const gfx::Insets& insets)
169 : nine_painter_(new gfx::NineImagePainter(image, insets)) {}
170
171 ImagePainter::~ImagePainter() = default;
172
GetMinimumSize() const173 gfx::Size ImagePainter::GetMinimumSize() const {
174 return nine_painter_->GetMinimumSize();
175 }
176
Paint(gfx::Canvas * canvas,const gfx::Size & size)177 void ImagePainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) {
178 nine_painter_->Paint(canvas, gfx::Rect(size));
179 }
180
181 class PaintedLayer : public ui::LayerOwner, public ui::LayerDelegate {
182 public:
183 explicit PaintedLayer(std::unique_ptr<Painter> painter);
184 ~PaintedLayer() override;
185
186 // LayerDelegate:
187 void OnPaintLayer(const ui::PaintContext& context) override;
188 void OnDeviceScaleFactorChanged(float old_device_scale_factor,
189 float new_device_scale_factor) override;
190
191 private:
192 std::unique_ptr<Painter> painter_;
193
194 DISALLOW_COPY_AND_ASSIGN(PaintedLayer);
195 };
196
PaintedLayer(std::unique_ptr<Painter> painter)197 PaintedLayer::PaintedLayer(std::unique_ptr<Painter> painter)
198 : painter_(std::move(painter)) {
199 SetLayer(std::make_unique<ui::Layer>(ui::LAYER_TEXTURED));
200 layer()->set_delegate(this);
201 }
202
203 PaintedLayer::~PaintedLayer() = default;
204
OnPaintLayer(const ui::PaintContext & context)205 void PaintedLayer::OnPaintLayer(const ui::PaintContext& context) {
206 ui::PaintRecorder recorder(context, layer()->size());
207 painter_->Paint(recorder.canvas(), layer()->size());
208 }
209
OnDeviceScaleFactorChanged(float old_device_scale_factor,float new_device_scale_factor)210 void PaintedLayer::OnDeviceScaleFactorChanged(float old_device_scale_factor,
211 float new_device_scale_factor) {}
212
213 } // namespace
214
215 // Painter --------------------------------------------------------------------
216
217 Painter::Painter() = default;
218
219 Painter::~Painter() = default;
220
221 // static
PaintPainterAt(gfx::Canvas * canvas,Painter * painter,const gfx::Rect & rect)222 void Painter::PaintPainterAt(gfx::Canvas* canvas,
223 Painter* painter,
224 const gfx::Rect& rect) {
225 DCHECK(canvas);
226 DCHECK(painter);
227 canvas->Save();
228 canvas->Translate(rect.OffsetFromOrigin());
229 painter->Paint(canvas, rect.size());
230 canvas->Restore();
231 }
232
233 // static
PaintFocusPainter(View * view,gfx::Canvas * canvas,Painter * focus_painter)234 void Painter::PaintFocusPainter(View* view,
235 gfx::Canvas* canvas,
236 Painter* focus_painter) {
237 if (focus_painter && view->HasFocus())
238 PaintPainterAt(canvas, focus_painter, view->GetLocalBounds());
239 }
240
241 // static
CreateSolidRoundRectPainter(SkColor color,float radius,const gfx::Insets & insets,SkBlendMode blend_mode,bool antialias)242 std::unique_ptr<Painter> Painter::CreateSolidRoundRectPainter(
243 SkColor color,
244 float radius,
245 const gfx::Insets& insets,
246 SkBlendMode blend_mode,
247 bool antialias) {
248 return std::make_unique<SolidRoundRectPainter>(
249 color, SK_ColorTRANSPARENT, radius, insets, blend_mode, antialias);
250 }
251
252 // static
CreateRoundRectWith1PxBorderPainter(SkColor bg_color,SkColor stroke_color,float radius,SkBlendMode blend_mode,bool antialias)253 std::unique_ptr<Painter> Painter::CreateRoundRectWith1PxBorderPainter(
254 SkColor bg_color,
255 SkColor stroke_color,
256 float radius,
257 SkBlendMode blend_mode,
258 bool antialias) {
259 return std::make_unique<SolidRoundRectPainter>(
260 bg_color, stroke_color, radius, gfx::Insets(), blend_mode, antialias);
261 }
262
263 // static
CreateImagePainter(const gfx::ImageSkia & image,const gfx::Insets & insets)264 std::unique_ptr<Painter> Painter::CreateImagePainter(
265 const gfx::ImageSkia& image,
266 const gfx::Insets& insets) {
267 return std::make_unique<ImagePainter>(image, insets);
268 }
269
270 // static
CreateImageGridPainter(const int image_ids[])271 std::unique_ptr<Painter> Painter::CreateImageGridPainter(
272 const int image_ids[]) {
273 return std::make_unique<ImagePainter>(image_ids);
274 }
275
276 // static
CreateSolidFocusPainter(SkColor color,const gfx::Insets & insets)277 std::unique_ptr<Painter> Painter::CreateSolidFocusPainter(
278 SkColor color,
279 const gfx::Insets& insets) {
280 // Before Canvas::DrawSolidFocusRect correctly inset the rect's bounds based
281 // on the thickness, callers had to add 1 to the bottom and right insets.
282 // Subtract that here so it works the same way with the new
283 // Canvas::DrawSolidFocusRect.
284 const gfx::Insets corrected_insets = insets - gfx::Insets(0, 0, 1, 1);
285 return std::make_unique<SolidFocusPainter>(color, 1, corrected_insets);
286 }
287
288 // static
CreateSolidFocusPainter(SkColor color,int thickness,const gfx::InsetsF & insets)289 std::unique_ptr<Painter> Painter::CreateSolidFocusPainter(
290 SkColor color,
291 int thickness,
292 const gfx::InsetsF& insets) {
293 return std::make_unique<SolidFocusPainter>(color, thickness, insets);
294 }
295
296 // static
CreatePaintedLayer(std::unique_ptr<Painter> painter)297 std::unique_ptr<ui::LayerOwner> Painter::CreatePaintedLayer(
298 std::unique_ptr<Painter> painter) {
299 return std::make_unique<PaintedLayer>(std::move(painter));
300 }
301
302 } // namespace views
303