Vectorize sad tab image.
[chromium-blink-merge.git] / ui / gfx / paint_vector_icon.cc
blob7051fb58fdf3e31f4ff652e6f9f210fb8b7cc0c5
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(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);
45 RETURN_IF_IS(CIRCLE);
46 RETURN_IF_IS(CLOSE);
47 RETURN_IF_IS(CANVAS_DIMENSIONS);
48 RETURN_IF_IS(CLIP);
49 RETURN_IF_IS(DISABLE_AA);
50 RETURN_IF_IS(END);
51 #undef RETURN_IF_IS
53 NOTREACHED();
54 return CLOSE;
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) {
62 double value;
63 if (base::StringToDouble(piece, &value))
64 path.push_back(PathElement(SkDoubleToScalar(value)));
65 else
66 path.push_back(PathElement(CommandFromString(piece)));
68 return path;
71 void PaintPath(Canvas* canvas,
72 const PathElement* path_elements,
73 size_t dip_size,
74 SkColor color) {
75 canvas->Save();
76 SkPath path;
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) {
98 // Handled above.
99 case NEW_PATH:
100 continue;
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));
108 break;
111 case PATH_MODE_CLEAR: {
112 paint.setXfermodeMode(SkXfermode::kClear_Mode);
113 break;
116 case STROKE: {
117 paint.setStyle(SkPaint::kStroke_Style);
118 SkScalar width = path_elements[++i].arg;
119 paint.setStrokeWidth(width);
120 break;
123 case CAP_SQUARE: {
124 paint.setStrokeCap(SkPaint::kSquare_Cap);
125 break;
128 case MOVE_TO: {
129 SkScalar x = path_elements[++i].arg;
130 SkScalar y = path_elements[++i].arg;
131 path.moveTo(x, y);
132 break;
135 case R_MOVE_TO: {
136 SkScalar x = path_elements[++i].arg;
137 SkScalar y = path_elements[++i].arg;
138 path.rMoveTo(x, y);
139 break;
142 case LINE_TO: {
143 SkScalar x = path_elements[++i].arg;
144 SkScalar y = path_elements[++i].arg;
145 path.lineTo(x, y);
146 break;
149 case R_LINE_TO: {
150 SkScalar x = path_elements[++i].arg;
151 SkScalar y = path_elements[++i].arg;
152 path.rLineTo(x, y);
153 break;
156 case H_LINE_TO: {
157 SkPoint last_point;
158 path.getLastPt(&last_point);
159 SkScalar x = path_elements[++i].arg;
160 path.lineTo(x, last_point.fY);
161 break;
164 case R_H_LINE_TO: {
165 SkScalar x = path_elements[++i].arg;
166 path.rLineTo(x, 0);
167 break;
170 case V_LINE_TO: {
171 SkPoint last_point;
172 path.getLastPt(&last_point);
173 SkScalar y = path_elements[++i].arg;
174 path.lineTo(last_point.fX, y);
175 break;
178 case R_V_LINE_TO: {
179 SkScalar y = path_elements[++i].arg;
180 path.rLineTo(0, y);
181 break;
184 case CUBIC_TO: {
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);
192 break;
195 case R_CUBIC_TO: {
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);
203 break;
206 case CIRCLE: {
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);
211 break;
214 case CLOSE: {
215 path.close();
216 break;
219 case CANVAS_DIMENSIONS: {
220 SkScalar width = path_elements[++i].arg;
221 canvas_size = SkScalarTruncToInt(width);
222 break;
225 case CLIP: {
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);
231 break;
234 case DISABLE_AA: {
235 paint.setAntiAlias(false);
236 break;
239 case END:
240 NOTREACHED();
241 break;
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]);
256 canvas->Restore();
259 class VectorIconSource : public CanvasImageSource {
260 public:
261 VectorIconSource(VectorIconId id,
262 size_t dip_size,
263 SkColor color,
264 VectorIconId badge_id)
265 : CanvasImageSource(
266 gfx::Size(static_cast<int>(dip_size), static_cast<int>(dip_size)),
267 false),
268 id_(id),
269 color_(color),
270 badge_id_(badge_id) {}
272 VectorIconSource(const std::string& definition,
273 size_t dip_size,
274 SkColor color)
275 : CanvasImageSource(
276 gfx::Size(static_cast<int>(dip_size), static_cast<int>(dip_size)),
277 false),
278 id_(VectorIconId::VECTOR_ICON_NONE),
279 path_(PathFromSource(definition)),
280 color_(color),
281 badge_id_(VectorIconId::VECTOR_ICON_NONE) {}
283 ~VectorIconSource() override {}
285 // CanvasImageSource:
286 void Draw(gfx::Canvas* canvas) override {
287 if (path_.empty()) {
288 PaintVectorIcon(canvas, id_, size_.width(), color_);
289 if (badge_id_ != VectorIconId::VECTOR_ICON_NONE)
290 PaintVectorIcon(canvas, badge_id_, size_.width(), color_);
291 } else {
292 PaintPath(canvas, path_.data(), size_.width(), color_);
296 private:
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 {
309 public:
310 VectorIconCache() {}
311 ~VectorIconCache() {}
313 ImageSkia GetOrCreateIcon(VectorIconId id,
314 size_t dip_size,
315 SkColor color,
316 VectorIconId badge_id) {
317 IconDescription description(id, dip_size, color, badge_id);
318 auto iter = images_.find(description);
319 if (iter != images_.end())
320 return iter->second;
322 ImageSkia icon(
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));
326 return icon;
329 private:
330 struct IconDescription {
331 IconDescription(VectorIconId id,
332 size_t dip_size,
333 SkColor color,
334 VectorIconId badge_id)
335 : id(id), dip_size(dip_size), color(color), badge_id(badge_id) {}
337 bool operator<(const IconDescription& other) const {
338 if (id != other.id)
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;
347 VectorIconId id;
348 size_t dip_size;
349 SkColor color;
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;
361 } // namespace
363 void PaintVectorIcon(Canvas* canvas,
364 VectorIconId id,
365 size_t dip_size,
366 SkColor color) {
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,
380 size_t dip_size,
381 SkColor color,
382 VectorIconId badge_id) {
383 return g_icon_cache.Get().GetOrCreateIcon(id, dip_size, color, badge_id);
386 ImageSkia CreateVectorIconFromSource(const std::string& source,
387 size_t dip_size,
388 SkColor color) {
389 return ImageSkia(
390 new VectorIconSource(source, dip_size, color),
391 gfx::Size(static_cast<int>(dip_size), static_cast<int>(dip_size)));
394 } // namespace gfx