1 // Copyright 2015 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/gfx/paint_vector_icon.h"
9 #include "base/lazy_instance.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_split.h"
12 #include "third_party/skia/include/core/SkPaint.h"
13 #include "third_party/skia/include/core/SkPath.h"
14 #include "third_party/skia/include/core/SkXfermode.h"
15 #include "ui/gfx/canvas.h"
16 #include "ui/gfx/image/canvas_image_source.h"
17 #include "ui/gfx/vector_icon_types.h"
18 #include "ui/gfx/vector_icons.h"
24 // Translates a string such as "MOVE_TO" into a command such as MOVE_TO.
25 CommandType
CommandFromString(const std::string
& source
) {
26 #define RETURN_IF_IS(command) \
27 if (source == #command) \
30 RETURN_IF_IS(NEW_PATH
);
31 RETURN_IF_IS(PATH_COLOR_ARGB
);
32 RETURN_IF_IS(PATH_MODE_CLEAR
);
34 RETURN_IF_IS(MOVE_TO
);
35 RETURN_IF_IS(R_MOVE_TO
);
36 RETURN_IF_IS(LINE_TO
);
37 RETURN_IF_IS(R_LINE_TO
);
38 RETURN_IF_IS(H_LINE_TO
);
39 RETURN_IF_IS(R_H_LINE_TO
);
40 RETURN_IF_IS(V_LINE_TO
);
41 RETURN_IF_IS(R_V_LINE_TO
);
42 RETURN_IF_IS(CUBIC_TO
);
43 RETURN_IF_IS(R_CUBIC_TO
);
46 RETURN_IF_IS(CANVAS_DIMENSIONS
);
48 RETURN_IF_IS(DISABLE_AA
);
56 std::vector
<PathElement
> PathFromSource(const std::string
& source
) {
57 std::vector
<PathElement
> path
;
58 std::vector
<std::string
> pieces
= base::SplitString(
59 source
, "\n ,f", base::TRIM_WHITESPACE
, base::SPLIT_WANT_NONEMPTY
);
60 for (const auto& piece
: pieces
) {
62 if (base::StringToDouble(piece
, &value
))
63 path
.push_back(PathElement(SkDoubleToScalar(value
)));
65 path
.push_back(PathElement(CommandFromString(piece
)));
70 void PaintPath(Canvas
* canvas
,
71 const PathElement
* path_elements
,
76 path
.setFillType(SkPath::kEvenOdd_FillType
);
78 size_t canvas_size
= kReferenceSizeDip
;
79 std::vector
<SkPath
> paths
;
80 std::vector
<SkPaint
> paints
;
81 SkRect clip_rect
= SkRect::MakeEmpty();
83 for (size_t i
= 0; path_elements
[i
].type
!= END
; i
++) {
84 if (paths
.empty() || path_elements
[i
].type
== NEW_PATH
) {
85 paths
.push_back(SkPath());
86 paths
.back().setFillType(SkPath::kEvenOdd_FillType
);
88 paints
.push_back(SkPaint());
89 paints
.back().setColor(color
);
90 paints
.back().setAntiAlias(true);
91 paints
.back().setStrokeCap(SkPaint::kRound_Cap
);
94 SkPath
& path
= paths
.back();
95 SkPaint
& paint
= paints
.back();
96 switch (path_elements
[i
].type
) {
101 case PATH_COLOR_ARGB
: {
102 int a
= SkScalarFloorToInt(path_elements
[++i
].arg
);
103 int r
= SkScalarFloorToInt(path_elements
[++i
].arg
);
104 int g
= SkScalarFloorToInt(path_elements
[++i
].arg
);
105 int b
= SkScalarFloorToInt(path_elements
[++i
].arg
);
106 paint
.setColor(SkColorSetARGB(a
, r
, g
, b
));
110 case PATH_MODE_CLEAR
: {
111 paint
.setXfermodeMode(SkXfermode::kClear_Mode
);
116 paint
.setStyle(SkPaint::kStroke_Style
);
117 SkScalar width
= path_elements
[++i
].arg
;
118 paint
.setStrokeWidth(width
);
123 SkScalar x
= path_elements
[++i
].arg
;
124 SkScalar y
= path_elements
[++i
].arg
;
130 SkScalar x
= path_elements
[++i
].arg
;
131 SkScalar y
= path_elements
[++i
].arg
;
137 SkScalar x
= path_elements
[++i
].arg
;
138 SkScalar y
= path_elements
[++i
].arg
;
144 SkScalar x
= path_elements
[++i
].arg
;
145 SkScalar y
= path_elements
[++i
].arg
;
152 path
.getLastPt(&last_point
);
153 SkScalar x
= path_elements
[++i
].arg
;
154 path
.lineTo(x
, last_point
.fY
);
159 SkScalar x
= path_elements
[++i
].arg
;
166 path
.getLastPt(&last_point
);
167 SkScalar y
= path_elements
[++i
].arg
;
168 path
.lineTo(last_point
.fX
, y
);
173 SkScalar y
= path_elements
[++i
].arg
;
179 SkScalar x1
= path_elements
[++i
].arg
;
180 SkScalar y1
= path_elements
[++i
].arg
;
181 SkScalar x2
= path_elements
[++i
].arg
;
182 SkScalar y2
= path_elements
[++i
].arg
;
183 SkScalar x3
= path_elements
[++i
].arg
;
184 SkScalar y3
= path_elements
[++i
].arg
;
185 path
.cubicTo(x1
, y1
, x2
, y2
, x3
, y3
);
190 SkScalar x1
= path_elements
[++i
].arg
;
191 SkScalar y1
= path_elements
[++i
].arg
;
192 SkScalar x2
= path_elements
[++i
].arg
;
193 SkScalar y2
= path_elements
[++i
].arg
;
194 SkScalar x3
= path_elements
[++i
].arg
;
195 SkScalar y3
= path_elements
[++i
].arg
;
196 path
.rCubicTo(x1
, y1
, x2
, y2
, x3
, y3
);
201 SkScalar x
= path_elements
[++i
].arg
;
202 SkScalar y
= path_elements
[++i
].arg
;
203 SkScalar r
= path_elements
[++i
].arg
;
204 path
.addCircle(x
, y
, r
);
213 case CANVAS_DIMENSIONS
: {
214 SkScalar width
= path_elements
[++i
].arg
;
215 canvas_size
= SkScalarTruncToInt(width
);
220 SkScalar x
= path_elements
[++i
].arg
;
221 SkScalar y
= path_elements
[++i
].arg
;
222 SkScalar w
= path_elements
[++i
].arg
;
223 SkScalar h
= path_elements
[++i
].arg
;
224 clip_rect
= SkRect::MakeXYWH(x
, y
, w
, h
);
229 paint
.setAntiAlias(false);
239 if (dip_size
!= canvas_size
) {
240 SkScalar scale
= SkIntToScalar(dip_size
) / SkIntToScalar(canvas_size
);
241 canvas
->sk_canvas()->scale(scale
, scale
);
244 if (!clip_rect
.isEmpty())
245 canvas
->sk_canvas()->clipRect(clip_rect
);
247 DCHECK_EQ(paints
.size(), paths
.size());
248 for (size_t i
= 0; i
< paths
.size(); ++i
)
249 canvas
->DrawPath(paths
[i
], paints
[i
]);
253 class VectorIconSource
: public CanvasImageSource
{
255 VectorIconSource(VectorIconId id
,
258 VectorIconId badge_id
)
260 gfx::Size(static_cast<int>(dip_size
), static_cast<int>(dip_size
)),
264 badge_id_(badge_id
) {}
266 VectorIconSource(const std::string
& definition
,
270 gfx::Size(static_cast<int>(dip_size
), static_cast<int>(dip_size
)),
272 id_(VectorIconId::VECTOR_ICON_NONE
),
273 path_(PathFromSource(definition
)),
275 badge_id_(VectorIconId::VECTOR_ICON_NONE
) {}
277 ~VectorIconSource() override
{}
279 // CanvasImageSource:
280 void Draw(gfx::Canvas
* canvas
) override
{
282 PaintVectorIcon(canvas
, id_
, size_
.width(), color_
);
283 if (badge_id_
!= VectorIconId::VECTOR_ICON_NONE
)
284 PaintVectorIcon(canvas
, badge_id_
, size_
.width(), color_
);
286 PaintPath(canvas
, path_
.data(), size_
.width(), color_
);
291 const VectorIconId id_
;
292 const std::vector
<PathElement
> path_
;
293 const SkColor color_
;
294 const VectorIconId badge_id_
;
296 DISALLOW_COPY_AND_ASSIGN(VectorIconSource
);
299 // This class caches vector icons (as ImageSkia) so they don't have to be drawn
300 // more than once. This also guarantees the backing data for the images returned
301 // by CreateVectorIcon will persist in memory until program termination.
302 class VectorIconCache
{
305 ~VectorIconCache() {}
307 ImageSkia
GetOrCreateIcon(VectorIconId id
,
310 VectorIconId badge_id
) {
311 IconDescription
description(id
, dip_size
, color
, badge_id
);
312 auto iter
= images_
.find(description
);
313 if (iter
!= images_
.end())
317 new VectorIconSource(id
, dip_size
, color
, badge_id
),
318 gfx::Size(static_cast<int>(dip_size
), static_cast<int>(dip_size
)));
319 images_
.insert(std::make_pair(description
, icon
));
324 struct IconDescription
{
325 IconDescription(VectorIconId id
,
328 VectorIconId badge_id
)
329 : id(id
), dip_size(dip_size
), color(color
), badge_id(badge_id
) {}
331 bool operator<(const IconDescription
& other
) const {
333 return id
< other
.id
;
334 if (dip_size
!= other
.dip_size
)
335 return dip_size
< other
.dip_size
;
336 if (color
!= other
.color
)
337 return color
< other
.color
;
338 return badge_id
< other
.badge_id
;
344 VectorIconId badge_id
;
347 std::map
<IconDescription
, ImageSkia
> images_
;
349 DISALLOW_COPY_AND_ASSIGN(VectorIconCache
);
352 static base::LazyInstance
<VectorIconCache
> g_icon_cache
=
353 LAZY_INSTANCE_INITIALIZER
;
357 void PaintVectorIcon(Canvas
* canvas
,
361 DCHECK(VectorIconId::VECTOR_ICON_NONE
!= id
);
362 const PathElement
* path
= canvas
->image_scale() == 1.f
363 ? GetPathForVectorIconAt1xScale(id
)
364 : GetPathForVectorIcon(id
);
365 PaintPath(canvas
, path
, dip_size
, color
);
368 ImageSkia
CreateVectorIcon(VectorIconId id
, size_t dip_size
, SkColor color
) {
369 return CreateVectorIconWithBadge(id
, dip_size
, color
,
370 VectorIconId::VECTOR_ICON_NONE
);
373 ImageSkia
CreateVectorIconWithBadge(VectorIconId id
,
376 VectorIconId badge_id
) {
377 return g_icon_cache
.Get().GetOrCreateIcon(id
, dip_size
, color
, badge_id
);
380 ImageSkia
CreateVectorIconFromSource(const std::string
& source
,
384 new VectorIconSource(source
, dip_size
, color
),
385 gfx::Size(static_cast<int>(dip_size
), static_cast<int>(dip_size
)));