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/SkPath.h"
13 #include "ui/gfx/canvas.h"
14 #include "ui/gfx/image/canvas_image_source.h"
15 #include "ui/gfx/vector_icon_types.h"
16 #include "ui/gfx/vector_icons2.h"
22 // Translates a string such as "MOVE_TO" into a command such as MOVE_TO.
23 CommandType
CommandFromString(const std::string
& source
) {
24 #define RETURN_IF_IS(command) \
25 if (source == #command) \
28 RETURN_IF_IS(NEW_PATH
);
29 RETURN_IF_IS(PATH_COLOR_ARGB
);
31 RETURN_IF_IS(MOVE_TO
);
32 RETURN_IF_IS(R_MOVE_TO
);
33 RETURN_IF_IS(LINE_TO
);
34 RETURN_IF_IS(R_LINE_TO
);
35 RETURN_IF_IS(H_LINE_TO
);
36 RETURN_IF_IS(R_H_LINE_TO
);
37 RETURN_IF_IS(V_LINE_TO
);
38 RETURN_IF_IS(R_V_LINE_TO
);
39 RETURN_IF_IS(CUBIC_TO
);
40 RETURN_IF_IS(R_CUBIC_TO
);
43 RETURN_IF_IS(CANVAS_DIMENSIONS
);
45 RETURN_IF_IS(DISABLE_AA
);
53 std::vector
<PathElement
> PathFromSource(const std::string
& source
) {
54 std::vector
<PathElement
> path
;
55 std::vector
<std::string
> pieces
= base::SplitString(
56 source
, "\n ,f", base::TRIM_WHITESPACE
, base::SPLIT_WANT_NONEMPTY
);
57 for (const auto& piece
: pieces
) {
59 if (base::StringToDouble(piece
, &value
))
60 path
.push_back(PathElement(SkDoubleToScalar(value
)));
62 path
.push_back(PathElement(CommandFromString(piece
)));
67 void PaintPath(Canvas
* canvas
,
68 const PathElement
* path_elements
,
72 path
.setFillType(SkPath::kEvenOdd_FillType
);
74 size_t canvas_size
= kReferenceSizeDip
;
75 std::vector
<SkPath
> paths
;
76 std::vector
<SkPaint
> paints
;
77 SkRect clip_rect
= SkRect::MakeEmpty();
79 for (size_t i
= 0; path_elements
[i
].type
!= END
; i
++) {
80 if (paths
.empty() || path_elements
[i
].type
== NEW_PATH
) {
81 paths
.push_back(SkPath());
82 paths
.back().setFillType(SkPath::kEvenOdd_FillType
);
84 paints
.push_back(SkPaint());
85 paints
.back().setColor(color
);
86 paints
.back().setAntiAlias(true);
87 paints
.back().setStrokeCap(SkPaint::kRound_Cap
);
90 SkPath
& path
= paths
.back();
91 SkPaint
& paint
= paints
.back();
92 switch (path_elements
[i
].type
) {
97 case PATH_COLOR_ARGB
: {
98 int a
= SkScalarFloorToInt(path_elements
[++i
].arg
);
99 int r
= SkScalarFloorToInt(path_elements
[++i
].arg
);
100 int g
= SkScalarFloorToInt(path_elements
[++i
].arg
);
101 int b
= SkScalarFloorToInt(path_elements
[++i
].arg
);
102 paint
.setColor(SkColorSetARGB(a
, r
, g
, b
));
107 paint
.setStyle(SkPaint::kStroke_Style
);
108 SkScalar width
= path_elements
[++i
].arg
;
109 paint
.setStrokeWidth(width
);
114 SkScalar x
= path_elements
[++i
].arg
;
115 SkScalar y
= path_elements
[++i
].arg
;
121 SkScalar x
= path_elements
[++i
].arg
;
122 SkScalar y
= path_elements
[++i
].arg
;
128 SkScalar x
= path_elements
[++i
].arg
;
129 SkScalar y
= path_elements
[++i
].arg
;
135 SkScalar x
= path_elements
[++i
].arg
;
136 SkScalar y
= path_elements
[++i
].arg
;
143 path
.getLastPt(&last_point
);
144 SkScalar x
= path_elements
[++i
].arg
;
145 path
.lineTo(x
, last_point
.fY
);
150 SkScalar x
= path_elements
[++i
].arg
;
157 path
.getLastPt(&last_point
);
158 SkScalar y
= path_elements
[++i
].arg
;
159 path
.lineTo(last_point
.fX
, y
);
164 SkScalar y
= path_elements
[++i
].arg
;
170 SkScalar x1
= path_elements
[++i
].arg
;
171 SkScalar y1
= path_elements
[++i
].arg
;
172 SkScalar x2
= path_elements
[++i
].arg
;
173 SkScalar y2
= path_elements
[++i
].arg
;
174 SkScalar x3
= path_elements
[++i
].arg
;
175 SkScalar y3
= path_elements
[++i
].arg
;
176 path
.cubicTo(x1
, y1
, x2
, y2
, x3
, y3
);
181 SkScalar x1
= path_elements
[++i
].arg
;
182 SkScalar y1
= path_elements
[++i
].arg
;
183 SkScalar x2
= path_elements
[++i
].arg
;
184 SkScalar y2
= path_elements
[++i
].arg
;
185 SkScalar x3
= path_elements
[++i
].arg
;
186 SkScalar y3
= path_elements
[++i
].arg
;
187 path
.rCubicTo(x1
, y1
, x2
, y2
, x3
, y3
);
192 SkScalar x
= path_elements
[++i
].arg
;
193 SkScalar y
= path_elements
[++i
].arg
;
194 SkScalar r
= path_elements
[++i
].arg
;
195 path
.addCircle(x
, y
, r
);
204 case CANVAS_DIMENSIONS
: {
205 SkScalar width
= path_elements
[++i
].arg
;
206 canvas_size
= SkScalarTruncToInt(width
);
211 SkScalar x
= path_elements
[++i
].arg
;
212 SkScalar y
= path_elements
[++i
].arg
;
213 SkScalar w
= path_elements
[++i
].arg
;
214 SkScalar h
= path_elements
[++i
].arg
;
215 clip_rect
= SkRect::MakeXYWH(x
, y
, w
, h
);
220 paint
.setAntiAlias(false);
230 if (dip_size
!= canvas_size
) {
231 SkScalar scale
= SkIntToScalar(dip_size
) / SkIntToScalar(canvas_size
);
232 canvas
->sk_canvas()->scale(scale
, scale
);
235 if (!clip_rect
.isEmpty())
236 canvas
->sk_canvas()->clipRect(clip_rect
);
238 DCHECK_EQ(paints
.size(), paths
.size());
239 for (size_t i
= 0; i
< paths
.size(); ++i
)
240 canvas
->DrawPath(paths
[i
], paints
[i
]);
243 class VectorIconSource
: public CanvasImageSource
{
245 VectorIconSource(VectorIconId id
, size_t dip_size
, SkColor color
)
247 gfx::Size(static_cast<int>(dip_size
), static_cast<int>(dip_size
)),
252 VectorIconSource(const std::string
& definition
,
256 gfx::Size(static_cast<int>(dip_size
), static_cast<int>(dip_size
)),
258 id_(VectorIconId::VECTOR_ICON_NONE
),
259 path_(PathFromSource(definition
)),
262 ~VectorIconSource() override
{}
264 // CanvasImageSource:
265 void Draw(gfx::Canvas
* canvas
) override
{
267 PaintVectorIcon(canvas
, id_
, size_
.width(), color_
);
269 PaintPath(canvas
, path_
.data(), size_
.width(), color_
);
273 const VectorIconId id_
;
274 const std::vector
<PathElement
> path_
;
275 const SkColor color_
;
277 DISALLOW_COPY_AND_ASSIGN(VectorIconSource
);
280 // This class caches vector icons (as ImageSkia) so they don't have to be drawn
281 // more than once. This also guarantees the backing data for the images returned
282 // by CreateVectorIcon will persist in memory until program termination.
283 class VectorIconCache
{
286 ~VectorIconCache() {}
288 ImageSkia
GetOrCreateIcon(VectorIconId id
, size_t dip_size
, SkColor color
) {
289 IconDescription
description(id
, dip_size
, color
);
290 auto iter
= images_
.find(description
);
291 if (iter
!= images_
.end())
295 new VectorIconSource(id
, dip_size
, color
),
296 gfx::Size(static_cast<int>(dip_size
), static_cast<int>(dip_size
)));
297 images_
.insert(std::make_pair(description
, icon
));
302 struct IconDescription
{
303 IconDescription(VectorIconId id
, size_t dip_size
, SkColor color
)
304 : id(id
), dip_size(dip_size
), color(color
) {}
306 bool operator<(const IconDescription
& other
) const {
308 return id
< other
.id
;
309 if (dip_size
!= other
.dip_size
)
310 return dip_size
< other
.dip_size
;
311 return color
< other
.color
;
319 std::map
<IconDescription
, ImageSkia
> images_
;
321 DISALLOW_COPY_AND_ASSIGN(VectorIconCache
);
324 static base::LazyInstance
<VectorIconCache
> g_icon_cache
=
325 LAZY_INSTANCE_INITIALIZER
;
329 void PaintVectorIcon(Canvas
* canvas
,
333 DCHECK(VectorIconId::VECTOR_ICON_NONE
!= id
);
334 const PathElement
* path
= canvas
->image_scale() == 1.f
335 ? GetPathForVectorIconAt1xScale(id
)
336 : GetPathForVectorIcon(id
);
337 PaintPath(canvas
, path
, dip_size
, color
);
340 ImageSkia
CreateVectorIcon(VectorIconId id
, size_t dip_size
, SkColor color
) {
341 return g_icon_cache
.Get().GetOrCreateIcon(id
, dip_size
, color
);
344 ImageSkia
CreateVectorIconFromSource(const std::string
& source
,
348 new VectorIconSource(source
, dip_size
, color
),
349 gfx::Size(static_cast<int>(dip_size
), static_cast<int>(dip_size
)));