Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / ui / gfx / paint_vector_icon.cc
blobaaeea30f9bfcccf98ac40db09b9147cb19e25071
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"
7 #include <map>
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"
20 namespace gfx {
22 namespace {
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) \
28 return command;
30 RETURN_IF_IS(NEW_PATH);
31 RETURN_IF_IS(PATH_COLOR_ARGB);
32 RETURN_IF_IS(PATH_MODE_CLEAR);
33 RETURN_IF_IS(STROKE);
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);
44 RETURN_IF_IS(CIRCLE);
45 RETURN_IF_IS(CLOSE);
46 RETURN_IF_IS(CANVAS_DIMENSIONS);
47 RETURN_IF_IS(CLIP);
48 RETURN_IF_IS(DISABLE_AA);
49 RETURN_IF_IS(END);
50 #undef RETURN_IF_IS
52 NOTREACHED();
53 return CLOSE;
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) {
61 double value;
62 if (base::StringToDouble(piece, &value))
63 path.push_back(PathElement(SkDoubleToScalar(value)));
64 else
65 path.push_back(PathElement(CommandFromString(piece)));
67 return path;
70 void PaintPath(Canvas* canvas,
71 const PathElement* path_elements,
72 size_t dip_size,
73 SkColor color) {
74 canvas->Save();
75 SkPath path;
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) {
97 // Handled above.
98 case NEW_PATH:
99 continue;
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));
107 break;
110 case PATH_MODE_CLEAR: {
111 paint.setXfermodeMode(SkXfermode::kClear_Mode);
112 break;
115 case STROKE: {
116 paint.setStyle(SkPaint::kStroke_Style);
117 SkScalar width = path_elements[++i].arg;
118 paint.setStrokeWidth(width);
119 break;
122 case MOVE_TO: {
123 SkScalar x = path_elements[++i].arg;
124 SkScalar y = path_elements[++i].arg;
125 path.moveTo(x, y);
126 break;
129 case R_MOVE_TO: {
130 SkScalar x = path_elements[++i].arg;
131 SkScalar y = path_elements[++i].arg;
132 path.rMoveTo(x, y);
133 break;
136 case LINE_TO: {
137 SkScalar x = path_elements[++i].arg;
138 SkScalar y = path_elements[++i].arg;
139 path.lineTo(x, y);
140 break;
143 case R_LINE_TO: {
144 SkScalar x = path_elements[++i].arg;
145 SkScalar y = path_elements[++i].arg;
146 path.rLineTo(x, y);
147 break;
150 case H_LINE_TO: {
151 SkPoint last_point;
152 path.getLastPt(&last_point);
153 SkScalar x = path_elements[++i].arg;
154 path.lineTo(x, last_point.fY);
155 break;
158 case R_H_LINE_TO: {
159 SkScalar x = path_elements[++i].arg;
160 path.rLineTo(x, 0);
161 break;
164 case V_LINE_TO: {
165 SkPoint last_point;
166 path.getLastPt(&last_point);
167 SkScalar y = path_elements[++i].arg;
168 path.lineTo(last_point.fX, y);
169 break;
172 case R_V_LINE_TO: {
173 SkScalar y = path_elements[++i].arg;
174 path.rLineTo(0, y);
175 break;
178 case CUBIC_TO: {
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);
186 break;
189 case R_CUBIC_TO: {
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);
197 break;
200 case CIRCLE: {
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);
205 break;
208 case CLOSE: {
209 path.close();
210 break;
213 case CANVAS_DIMENSIONS: {
214 SkScalar width = path_elements[++i].arg;
215 canvas_size = SkScalarTruncToInt(width);
216 break;
219 case CLIP: {
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);
225 break;
228 case DISABLE_AA: {
229 paint.setAntiAlias(false);
230 break;
233 case END:
234 NOTREACHED();
235 break;
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]);
250 canvas->Restore();
253 class VectorIconSource : public CanvasImageSource {
254 public:
255 VectorIconSource(VectorIconId id,
256 size_t dip_size,
257 SkColor color,
258 VectorIconId badge_id)
259 : CanvasImageSource(
260 gfx::Size(static_cast<int>(dip_size), static_cast<int>(dip_size)),
261 false),
262 id_(id),
263 color_(color),
264 badge_id_(badge_id) {}
266 VectorIconSource(const std::string& definition,
267 size_t dip_size,
268 SkColor color)
269 : CanvasImageSource(
270 gfx::Size(static_cast<int>(dip_size), static_cast<int>(dip_size)),
271 false),
272 id_(VectorIconId::VECTOR_ICON_NONE),
273 path_(PathFromSource(definition)),
274 color_(color),
275 badge_id_(VectorIconId::VECTOR_ICON_NONE) {}
277 ~VectorIconSource() override {}
279 // CanvasImageSource:
280 void Draw(gfx::Canvas* canvas) override {
281 if (path_.empty()) {
282 PaintVectorIcon(canvas, id_, size_.width(), color_);
283 if (badge_id_ != VectorIconId::VECTOR_ICON_NONE)
284 PaintVectorIcon(canvas, badge_id_, size_.width(), color_);
285 } else {
286 PaintPath(canvas, path_.data(), size_.width(), color_);
290 private:
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 {
303 public:
304 VectorIconCache() {}
305 ~VectorIconCache() {}
307 ImageSkia GetOrCreateIcon(VectorIconId id,
308 size_t dip_size,
309 SkColor color,
310 VectorIconId badge_id) {
311 IconDescription description(id, dip_size, color, badge_id);
312 auto iter = images_.find(description);
313 if (iter != images_.end())
314 return iter->second;
316 ImageSkia icon(
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));
320 return icon;
323 private:
324 struct IconDescription {
325 IconDescription(VectorIconId id,
326 size_t dip_size,
327 SkColor color,
328 VectorIconId badge_id)
329 : id(id), dip_size(dip_size), color(color), badge_id(badge_id) {}
331 bool operator<(const IconDescription& other) const {
332 if (id != other.id)
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;
341 VectorIconId id;
342 size_t dip_size;
343 SkColor color;
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;
355 } // namespace
357 void PaintVectorIcon(Canvas* canvas,
358 VectorIconId id,
359 size_t dip_size,
360 SkColor color) {
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,
374 size_t dip_size,
375 SkColor color,
376 VectorIconId badge_id) {
377 return g_icon_cache.Get().GetOrCreateIcon(id, dip_size, color, badge_id);
380 ImageSkia CreateVectorIconFromSource(const std::string& source,
381 size_t dip_size,
382 SkColor color) {
383 return ImageSkia(
384 new VectorIconSource(source, dip_size, color),
385 gfx::Size(static_cast<int>(dip_size), static_cast<int>(dip_size)));
388 } // namespace gfx