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(CAP_SQUARE
);
35 RETURN_IF_IS(MOVE_TO
);
36 RETURN_IF_IS(R_MOVE_TO
);
37 RETURN_IF_IS(LINE_TO
);
38 RETURN_IF_IS(R_LINE_TO
);
39 RETURN_IF_IS(H_LINE_TO
);
40 RETURN_IF_IS(R_H_LINE_TO
);
41 RETURN_IF_IS(V_LINE_TO
);
42 RETURN_IF_IS(R_V_LINE_TO
);
43 RETURN_IF_IS(CUBIC_TO
);
44 RETURN_IF_IS(R_CUBIC_TO
);
47 RETURN_IF_IS(CANVAS_DIMENSIONS
);
49 RETURN_IF_IS(DISABLE_AA
);
57 std::vector
<PathElement
> PathFromSource(const std::string
& source
) {
58 std::vector
<PathElement
> path
;
59 std::vector
<std::string
> pieces
= base::SplitString(
60 source
, "\n ,f", base::TRIM_WHITESPACE
, base::SPLIT_WANT_NONEMPTY
);
61 for (const auto& piece
: pieces
) {
63 if (base::StringToDouble(piece
, &value
))
64 path
.push_back(PathElement(SkDoubleToScalar(value
)));
66 path
.push_back(PathElement(CommandFromString(piece
)));
71 void PaintPath(Canvas
* canvas
,
72 const PathElement
* path_elements
,
77 path
.setFillType(SkPath::kEvenOdd_FillType
);
79 size_t canvas_size
= kReferenceSizeDip
;
80 std::vector
<SkPath
> paths
;
81 std::vector
<SkPaint
> paints
;
82 SkRect clip_rect
= SkRect::MakeEmpty();
84 for (size_t i
= 0; path_elements
[i
].type
!= END
; i
++) {
85 if (paths
.empty() || path_elements
[i
].type
== NEW_PATH
) {
86 paths
.push_back(SkPath());
87 paths
.back().setFillType(SkPath::kEvenOdd_FillType
);
89 paints
.push_back(SkPaint());
90 paints
.back().setColor(color
);
91 paints
.back().setAntiAlias(true);
92 paints
.back().setStrokeCap(SkPaint::kRound_Cap
);
95 SkPath
& path
= paths
.back();
96 SkPaint
& paint
= paints
.back();
97 switch (path_elements
[i
].type
) {
102 case PATH_COLOR_ARGB
: {
103 int a
= SkScalarFloorToInt(path_elements
[++i
].arg
);
104 int r
= SkScalarFloorToInt(path_elements
[++i
].arg
);
105 int g
= SkScalarFloorToInt(path_elements
[++i
].arg
);
106 int b
= SkScalarFloorToInt(path_elements
[++i
].arg
);
107 paint
.setColor(SkColorSetARGB(a
, r
, g
, b
));
111 case PATH_MODE_CLEAR
: {
112 paint
.setXfermodeMode(SkXfermode::kClear_Mode
);
117 paint
.setStyle(SkPaint::kStroke_Style
);
118 SkScalar width
= path_elements
[++i
].arg
;
119 paint
.setStrokeWidth(width
);
124 paint
.setStrokeCap(SkPaint::kSquare_Cap
);
129 SkScalar x
= path_elements
[++i
].arg
;
130 SkScalar y
= path_elements
[++i
].arg
;
136 SkScalar x
= path_elements
[++i
].arg
;
137 SkScalar y
= path_elements
[++i
].arg
;
143 SkScalar x
= path_elements
[++i
].arg
;
144 SkScalar y
= path_elements
[++i
].arg
;
150 SkScalar x
= path_elements
[++i
].arg
;
151 SkScalar y
= path_elements
[++i
].arg
;
158 path
.getLastPt(&last_point
);
159 SkScalar x
= path_elements
[++i
].arg
;
160 path
.lineTo(x
, last_point
.fY
);
165 SkScalar x
= path_elements
[++i
].arg
;
172 path
.getLastPt(&last_point
);
173 SkScalar y
= path_elements
[++i
].arg
;
174 path
.lineTo(last_point
.fX
, y
);
179 SkScalar y
= path_elements
[++i
].arg
;
185 SkScalar x1
= path_elements
[++i
].arg
;
186 SkScalar y1
= path_elements
[++i
].arg
;
187 SkScalar x2
= path_elements
[++i
].arg
;
188 SkScalar y2
= path_elements
[++i
].arg
;
189 SkScalar x3
= path_elements
[++i
].arg
;
190 SkScalar y3
= path_elements
[++i
].arg
;
191 path
.cubicTo(x1
, y1
, x2
, y2
, x3
, y3
);
196 SkScalar x1
= path_elements
[++i
].arg
;
197 SkScalar y1
= path_elements
[++i
].arg
;
198 SkScalar x2
= path_elements
[++i
].arg
;
199 SkScalar y2
= path_elements
[++i
].arg
;
200 SkScalar x3
= path_elements
[++i
].arg
;
201 SkScalar y3
= path_elements
[++i
].arg
;
202 path
.rCubicTo(x1
, y1
, x2
, y2
, x3
, y3
);
207 SkScalar x
= path_elements
[++i
].arg
;
208 SkScalar y
= path_elements
[++i
].arg
;
209 SkScalar r
= path_elements
[++i
].arg
;
210 path
.addCircle(x
, y
, r
);
219 case CANVAS_DIMENSIONS
: {
220 SkScalar width
= path_elements
[++i
].arg
;
221 canvas_size
= SkScalarTruncToInt(width
);
226 SkScalar x
= path_elements
[++i
].arg
;
227 SkScalar y
= path_elements
[++i
].arg
;
228 SkScalar w
= path_elements
[++i
].arg
;
229 SkScalar h
= path_elements
[++i
].arg
;
230 clip_rect
= SkRect::MakeXYWH(x
, y
, w
, h
);
235 paint
.setAntiAlias(false);
245 if (dip_size
!= canvas_size
) {
246 SkScalar scale
= SkIntToScalar(dip_size
) / SkIntToScalar(canvas_size
);
247 canvas
->sk_canvas()->scale(scale
, scale
);
250 if (!clip_rect
.isEmpty())
251 canvas
->sk_canvas()->clipRect(clip_rect
);
253 DCHECK_EQ(paints
.size(), paths
.size());
254 for (size_t i
= 0; i
< paths
.size(); ++i
)
255 canvas
->DrawPath(paths
[i
], paints
[i
]);
259 class VectorIconSource
: public CanvasImageSource
{
261 VectorIconSource(VectorIconId id
,
264 VectorIconId badge_id
)
266 gfx::Size(static_cast<int>(dip_size
), static_cast<int>(dip_size
)),
270 badge_id_(badge_id
) {}
272 VectorIconSource(const std::string
& definition
,
276 gfx::Size(static_cast<int>(dip_size
), static_cast<int>(dip_size
)),
278 id_(VectorIconId::VECTOR_ICON_NONE
),
279 path_(PathFromSource(definition
)),
281 badge_id_(VectorIconId::VECTOR_ICON_NONE
) {}
283 ~VectorIconSource() override
{}
285 // CanvasImageSource:
286 void Draw(gfx::Canvas
* canvas
) override
{
288 PaintVectorIcon(canvas
, id_
, size_
.width(), color_
);
289 if (badge_id_
!= VectorIconId::VECTOR_ICON_NONE
)
290 PaintVectorIcon(canvas
, badge_id_
, size_
.width(), color_
);
292 PaintPath(canvas
, path_
.data(), size_
.width(), color_
);
297 const VectorIconId id_
;
298 const std::vector
<PathElement
> path_
;
299 const SkColor color_
;
300 const VectorIconId badge_id_
;
302 DISALLOW_COPY_AND_ASSIGN(VectorIconSource
);
305 // This class caches vector icons (as ImageSkia) so they don't have to be drawn
306 // more than once. This also guarantees the backing data for the images returned
307 // by CreateVectorIcon will persist in memory until program termination.
308 class VectorIconCache
{
311 ~VectorIconCache() {}
313 ImageSkia
GetOrCreateIcon(VectorIconId id
,
316 VectorIconId badge_id
) {
317 IconDescription
description(id
, dip_size
, color
, badge_id
);
318 auto iter
= images_
.find(description
);
319 if (iter
!= images_
.end())
323 new VectorIconSource(id
, dip_size
, color
, badge_id
),
324 gfx::Size(static_cast<int>(dip_size
), static_cast<int>(dip_size
)));
325 images_
.insert(std::make_pair(description
, icon
));
330 struct IconDescription
{
331 IconDescription(VectorIconId id
,
334 VectorIconId badge_id
)
335 : id(id
), dip_size(dip_size
), color(color
), badge_id(badge_id
) {}
337 bool operator<(const IconDescription
& other
) const {
339 return id
< other
.id
;
340 if (dip_size
!= other
.dip_size
)
341 return dip_size
< other
.dip_size
;
342 if (color
!= other
.color
)
343 return color
< other
.color
;
344 return badge_id
< other
.badge_id
;
350 VectorIconId badge_id
;
353 std::map
<IconDescription
, ImageSkia
> images_
;
355 DISALLOW_COPY_AND_ASSIGN(VectorIconCache
);
358 static base::LazyInstance
<VectorIconCache
> g_icon_cache
=
359 LAZY_INSTANCE_INITIALIZER
;
363 void PaintVectorIcon(Canvas
* canvas
,
367 DCHECK(VectorIconId::VECTOR_ICON_NONE
!= id
);
368 const PathElement
* path
= canvas
->image_scale() == 1.f
369 ? GetPathForVectorIconAt1xScale(id
)
370 : GetPathForVectorIcon(id
);
371 PaintPath(canvas
, path
, dip_size
, color
);
374 ImageSkia
CreateVectorIcon(VectorIconId id
, size_t dip_size
, SkColor color
) {
375 return CreateVectorIconWithBadge(id
, dip_size
, color
,
376 VectorIconId::VECTOR_ICON_NONE
);
379 ImageSkia
CreateVectorIconWithBadge(VectorIconId id
,
382 VectorIconId badge_id
) {
383 return g_icon_cache
.Get().GetOrCreateIcon(id
, dip_size
, color
, badge_id
);
386 ImageSkia
CreateVectorIconFromSource(const std::string
& source
,
390 new VectorIconSource(source
, dip_size
, color
),
391 gfx::Size(static_cast<int>(dip_size
), static_cast<int>(dip_size
)));