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

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

Issue 2870643003: Add support for animations in vector icons. (Closed)
Patch Set: what happened 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) {
sadrul 2017/05/11 04:22:55 Can this just be a const-ref? const base::TimeD
Evan Stade 2017/05/11 16:16:26 Done.
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 auto start_new_path = [&paths]() {
176 paths.push_back(SkPath()); 181 paths.push_back(SkPath());
177 paths.back().setFillType(SkPath::kEvenOdd_FillType); 182 paths.back().setFillType(SkPath::kEvenOdd_FillType);
178 183 };
184 auto start_new_flags = [&flags_array, &color]() {
179 flags_array.push_back(cc::PaintFlags()); 185 flags_array.push_back(cc::PaintFlags());
180 flags_array.back().setColor(color); 186 flags_array.back().setColor(color);
181 flags_array.back().setAntiAlias(true); 187 flags_array.back().setAntiAlias(true);
182 flags_array.back().setStrokeCap(cc::PaintFlags::kRound_Cap); 188 flags_array.back().setStrokeCap(cc::PaintFlags::kRound_Cap);
189 };
190
191 if (paths.empty() || command_type == NEW_PATH) {
192 start_new_path();
193 start_new_flags();
183 } 194 }
184 195
185 SkPath& path = paths.back(); 196 SkPath& path = paths.back();
186 cc::PaintFlags& flags = flags_array.back(); 197 cc::PaintFlags& flags = flags_array.back();
187 switch (command_type) { 198 switch (command_type) {
188 // Handled above. 199 // Handled above.
189 case NEW_PATH: 200 case NEW_PATH:
190 break; 201 break;
191 202
192 case PATH_COLOR_ARGB: 203 case PATH_COLOR_ARGB:
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
332 break; 343 break;
333 344
334 case DISABLE_AA: 345 case DISABLE_AA:
335 flags.setAntiAlias(false); 346 flags.setAntiAlias(false);
336 break; 347 break;
337 348
338 case FLIPS_IN_RTL: 349 case FLIPS_IN_RTL:
339 flips_in_rtl = true; 350 flips_in_rtl = true;
340 break; 351 break;
341 352
353 // Transitions work by pushing new paths and a new set of flags onto the
354 // stack. When TRANSITION_END is seen, the paths and flags are
355 // interpolated based on |elapsed_time| and the tween type.
356 case TRANSITION_FROM: {
357 start_new_path();
358 break;
359 }
360
361 case TRANSITION_TO: {
362 start_new_path();
363 start_new_flags();
364 break;
365 }
366
367 case TRANSITION_END: {
368 DCHECK_GT(paths.size(), 2U);
369
370 const base::TimeDelta delay =
371 base::TimeDelta::FromMillisecondsD(SkScalarToDouble(arg(0)));
372 const base::TimeDelta duration =
373 base::TimeDelta::FromMillisecondsD(SkScalarToDouble(arg(1)));
374 const base::TimeDelta current_time =
375 elapsed_time ? *elapsed_time : base::TimeDelta();
376
377 double state = 0;
378 if (current_time >= delay + duration) {
379 state = 1;
380 } else if (current_time > delay) {
381 state = (current_time - delay).ToInternalValue() /
382 static_cast<double>(duration.ToInternalValue());
383 }
384
385 auto weight =
386 Tween::CalculateValue(static_cast<Tween::Type>(arg(2)), state);
387
388 SkPath path1, path2;
389 path1.swap(paths.back());
390 paths.pop_back();
391 path2.swap(paths.back());
392 paths.pop_back();
393
394 SkPath interpolated_path;
395 bool could_interpolate =
396 path1.interpolate(path2, weight, &interpolated_path);
sadrul 2017/05/11 04:22:55 Do you have a sense of how costly this interpolati
Evan Stade 2017/05/11 16:16:26 it should be cheap. For each argument it's just ta
sadrul 2017/05/11 17:36:38 Let's add TRACE_EVENT now, and a TODO referencing
397 DCHECK(could_interpolate);
398 paths.back().addPath(interpolated_path);
399
400 // Perform manual interpolation of flags properties. TODO(estade): fill
401 // more of these in as necessary.
402 DCHECK_GT(flags_array.size(), 1U);
403 cc::PaintFlags& end_flags = flags_array.back();
404 cc::PaintFlags& start_flags = flags_array[flags_array.size() - 2];
405
406 start_flags.setColor(Tween::ColorValueBetween(
407 weight, start_flags.getColor(), end_flags.getColor()));
408
409 flags_array.pop_back();
410 break;
411 }
412
342 case END: 413 case END:
343 NOTREACHED(); 414 NOTREACHED();
344 break; 415 break;
345 } 416 }
346 417
347 previous_command_type = command_type; 418 previous_command_type = command_type;
348 } 419 }
349 420
350 gfx::ScopedRTLFlipCanvas scoped_rtl_flip_canvas(canvas, canvas_size, 421 ScopedRTLFlipCanvas scoped_rtl_flip_canvas(canvas, canvas_size, flips_in_rtl);
351 flips_in_rtl);
352 422
353 if (dip_size != canvas_size) { 423 if (dip_size != canvas_size) {
354 SkScalar scale = SkIntToScalar(dip_size) / SkIntToScalar(canvas_size); 424 SkScalar scale = SkIntToScalar(dip_size) / SkIntToScalar(canvas_size);
355 canvas->sk_canvas()->scale(scale, scale); 425 canvas->sk_canvas()->scale(scale, scale);
356 } 426 }
357 427
358 if (!clip_rect.isEmpty()) 428 if (!clip_rect.isEmpty())
359 canvas->sk_canvas()->clipRect(clip_rect); 429 canvas->sk_canvas()->clipRect(clip_rect);
360 430
361 DCHECK_EQ(flags_array.size(), paths.size()); 431 DCHECK_EQ(flags_array.size(), paths.size());
362 for (size_t i = 0; i < paths.size(); ++i) 432 for (size_t i = 0; i < paths.size(); ++i)
363 canvas->DrawPath(paths[i], flags_array[i]); 433 canvas->DrawPath(paths[i], flags_array[i]);
364 } 434 }
365 435
366 class VectorIconSource : public CanvasImageSource { 436 class VectorIconSource : public CanvasImageSource {
367 public: 437 public:
368 VectorIconSource(const VectorIcon& icon, 438 VectorIconSource(const VectorIcon& icon,
369 int dip_size, 439 int dip_size,
370 SkColor color, 440 SkColor color,
371 const VectorIcon& badge_icon) 441 const VectorIcon& badge_icon)
372 : CanvasImageSource(gfx::Size(dip_size, dip_size), false), 442 : CanvasImageSource(Size(dip_size, dip_size), false),
373 color_(color), 443 color_(color),
374 icon_(icon), 444 icon_(icon),
375 badge_(badge_icon) {} 445 badge_(badge_icon) {}
376 446
377 VectorIconSource(const std::string& definition, int dip_size, SkColor color) 447 VectorIconSource(const std::string& definition, int dip_size, SkColor color)
378 : CanvasImageSource(gfx::Size(dip_size, dip_size), false), 448 : CanvasImageSource(Size(dip_size, dip_size), false),
379 color_(color), 449 color_(color),
380 icon_(kNoneIcon), 450 icon_(kNoneIcon),
381 badge_(kNoneIcon), 451 badge_(kNoneIcon),
382 path_(PathFromSource(definition)) {} 452 path_(PathFromSource(definition)) {}
383 453
384 ~VectorIconSource() override {} 454 ~VectorIconSource() override {}
385 455
386 // CanvasImageSource: 456 // CanvasImageSource:
387 bool HasRepresentationAtAllScales() const override { 457 bool HasRepresentationAtAllScales() const override {
388 return !icon_.is_empty(); 458 return !icon_.is_empty();
389 } 459 }
390 460
391 void Draw(gfx::Canvas* canvas) override { 461 void Draw(Canvas* canvas) override {
392 if (path_.empty()) { 462 if (path_.empty()) {
393 PaintVectorIcon(canvas, icon_, size_.width(), color_); 463 PaintVectorIcon(canvas, icon_, size_.width(), color_);
394 if (!badge_.is_empty()) 464 if (!badge_.is_empty())
395 PaintVectorIcon(canvas, badge_, size_.width(), color_); 465 PaintVectorIcon(canvas, badge_, size_.width(), color_);
396 } else { 466 } else {
397 PaintPath(canvas, path_.data(), size_.width(), color_); 467 PaintPath(canvas, path_.data(), size_.width(), color_);
398 } 468 }
399 } 469 }
400 470
401 private: 471 private:
(...skipping 17 matching lines...) Expand all
419 int dip_size, 489 int dip_size,
420 SkColor color, 490 SkColor color,
421 const VectorIcon& badge_icon) { 491 const VectorIcon& badge_icon) {
422 IconDescription description(&icon, dip_size, color, &badge_icon); 492 IconDescription description(&icon, dip_size, color, &badge_icon);
423 auto iter = images_.find(description); 493 auto iter = images_.find(description);
424 if (iter != images_.end()) 494 if (iter != images_.end())
425 return iter->second; 495 return iter->second;
426 496
427 ImageSkia icon_image( 497 ImageSkia icon_image(
428 new VectorIconSource(icon, dip_size, color, badge_icon), 498 new VectorIconSource(icon, dip_size, color, badge_icon),
429 gfx::Size(dip_size, dip_size)); 499 Size(dip_size, dip_size));
430 images_.insert(std::make_pair(description, icon_image)); 500 images_.insert(std::make_pair(description, icon_image));
431 return icon_image; 501 return icon_image;
432 } 502 }
433 503
434 private: 504 private:
435 struct IconDescription { 505 struct IconDescription {
436 IconDescription(const VectorIcon* icon, 506 IconDescription(const VectorIcon* icon,
437 int dip_size, 507 int dip_size,
438 SkColor color, 508 SkColor color,
439 const VectorIcon* badge_icon) 509 const VectorIcon* badge_icon)
440 : icon(icon), 510 : icon(icon),
441 dip_size(dip_size), 511 dip_size(dip_size),
442 color(color), 512 color(color),
443 badge_icon(badge_icon) {} 513 badge_icon(badge_icon) {}
444 514
445 bool operator<(const IconDescription& other) const { 515 bool operator<(const IconDescription& other) const {
446 return std::tie(icon, dip_size, color, badge_icon) < 516 return std::tie(icon, dip_size, color, badge_icon) <
447 std::tie(other.icon, other.dip_size, other.color, 517 std::tie(other.icon, other.dip_size, other.color,
448 other.badge_icon); 518 other.badge_icon);
449 } 519 }
450 520
451 const gfx::VectorIcon* icon; 521 const VectorIcon* icon;
452 int dip_size; 522 int dip_size;
453 SkColor color; 523 SkColor color;
454 const gfx::VectorIcon* badge_icon; 524 const VectorIcon* badge_icon;
455 }; 525 };
456 526
457 std::map<IconDescription, ImageSkia> images_; 527 std::map<IconDescription, ImageSkia> images_;
458 528
459 DISALLOW_COPY_AND_ASSIGN(VectorIconCache); 529 DISALLOW_COPY_AND_ASSIGN(VectorIconCache);
460 }; 530 };
461 531
462 static base::LazyInstance<VectorIconCache>::DestructorAtExit g_icon_cache = 532 static base::LazyInstance<VectorIconCache>::DestructorAtExit g_icon_cache =
463 LAZY_INSTANCE_INITIALIZER; 533 LAZY_INSTANCE_INITIALIZER;
464 534
465 } // namespace 535 } // namespace
466 536
467 const VectorIcon kNoneIcon = {}; 537 const VectorIcon kNoneIcon = {};
468 538
469 void PaintVectorIcon(Canvas* canvas, const VectorIcon& icon, SkColor color) { 539 void PaintVectorIcon(Canvas* canvas,
470 PaintVectorIcon(canvas, icon, GetDefaultSizeOfVectorIcon(icon), color); 540 const VectorIcon& icon,
541 SkColor color,
542 const base::TimeDelta* elapsed_time) {
543 PaintVectorIcon(canvas, icon, GetDefaultSizeOfVectorIcon(icon), color,
544 elapsed_time);
471 } 545 }
472 546
473 void PaintVectorIcon(Canvas* canvas, 547 void PaintVectorIcon(Canvas* canvas,
474 const VectorIcon& icon, 548 const VectorIcon& icon,
475 int dip_size, 549 int dip_size,
476 SkColor color) { 550 SkColor color,
551 const base::TimeDelta* elapsed_time) {
477 DCHECK(!icon.is_empty()); 552 DCHECK(!icon.is_empty());
478 const PathElement* path = (canvas->image_scale() == 1.f && icon.path_1x_) 553 const PathElement* path = (canvas->image_scale() == 1.f && icon.path_1x_)
479 ? icon.path_1x_ 554 ? icon.path_1x_
480 : icon.path_; 555 : icon.path_;
481 PaintPath(canvas, path, dip_size, color); 556 PaintPath(canvas, path, dip_size, color, elapsed_time);
482 } 557 }
483 558
484 ImageSkia CreateVectorIcon(const VectorIcon& icon, SkColor color) { 559 ImageSkia CreateVectorIcon(const VectorIcon& icon, SkColor color) {
485 return CreateVectorIcon(icon, GetDefaultSizeOfVectorIcon(icon), color); 560 return CreateVectorIcon(icon, GetDefaultSizeOfVectorIcon(icon), color);
486 } 561 }
487 562
488 ImageSkia CreateVectorIcon(const VectorIcon& icon, 563 ImageSkia CreateVectorIcon(const VectorIcon& icon,
489 int dip_size, 564 int dip_size,
490 SkColor color) { 565 SkColor color) {
491 return CreateVectorIconWithBadge(icon, dip_size, color, kNoneIcon); 566 return CreateVectorIconWithBadge(icon, dip_size, color, kNoneIcon);
492 } 567 }
493 568
494 ImageSkia CreateVectorIconWithBadge(const VectorIcon& icon, 569 ImageSkia CreateVectorIconWithBadge(const VectorIcon& icon,
495 int dip_size, 570 int dip_size,
496 SkColor color, 571 SkColor color,
497 const VectorIcon& badge_icon) { 572 const VectorIcon& badge_icon) {
498 return icon.is_empty() ? gfx::ImageSkia() 573 return icon.is_empty() ? ImageSkia()
499 : g_icon_cache.Get().GetOrCreateIcon( 574 : g_icon_cache.Get().GetOrCreateIcon(
500 icon, dip_size, color, badge_icon); 575 icon, dip_size, color, badge_icon);
501 } 576 }
502 577
503 ImageSkia CreateVectorIconFromSource(const std::string& source, 578 ImageSkia CreateVectorIconFromSource(const std::string& source,
504 int dip_size, 579 int dip_size,
505 SkColor color) { 580 SkColor color) {
506 return CanvasImageSource::MakeImageSkia<VectorIconSource>(source, dip_size, 581 return CanvasImageSource::MakeImageSkia<VectorIconSource>(source, dip_size,
507 color); 582 color);
508 } 583 }
509 584
510 int GetDefaultSizeOfVectorIcon(const gfx::VectorIcon& icon) { 585 int GetDefaultSizeOfVectorIcon(const VectorIcon& icon) {
511 const PathElement* one_x_path = icon.path_1x_ ? icon.path_1x_ : icon.path_; 586 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 587 return one_x_path[0].command == CANVAS_DIMENSIONS ? one_x_path[1].arg
513 : kReferenceSizeDip; 588 : kReferenceSizeDip;
514 } 589 }
515 590
591 base::TimeDelta GetDurationOfAnimation(const VectorIcon& icon) {
592 base::TimeDelta last_motion;
593 for (PathParser parser(icon.path); parser.CurrentCommand() != END;
594 parser.Advance()) {
595 if (parser.CurrentCommand() != TRANSITION_END)
596 continue;
597
598 auto end_time = base::TimeDelta::FromMillisecondsD(parser.GetArgument(0)) +
599 base::TimeDelta::FromMillisecondsD(parser.GetArgument(1));
600 if (end_time > last_motion)
601 last_motion = end_time;
602 }
603 return last_motion;
604 }
605
516 } // namespace gfx 606 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698