Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(598)

Side by Side Diff: ui/gfx/paint_vector_icon.cc

Issue 2870643003: Add support for animations in vector icons. (Closed)
Patch Set: docs Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/gfx/paint_vector_icon.h" 5 #include "ui/gfx/paint_vector_icon.h"
6 6
7 #include <map> 7 #include <map>
8 #include <tuple> 8 #include <tuple>
9 9
10 #include "base/i18n/rtl.h" 10 #include "base/i18n/rtl.h"
11 #include "base/lazy_instance.h" 11 #include "base/lazy_instance.h"
12 #include "base/macros.h" 12 #include "base/macros.h"
13 #include "base/memory/ptr_util.h" 13 #include "base/memory/ptr_util.h"
14 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h" 15 #include "base/strings/string_split.h"
16 #include "cc/paint/paint_canvas.h" 16 #include "cc/paint/paint_canvas.h"
17 #include "cc/paint/paint_flags.h" 17 #include "cc/paint/paint_flags.h"
18 #include "third_party/skia/include/core/SkPath.h" 18 #include "third_party/skia/include/core/SkPath.h"
19 #include "ui/gfx/animation/tween.h"
19 #include "ui/gfx/canvas.h" 20 #include "ui/gfx/canvas.h"
20 #include "ui/gfx/image/canvas_image_source.h" 21 #include "ui/gfx/image/canvas_image_source.h"
21 #include "ui/gfx/scoped_canvas.h" 22 #include "ui/gfx/scoped_canvas.h"
22 #include "ui/gfx/vector_icon_types.h" 23 #include "ui/gfx/vector_icon_types.h"
23 24
24 namespace gfx { 25 namespace gfx {
25 26
26 namespace { 27 namespace {
27 28
28 // Helper that simplifies iterating over a sequence of PathElements. 29 // Helper that simplifies iterating over a sequence of PathElements.
(...skipping 25 matching lines...) Expand all
54 case CANVAS_DIMENSIONS: 55 case CANVAS_DIMENSIONS:
55 return 1; 56 return 1;
56 57
57 case MOVE_TO: 58 case MOVE_TO:
58 case R_MOVE_TO: 59 case R_MOVE_TO:
59 case LINE_TO: 60 case LINE_TO:
60 case R_LINE_TO: 61 case R_LINE_TO:
61 return 2; 62 return 2;
62 63
63 case CIRCLE: 64 case CIRCLE:
65 case TRANSITION_END:
64 return 3; 66 return 3;
65 67
66 case PATH_COLOR_ARGB: 68 case PATH_COLOR_ARGB:
67 case CUBIC_TO_SHORTHAND: 69 case CUBIC_TO_SHORTHAND:
68 case CLIP: 70 case CLIP:
69 return 4; 71 return 4;
70 72
71 case ROUND_RECT: 73 case ROUND_RECT:
72 return 5; 74 return 5;
73 75
74 case CUBIC_TO: 76 case CUBIC_TO:
75 case R_CUBIC_TO: 77 case R_CUBIC_TO:
76 return 6; 78 return 6;
77 79
78 case ARC_TO: 80 case ARC_TO:
79 case R_ARC_TO: 81 case R_ARC_TO:
80 return 7; 82 return 7;
81 83
82 case NEW_PATH: 84 case NEW_PATH:
83 case PATH_MODE_CLEAR: 85 case PATH_MODE_CLEAR:
84 case CAP_SQUARE: 86 case CAP_SQUARE:
85 case CLOSE: 87 case CLOSE:
86 case DISABLE_AA: 88 case DISABLE_AA:
87 case FLIPS_IN_RTL: 89 case FLIPS_IN_RTL:
90 case TRANSITION_FROM:
91 case TRANSITION_TO:
88 case END: 92 case END:
89 return 0; 93 return 0;
90 } 94 }
91 95
92 NOTREACHED(); 96 NOTREACHED();
93 return 0; 97 return 0;
94 } 98 }
95 99
96 const PathElement* path_elements_; 100 const PathElement* path_elements_;
97 int command_index_ = 0; 101 int command_index_ = 0;
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
150 path.push_back(PathElement(SkIntToScalar(hex_value))); 154 path.push_back(PathElement(SkIntToScalar(hex_value)));
151 else 155 else
152 path.push_back(PathElement(CommandFromString(piece))); 156 path.push_back(PathElement(CommandFromString(piece)));
153 } 157 }
154 return path; 158 return path;
155 } 159 }
156 160
157 void PaintPath(Canvas* canvas, 161 void PaintPath(Canvas* canvas,
158 const PathElement* path_elements, 162 const PathElement* path_elements,
159 int dip_size, 163 int dip_size,
160 SkColor color) { 164 SkColor color,
165 const base::TimeDelta* elapsed_time = nullptr) {
161 SkPath path; 166 SkPath path;
162 path.setFillType(SkPath::kEvenOdd_FillType); 167 path.setFillType(SkPath::kEvenOdd_FillType);
163 168
164 int canvas_size = kReferenceSizeDip; 169 int canvas_size = kReferenceSizeDip;
165 std::vector<SkPath> paths; 170 std::vector<SkPath> paths;
166 std::vector<cc::PaintFlags> flags_array; 171 std::vector<cc::PaintFlags> flags_array;
167 SkRect clip_rect = SkRect::MakeEmpty(); 172 SkRect clip_rect = SkRect::MakeEmpty();
168 bool flips_in_rtl = false; 173 bool flips_in_rtl = false;
169 CommandType previous_command_type = NEW_PATH; 174 CommandType previous_command_type = NEW_PATH;
170 175
171 for (PathParser parser(path_elements); parser.CurrentCommand() != END; 176 for (PathParser parser(path_elements); parser.CurrentCommand() != END;
172 parser.Advance()) { 177 parser.Advance()) {
173 auto arg = [&parser](int i) { return parser.GetArgument(i); }; 178 auto arg = [&parser](int i) { return parser.GetArgument(i); };
174 const CommandType command_type = parser.CurrentCommand(); 179 const CommandType command_type = parser.CurrentCommand();
175 if (paths.empty() || command_type == NEW_PATH) { 180
181 auto start_new_path = [&paths]() {
176 paths.push_back(SkPath()); 182 paths.push_back(SkPath());
177 paths.back().setFillType(SkPath::kEvenOdd_FillType); 183 paths.back().setFillType(SkPath::kEvenOdd_FillType);
178 184 };
185 auto start_new_flags = [&flags_array, &color]() {
179 flags_array.push_back(cc::PaintFlags()); 186 flags_array.push_back(cc::PaintFlags());
180 flags_array.back().setColor(color); 187 flags_array.back().setColor(color);
181 flags_array.back().setAntiAlias(true); 188 flags_array.back().setAntiAlias(true);
182 flags_array.back().setStrokeCap(cc::PaintFlags::kRound_Cap); 189 flags_array.back().setStrokeCap(cc::PaintFlags::kRound_Cap);
190 };
191
192 if (paths.empty() || command_type == NEW_PATH) {
193 start_new_path();
194 start_new_flags();
183 } 195 }
184 196
185 SkPath& path = paths.back(); 197 SkPath& path = paths.back();
186 cc::PaintFlags& flags = flags_array.back(); 198 cc::PaintFlags& flags = flags_array.back();
187 switch (command_type) { 199 switch (command_type) {
188 // Handled above. 200 // Handled above.
189 case NEW_PATH: 201 case NEW_PATH:
190 break; 202 break;
191 203
192 case PATH_COLOR_ARGB: 204 case PATH_COLOR_ARGB:
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
332 break; 344 break;
333 345
334 case DISABLE_AA: 346 case DISABLE_AA:
335 flags.setAntiAlias(false); 347 flags.setAntiAlias(false);
336 break; 348 break;
337 349
338 case FLIPS_IN_RTL: 350 case FLIPS_IN_RTL:
339 flips_in_rtl = true; 351 flips_in_rtl = true;
340 break; 352 break;
341 353
354 // Transitions work by pushing new paths and a new set of flags onto the
355 // stack. When TRANSITION_END is seen, the paths and flags are
356 // interpolated based on |elapsed_time| and the tween type.
357 case TRANSITION_FROM: {
358 start_new_path();
tdanderson 2017/05/09 20:31:40 I think you want to push a copy of flags_array.bac
Evan Stade 2017/05/09 21:25:04 The current implementation is correct. For pathing
tdanderson 2017/05/09 21:54:18 This makes sense, thank you for the clear explanat
359 break;
360 }
361
362 case TRANSITION_TO: {
363 start_new_path();
364 start_new_flags();
365 break;
366 }
367
368 case TRANSITION_END: {
369 DCHECK_GT(paths.size(), 2U);
370
371 const base::TimeDelta delay =
372 base::TimeDelta::FromMillisecondsD(SkScalarToDouble(arg(0)));
373 const base::TimeDelta duration =
374 base::TimeDelta::FromMillisecondsD(SkScalarToDouble(arg(1)));
375 const base::TimeDelta current_time =
376 elapsed_time ? *elapsed_time : base::TimeDelta();
377
378 double state = 0;
379 if (current_time >= delay + duration) {
380 state = 1;
381 } else if (current_time > delay) {
382 state = (current_time - delay).ToInternalValue() /
383 static_cast<double>(duration.ToInternalValue());
384 }
385
386 auto weight =
387 Tween::CalculateValue(static_cast<Tween::Type>(arg(2)), state);
388
389 SkPath path1, path2;
390 path1.swap(paths.back());
391 paths.pop_back();
392 path2.swap(paths.back());
393 paths.pop_back();
394
395 SkPath interpolated_path;
396 bool could_interpolate =
397 path1.interpolate(path2, weight, &interpolated_path);
398 DCHECK(could_interpolate);
399 paths.back().addPath(interpolated_path);
400
401 // Perform manual interpolation of flags properties. TODO(estade): fill
402 // more of these in as necessary.
403 DCHECK_GT(flags_array.size(), 1U);
404 cc::PaintFlags& end_flags = flags_array.back();
405 cc::PaintFlags& start_flags = flags_array[flags_array.size() - 2];
406
407 start_flags.setColor(Tween::ColorValueBetween(
408 weight, start_flags.getColor(), end_flags.getColor()));
409 start_flags.setStrokeWidth(Tween::FloatValueBetween(
410 weight, start_flags.getStrokeWidth(), end_flags.getStrokeWidth()));
411
412 flags_array.pop_back();
413 break;
414 }
415
342 case END: 416 case END:
343 NOTREACHED(); 417 NOTREACHED();
344 break; 418 break;
345 } 419 }
346 420
347 previous_command_type = command_type; 421 previous_command_type = command_type;
348 } 422 }
349 423
350 gfx::ScopedRTLFlipCanvas scoped_rtl_flip_canvas(canvas, canvas_size, 424 ScopedRTLFlipCanvas scoped_rtl_flip_canvas(canvas, canvas_size, flips_in_rtl);
351 flips_in_rtl);
352 425
353 if (dip_size != canvas_size) { 426 if (dip_size != canvas_size) {
354 SkScalar scale = SkIntToScalar(dip_size) / SkIntToScalar(canvas_size); 427 SkScalar scale = SkIntToScalar(dip_size) / SkIntToScalar(canvas_size);
355 canvas->sk_canvas()->scale(scale, scale); 428 canvas->sk_canvas()->scale(scale, scale);
356 } 429 }
357 430
358 if (!clip_rect.isEmpty()) 431 if (!clip_rect.isEmpty())
359 canvas->sk_canvas()->clipRect(clip_rect); 432 canvas->sk_canvas()->clipRect(clip_rect);
360 433
361 DCHECK_EQ(flags_array.size(), paths.size()); 434 DCHECK_EQ(flags_array.size(), paths.size());
362 for (size_t i = 0; i < paths.size(); ++i) 435 for (size_t i = 0; i < paths.size(); ++i)
363 canvas->DrawPath(paths[i], flags_array[i]); 436 canvas->DrawPath(paths[i], flags_array[i]);
364 } 437 }
365 438
366 class VectorIconSource : public CanvasImageSource { 439 class VectorIconSource : public CanvasImageSource {
367 public: 440 public:
368 VectorIconSource(const VectorIcon& icon, 441 VectorIconSource(const VectorIcon& icon,
369 int dip_size, 442 int dip_size,
370 SkColor color, 443 SkColor color,
371 const VectorIcon& badge_icon) 444 const VectorIcon& badge_icon)
372 : CanvasImageSource(gfx::Size(dip_size, dip_size), false), 445 : CanvasImageSource(Size(dip_size, dip_size), false),
373 color_(color), 446 color_(color),
374 icon_(icon), 447 icon_(icon),
375 badge_(badge_icon) {} 448 badge_(badge_icon) {}
376 449
377 VectorIconSource(const std::string& definition, int dip_size, SkColor color) 450 VectorIconSource(const std::string& definition, int dip_size, SkColor color)
378 : CanvasImageSource(gfx::Size(dip_size, dip_size), false), 451 : CanvasImageSource(Size(dip_size, dip_size), false),
379 color_(color), 452 color_(color),
380 icon_(kNoneIcon), 453 icon_(kNoneIcon),
381 badge_(kNoneIcon), 454 badge_(kNoneIcon),
382 path_(PathFromSource(definition)) {} 455 path_(PathFromSource(definition)) {}
383 456
384 ~VectorIconSource() override {} 457 ~VectorIconSource() override {}
385 458
386 // CanvasImageSource: 459 // CanvasImageSource:
387 bool HasRepresentationAtAllScales() const override { 460 bool HasRepresentationAtAllScales() const override {
388 return !icon_.is_empty(); 461 return !icon_.is_empty();
389 } 462 }
390 463
391 void Draw(gfx::Canvas* canvas) override { 464 void Draw(Canvas* canvas) override {
392 if (path_.empty()) { 465 if (path_.empty()) {
393 PaintVectorIcon(canvas, icon_, size_.width(), color_); 466 PaintVectorIcon(canvas, icon_, size_.width(), color_);
394 if (!badge_.is_empty()) 467 if (!badge_.is_empty())
395 PaintVectorIcon(canvas, badge_, size_.width(), color_); 468 PaintVectorIcon(canvas, badge_, size_.width(), color_);
396 } else { 469 } else {
397 PaintPath(canvas, path_.data(), size_.width(), color_); 470 PaintPath(canvas, path_.data(), size_.width(), color_);
398 } 471 }
399 } 472 }
400 473
401 private: 474 private:
(...skipping 17 matching lines...) Expand all
419 int dip_size, 492 int dip_size,
420 SkColor color, 493 SkColor color,
421 const VectorIcon& badge_icon) { 494 const VectorIcon& badge_icon) {
422 IconDescription description(&icon, dip_size, color, &badge_icon); 495 IconDescription description(&icon, dip_size, color, &badge_icon);
423 auto iter = images_.find(description); 496 auto iter = images_.find(description);
424 if (iter != images_.end()) 497 if (iter != images_.end())
425 return iter->second; 498 return iter->second;
426 499
427 ImageSkia icon_image( 500 ImageSkia icon_image(
428 new VectorIconSource(icon, dip_size, color, badge_icon), 501 new VectorIconSource(icon, dip_size, color, badge_icon),
429 gfx::Size(dip_size, dip_size)); 502 Size(dip_size, dip_size));
430 images_.insert(std::make_pair(description, icon_image)); 503 images_.insert(std::make_pair(description, icon_image));
431 return icon_image; 504 return icon_image;
432 } 505 }
433 506
434 private: 507 private:
435 struct IconDescription { 508 struct IconDescription {
436 IconDescription(const VectorIcon* icon, 509 IconDescription(const VectorIcon* icon,
437 int dip_size, 510 int dip_size,
438 SkColor color, 511 SkColor color,
439 const VectorIcon* badge_icon) 512 const VectorIcon* badge_icon)
440 : icon(icon), 513 : icon(icon),
441 dip_size(dip_size), 514 dip_size(dip_size),
442 color(color), 515 color(color),
443 badge_icon(badge_icon) {} 516 badge_icon(badge_icon) {}
444 517
445 bool operator<(const IconDescription& other) const { 518 bool operator<(const IconDescription& other) const {
446 return std::tie(icon, dip_size, color, badge_icon) < 519 return std::tie(icon, dip_size, color, badge_icon) <
447 std::tie(other.icon, other.dip_size, other.color, 520 std::tie(other.icon, other.dip_size, other.color,
448 other.badge_icon); 521 other.badge_icon);
449 } 522 }
450 523
451 const gfx::VectorIcon* icon; 524 const VectorIcon* icon;
452 int dip_size; 525 int dip_size;
453 SkColor color; 526 SkColor color;
454 const gfx::VectorIcon* badge_icon; 527 const VectorIcon* badge_icon;
455 }; 528 };
456 529
457 std::map<IconDescription, ImageSkia> images_; 530 std::map<IconDescription, ImageSkia> images_;
458 531
459 DISALLOW_COPY_AND_ASSIGN(VectorIconCache); 532 DISALLOW_COPY_AND_ASSIGN(VectorIconCache);
460 }; 533 };
461 534
462 static base::LazyInstance<VectorIconCache>::DestructorAtExit g_icon_cache = 535 static base::LazyInstance<VectorIconCache>::DestructorAtExit g_icon_cache =
463 LAZY_INSTANCE_INITIALIZER; 536 LAZY_INSTANCE_INITIALIZER;
464 537
465 } // namespace 538 } // namespace
466 539
467 const VectorIcon kNoneIcon = {}; 540 const VectorIcon kNoneIcon = {};
468 541
469 void PaintVectorIcon(Canvas* canvas, const VectorIcon& icon, SkColor color) { 542 void PaintVectorIcon(Canvas* canvas,
470 PaintVectorIcon(canvas, icon, GetDefaultSizeOfVectorIcon(icon), color); 543 const VectorIcon& icon,
544 SkColor color,
545 const base::TimeDelta* elapsed_time) {
546 PaintVectorIcon(canvas, icon, GetDefaultSizeOfVectorIcon(icon), color,
547 elapsed_time);
471 } 548 }
472 549
473 void PaintVectorIcon(Canvas* canvas, 550 void PaintVectorIcon(Canvas* canvas,
474 const VectorIcon& icon, 551 const VectorIcon& icon,
475 int dip_size, 552 int dip_size,
476 SkColor color) { 553 SkColor color,
554 const base::TimeDelta* elapsed_time) {
477 DCHECK(!icon.is_empty()); 555 DCHECK(!icon.is_empty());
478 const PathElement* path = (canvas->image_scale() == 1.f && icon.path_1x_) 556 const PathElement* path = (canvas->image_scale() == 1.f && icon.path_1x_)
479 ? icon.path_1x_ 557 ? icon.path_1x_
480 : icon.path_; 558 : icon.path_;
481 PaintPath(canvas, path, dip_size, color); 559 PaintPath(canvas, path, dip_size, color, elapsed_time);
482 } 560 }
483 561
484 ImageSkia CreateVectorIcon(const VectorIcon& icon, SkColor color) { 562 ImageSkia CreateVectorIcon(const VectorIcon& icon, SkColor color) {
485 return CreateVectorIcon(icon, GetDefaultSizeOfVectorIcon(icon), color); 563 return CreateVectorIcon(icon, GetDefaultSizeOfVectorIcon(icon), color);
486 } 564 }
487 565
488 ImageSkia CreateVectorIcon(const VectorIcon& icon, 566 ImageSkia CreateVectorIcon(const VectorIcon& icon,
489 int dip_size, 567 int dip_size,
490 SkColor color) { 568 SkColor color) {
491 return CreateVectorIconWithBadge(icon, dip_size, color, kNoneIcon); 569 return CreateVectorIconWithBadge(icon, dip_size, color, kNoneIcon);
492 } 570 }
493 571
494 ImageSkia CreateVectorIconWithBadge(const VectorIcon& icon, 572 ImageSkia CreateVectorIconWithBadge(const VectorIcon& icon,
495 int dip_size, 573 int dip_size,
496 SkColor color, 574 SkColor color,
497 const VectorIcon& badge_icon) { 575 const VectorIcon& badge_icon) {
498 return icon.is_empty() ? gfx::ImageSkia() 576 return icon.is_empty() ? ImageSkia()
499 : g_icon_cache.Get().GetOrCreateIcon( 577 : g_icon_cache.Get().GetOrCreateIcon(
500 icon, dip_size, color, badge_icon); 578 icon, dip_size, color, badge_icon);
501 } 579 }
502 580
503 ImageSkia CreateVectorIconFromSource(const std::string& source, 581 ImageSkia CreateVectorIconFromSource(const std::string& source,
504 int dip_size, 582 int dip_size,
505 SkColor color) { 583 SkColor color) {
506 return CanvasImageSource::MakeImageSkia<VectorIconSource>(source, dip_size, 584 return CanvasImageSource::MakeImageSkia<VectorIconSource>(source, dip_size,
507 color); 585 color);
508 } 586 }
509 587
510 int GetDefaultSizeOfVectorIcon(const gfx::VectorIcon& icon) { 588 int GetDefaultSizeOfVectorIcon(const VectorIcon& icon) {
511 const PathElement* one_x_path = icon.path_1x_ ? icon.path_1x_ : icon.path_; 589 const PathElement* one_x_path = icon.path_1x_ ? icon.path_1x_ : icon.path_;
512 return one_x_path[0].command == CANVAS_DIMENSIONS ? one_x_path[1].arg 590 return one_x_path[0].command == CANVAS_DIMENSIONS ? one_x_path[1].arg
513 : kReferenceSizeDip; 591 : kReferenceSizeDip;
514 } 592 }
515 593
594 base::TimeDelta GetDurationOfAnimation(const VectorIcon& icon) {
595 base::TimeDelta last_motion;
596 PathParser parser(icon.path_);
597 while (parser.Advance()) {
tdanderson 2017/05/09 20:31:40 You will need to change this condition since Advan
Evan Stade 2017/05/09 22:45:14 Done.
598 if (parser.CurrentCommand() != TRANSITION_END)
599 continue;
600
601 auto end_time = base::TimeDelta::FromMillisecondsD(parser.GetArgument(0)) +
602 base::TimeDelta::FromMillisecondsD(parser.GetArgument(1));
603 if (end_time > last_motion)
604 last_motion = end_time;
605 }
606 return last_motion;
sadrul 2017/05/09 22:12:03 Can this be cached in VectorIcon? e.g. Optional<ba
Evan Stade 2017/05/09 22:45:14 It will be cached by the call site. I suppose I co
607 }
608
516 } // namespace gfx 609 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698