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.
5 #include "ui/views/painter.h"
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"
24 // GradientPainter ------------------------------------------------------------
26 class GradientPainter
: public Painter
{
28 GradientPainter(bool horizontal
,
32 virtual ~GradientPainter();
35 virtual gfx::Size
GetMinimumSize() const OVERRIDE
;
36 virtual void Paint(gfx::Canvas
* canvas
, const gfx::Size
& size
) OVERRIDE
;
39 // If |horizontal_| is true then the gradient is painted horizontally.
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_|.
48 DISALLOW_COPY_AND_ASSIGN(GradientPainter
);
51 GradientPainter::GradientPainter(bool horizontal
,
55 : horizontal_(horizontal
),
56 colors_(new SkColor
[count
]),
57 pos_(new SkScalar
[count
]),
59 for (size_t i
= 0; i
< count_
; ++i
) {
61 colors_
[i
] = colors
[i
];
65 GradientPainter::~GradientPainter() {
68 gfx::Size
GradientPainter::GetMinimumSize() const {
72 void GradientPainter::Paint(gfx::Canvas
* canvas
, const gfx::Size
& size
) {
77 p
[1].iset(size
.width(), 0);
79 p
[1].iset(0, size
.height());
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());
86 canvas
->sk_canvas()->drawRectCoords(SkIntToScalar(0), SkIntToScalar(0),
87 SkIntToScalar(size
.width()),
88 SkIntToScalar(size
.height()), paint
);
92 // ImagePainter ---------------------------------------------------------------
94 // ImagePainter stores and paints nine images as a scalable grid.
95 class VIEWS_EXPORT ImagePainter
: public Painter
{
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
[]);
101 // Constructs an ImagePainter with the specified image and insets.
102 ImagePainter(const gfx::ImageSkia
& image
, const gfx::Insets
& insets
);
104 virtual ~ImagePainter();
106 // Returns true if the images are empty.
107 bool IsEmpty() const;
110 virtual gfx::Size
GetMinimumSize() const OVERRIDE
;
111 virtual void Paint(gfx::Canvas
* canvas
, const gfx::Size
& size
) OVERRIDE
;
114 // Stretches the given image over the specified canvas area.
115 static void Fill(gfx::Canvas
* c
,
116 const gfx::ImageSkia
& i
,
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];
129 DISALLOW_COPY_AND_ASSIGN(ImagePainter
);
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
]);
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());
144 // Extract subsets of the original image to match the |images_| format.
146 { 0, insets
.left(), image
.width() - insets
.right(), image
.width() };
148 { 0, insets
.top(), image
.height() - insets
.bottom(), image
.height() };
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
]));
158 ImagePainter::~ImagePainter() {
161 bool ImagePainter::IsEmpty() const {
162 return images_
[0].isNull();
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());
171 void ImagePainter::Paint(gfx::Canvas
* canvas
, const gfx::Size
& size
) {
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
);
210 void ImagePainter::Fill(gfx::Canvas
* c
,
211 const gfx::ImageSkia
& i
,
216 c
->DrawImageInt(i
, 0, 0, i
.width(), i
.height(), x
, y
, w
, h
, false);
222 // Painter --------------------------------------------------------------------
227 Painter::~Painter() {
231 void Painter::PaintPainterAt(gfx::Canvas
* canvas
,
233 const gfx::Rect
& rect
) {
234 DCHECK(canvas
&& painter
);
236 canvas
->Translate(rect
.OffsetFromOrigin());
237 painter
->Paint(canvas
, rect
.size());
242 Painter
* Painter::CreateHorizontalGradient(SkColor c1
, SkColor c2
) {
246 SkScalar pos
[] = {0, 1};
247 return new GradientPainter(true, colors
, pos
, 2);
251 Painter
* Painter::CreateVerticalGradient(SkColor c1
, SkColor c2
) {
255 SkScalar pos
[] = {0, 1};
256 return new GradientPainter(false, colors
, pos
, 2);
260 Painter
* Painter::CreateVerticalMultiColorGradient(SkColor
* colors
,
263 return new GradientPainter(false, colors
, pos
, count
);
267 Painter
* Painter::CreateImagePainter(const gfx::ImageSkia
& image
,
268 const gfx::Insets
& insets
) {
269 return new ImagePainter(image
, insets
);
273 Painter
* Painter::CreateImageGridPainter(const int image_ids
[]) {
274 return new ImagePainter(image_ids
);
278 // HorizontalPainter ----------------------------------------------------------
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());
288 HorizontalPainter::~HorizontalPainter() {
291 gfx::Size
HorizontalPainter::GetMinimumSize() const {
293 images_
[LEFT
]->width() + images_
[CENTER
]->width() +
294 images_
[RIGHT
]->width(), images_
[LEFT
]->height());
297 void HorizontalPainter::Paint(gfx::Canvas
* canvas
, const gfx::Size
& size
) {
298 if (size
.width() < GetMinimumSize().width())
299 return; // No room to paint.
301 canvas
->DrawImageInt(*images_
[LEFT
], 0, 0);
302 canvas
->DrawImageInt(*images_
[RIGHT
], size
.width() - images_
[RIGHT
]->width(),
304 canvas
->TileImageInt(
305 *images_
[CENTER
], images_
[LEFT
]->width(), 0,
306 size
.width() - images_
[LEFT
]->width() - images_
[RIGHT
]->width(),
307 images_
[LEFT
]->height());