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

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

Issue 2870643003: Add support for animations in vector icons. (Closed)
Patch Set: 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"
tdanderson 2017/05/08 21:22:54 general question: are the performance characterist
Evan Stade 2017/05/08 22:27:41 I don't see why they wouldn't be acceptable. We ma
tdanderson 2017/05/09 20:31:40 I remember Sadrul noting some potential concerns i
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 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
60 case CANVAS_DIMENSIONS: 61 case CANVAS_DIMENSIONS:
61 return 1; 62 return 1;
62 63
63 case MOVE_TO: 64 case MOVE_TO:
64 case R_MOVE_TO: 65 case R_MOVE_TO:
65 case LINE_TO: 66 case LINE_TO:
66 case R_LINE_TO: 67 case R_LINE_TO:
67 return 2; 68 return 2;
68 69
69 case CIRCLE: 70 case CIRCLE:
71 case TRANSITION_END:
70 return 3; 72 return 3;
71 73
72 case PATH_COLOR_ARGB: 74 case PATH_COLOR_ARGB:
73 case CUBIC_TO_SHORTHAND: 75 case CUBIC_TO_SHORTHAND:
74 case CLIP: 76 case CLIP:
75 return 4; 77 return 4;
76 78
77 case ROUND_RECT: 79 case ROUND_RECT:
78 return 5; 80 return 5;
79 81
80 case CUBIC_TO: 82 case CUBIC_TO:
81 case R_CUBIC_TO: 83 case R_CUBIC_TO:
82 return 6; 84 return 6;
83 85
84 case ARC_TO: 86 case ARC_TO:
85 case R_ARC_TO: 87 case R_ARC_TO:
86 return 7; 88 return 7;
87 89
88 case NEW_PATH: 90 case NEW_PATH:
89 case PATH_MODE_CLEAR: 91 case PATH_MODE_CLEAR:
90 case CAP_SQUARE: 92 case CAP_SQUARE:
91 case CLOSE: 93 case CLOSE:
92 case DISABLE_AA: 94 case DISABLE_AA:
93 case FLIPS_IN_RTL: 95 case FLIPS_IN_RTL:
96 case TRANSITION_FROM:
97 case TRANSITION_TO:
94 case END: 98 case END:
95 return 0; 99 return 0;
96 } 100 }
97 101
98 NOTREACHED(); 102 NOTREACHED();
99 return 0; 103 return 0;
100 } 104 }
101 105
102 const PathElement* path_elements_; 106 const PathElement* path_elements_;
103 int command_index_ = -1; 107 int command_index_ = -1;
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
156 path.push_back(PathElement(SkIntToScalar(hex_value))); 160 path.push_back(PathElement(SkIntToScalar(hex_value)));
157 else 161 else
158 path.push_back(PathElement(CommandFromString(piece))); 162 path.push_back(PathElement(CommandFromString(piece)));
159 } 163 }
160 return path; 164 return path;
161 } 165 }
162 166
163 void PaintPath(Canvas* canvas, 167 void PaintPath(Canvas* canvas,
164 const PathElement* path_elements, 168 const PathElement* path_elements,
165 int dip_size, 169 int dip_size,
166 SkColor color) { 170 SkColor color,
171 const base::TimeDelta* elapsed_time = nullptr) {
167 SkPath path; 172 SkPath path;
168 path.setFillType(SkPath::kEvenOdd_FillType); 173 path.setFillType(SkPath::kEvenOdd_FillType);
169 174
170 int canvas_size = kReferenceSizeDip; 175 int canvas_size = kReferenceSizeDip;
171 std::vector<SkPath> paths; 176 std::vector<SkPath> paths;
172 std::vector<cc::PaintFlags> flags_array; 177 std::vector<cc::PaintFlags> flags_array;
173 SkRect clip_rect = SkRect::MakeEmpty(); 178 SkRect clip_rect = SkRect::MakeEmpty();
174 bool flips_in_rtl = false; 179 bool flips_in_rtl = false;
175 CommandType previous_command_type = NEW_PATH; 180 CommandType previous_command_type = NEW_PATH;
176 181
177 PathParser parser(path_elements); 182 PathParser parser(path_elements);
178 auto arg = [&parser](int i) { return parser.GetArgument(i); }; 183 auto arg = [&parser](int i) { return parser.GetArgument(i); };
179 184
180 while (parser.Advance()) { 185 while (parser.Advance()) {
181 const CommandType command_type = parser.CurrentCommand(); 186 const CommandType command_type = parser.CurrentCommand();
182 if (paths.empty() || command_type == NEW_PATH) { 187
188 auto start_new_path = [&paths]() {
183 paths.push_back(SkPath()); 189 paths.push_back(SkPath());
184 paths.back().setFillType(SkPath::kEvenOdd_FillType); 190 paths.back().setFillType(SkPath::kEvenOdd_FillType);
185 191 };
192 auto start_new_flags = [&flags_array, &color]() {
186 flags_array.push_back(cc::PaintFlags()); 193 flags_array.push_back(cc::PaintFlags());
187 flags_array.back().setColor(color); 194 flags_array.back().setColor(color);
188 flags_array.back().setAntiAlias(true); 195 flags_array.back().setAntiAlias(true);
189 flags_array.back().setStrokeCap(cc::PaintFlags::kRound_Cap); 196 flags_array.back().setStrokeCap(cc::PaintFlags::kRound_Cap);
197 };
198
199 if (paths.empty() || command_type == NEW_PATH) {
200 start_new_path();
201 start_new_flags();
190 } 202 }
191 203
192 SkPath& path = paths.back(); 204 SkPath& path = paths.back();
193 cc::PaintFlags& flags = flags_array.back(); 205 cc::PaintFlags& flags = flags_array.back();
194 switch (command_type) { 206 switch (command_type) {
195 // Handled above. 207 // Handled above.
196 case NEW_PATH: 208 case NEW_PATH:
197 break; 209 break;
198 210
199 case PATH_COLOR_ARGB: 211 case PATH_COLOR_ARGB:
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
339 break; 351 break;
340 352
341 case DISABLE_AA: 353 case DISABLE_AA:
342 flags.setAntiAlias(false); 354 flags.setAntiAlias(false);
343 break; 355 break;
344 356
345 case FLIPS_IN_RTL: 357 case FLIPS_IN_RTL:
346 flips_in_rtl = true; 358 flips_in_rtl = true;
347 break; 359 break;
348 360
361 // Transitions work by pushing new paths and a new set of flags onto the
362 // stack. When TRANSITION_END is seen, the paths and flags are
363 // interpolated based on |elapsed_time| and the tween type.
364 case TRANSITION_FROM: {
365 start_new_path();
366 break;
367 }
368
369 case TRANSITION_TO: {
370 start_new_path();
371 start_new_flags();
372 break;
373 }
374
375 case TRANSITION_END: {
376 DCHECK_GT(paths.size(), 2U);
377
378 const base::TimeDelta delay =
379 base::TimeDelta::FromMillisecondsD(SkScalarToDouble(arg(0)));
380 const base::TimeDelta duration =
381 base::TimeDelta::FromMillisecondsD(SkScalarToDouble(arg(1)));
382 const base::TimeDelta current_time =
383 elapsed_time ? *elapsed_time : base::TimeDelta();
384
385 double state = 0;
386 if (current_time >= delay + duration) {
387 state = 1;
388 } else if (current_time > delay) {
389 state = (current_time - delay).ToInternalValue() /
390 static_cast<double>(duration.ToInternalValue());
391 }
392
393 auto weight =
394 Tween::CalculateValue(static_cast<Tween::Type>(arg(2)), state);
tdanderson 2017/05/08 21:22:54 I recognize that allowing non-numeric arguments wo
Evan Stade 2017/05/08 22:27:41 I don't think that's necessary. I haven't actually
tdanderson 2017/05/09 20:31:40 Acknowledged.
395
396 SkPath path1, path2;
397 path1.swap(paths.back());
398 paths.pop_back();
399 path2.swap(paths.back());
400 paths.pop_back();
401
402 SkPath interpolated_path;
403 bool could_interpolate =
404 path1.interpolate(path2, weight, &interpolated_path);
405 DCHECK(could_interpolate);
406 paths.back().addPath(interpolated_path);
407
408 // Perform manual interpolation of flags properties. TODO(estade): fill
409 // more of these in as necessary.
410 DCHECK_GT(flags_array.size(), 1U);
411 cc::PaintFlags& end_flags = flags_array.back();
412 cc::PaintFlags& start_flags = flags_array[flags_array.size() - 2];
413
414 start_flags.setColor(Tween::ColorValueBetween(
415 weight, start_flags.getColor(), end_flags.getColor()));
416 start_flags.setStrokeWidth(Tween::FloatValueBetween(
417 weight, start_flags.getStrokeWidth(), end_flags.getStrokeWidth()));
418
419 flags_array.pop_back();
420 break;
421 }
422
349 case END: 423 case END:
350 NOTREACHED(); 424 NOTREACHED();
351 break; 425 break;
352 } 426 }
353 427
354 previous_command_type = command_type; 428 previous_command_type = command_type;
355 } 429 }
356 430
357 gfx::ScopedRTLFlipCanvas scoped_rtl_flip_canvas(canvas, canvas_size, 431 ScopedRTLFlipCanvas scoped_rtl_flip_canvas(canvas, canvas_size, flips_in_rtl);
358 flips_in_rtl);
359 432
360 if (dip_size != canvas_size) { 433 if (dip_size != canvas_size) {
361 SkScalar scale = SkIntToScalar(dip_size) / SkIntToScalar(canvas_size); 434 SkScalar scale = SkIntToScalar(dip_size) / SkIntToScalar(canvas_size);
362 canvas->sk_canvas()->scale(scale, scale); 435 canvas->sk_canvas()->scale(scale, scale);
363 } 436 }
364 437
365 if (!clip_rect.isEmpty()) 438 if (!clip_rect.isEmpty())
366 canvas->sk_canvas()->clipRect(clip_rect); 439 canvas->sk_canvas()->clipRect(clip_rect);
367 440
368 DCHECK_EQ(flags_array.size(), paths.size()); 441 DCHECK_EQ(flags_array.size(), paths.size());
369 for (size_t i = 0; i < paths.size(); ++i) 442 for (size_t i = 0; i < paths.size(); ++i)
370 canvas->DrawPath(paths[i], flags_array[i]); 443 canvas->DrawPath(paths[i], flags_array[i]);
371 } 444 }
372 445
373 class VectorIconSource : public CanvasImageSource { 446 class VectorIconSource : public CanvasImageSource {
374 public: 447 public:
375 VectorIconSource(const VectorIcon& icon, 448 VectorIconSource(const VectorIcon& icon,
376 int dip_size, 449 int dip_size,
377 SkColor color, 450 SkColor color,
378 const VectorIcon& badge_icon) 451 const VectorIcon& badge_icon)
379 : CanvasImageSource(gfx::Size(dip_size, dip_size), false), 452 : CanvasImageSource(Size(dip_size, dip_size), false),
380 color_(color), 453 color_(color),
381 icon_(icon), 454 icon_(icon),
382 badge_(badge_icon) {} 455 badge_(badge_icon) {}
383 456
384 VectorIconSource(const std::string& definition, int dip_size, SkColor color) 457 VectorIconSource(const std::string& definition, int dip_size, SkColor color)
385 : CanvasImageSource(gfx::Size(dip_size, dip_size), false), 458 : CanvasImageSource(Size(dip_size, dip_size), false),
386 color_(color), 459 color_(color),
387 icon_(kNoneIcon), 460 icon_(kNoneIcon),
388 badge_(kNoneIcon), 461 badge_(kNoneIcon),
389 path_(PathFromSource(definition)) {} 462 path_(PathFromSource(definition)) {}
390 463
391 ~VectorIconSource() override {} 464 ~VectorIconSource() override {}
392 465
393 // CanvasImageSource: 466 // CanvasImageSource:
394 bool HasRepresentationAtAllScales() const override { 467 bool HasRepresentationAtAllScales() const override {
395 return !icon_.is_empty(); 468 return !icon_.is_empty();
396 } 469 }
397 470
398 void Draw(gfx::Canvas* canvas) override { 471 void Draw(Canvas* canvas) override {
399 if (path_.empty()) { 472 if (path_.empty()) {
400 PaintVectorIcon(canvas, icon_, size_.width(), color_); 473 PaintVectorIcon(canvas, icon_, size_.width(), color_);
401 if (!badge_.is_empty()) 474 if (!badge_.is_empty())
402 PaintVectorIcon(canvas, badge_, size_.width(), color_); 475 PaintVectorIcon(canvas, badge_, size_.width(), color_);
403 } else { 476 } else {
404 PaintPath(canvas, path_.data(), size_.width(), color_); 477 PaintPath(canvas, path_.data(), size_.width(), color_);
405 } 478 }
406 } 479 }
407 480
408 private: 481 private:
(...skipping 17 matching lines...) Expand all
426 int dip_size, 499 int dip_size,
427 SkColor color, 500 SkColor color,
428 const VectorIcon& badge_icon) { 501 const VectorIcon& badge_icon) {
429 IconDescription description(&icon, dip_size, color, &badge_icon); 502 IconDescription description(&icon, dip_size, color, &badge_icon);
430 auto iter = images_.find(description); 503 auto iter = images_.find(description);
431 if (iter != images_.end()) 504 if (iter != images_.end())
432 return iter->second; 505 return iter->second;
433 506
434 ImageSkia icon_image( 507 ImageSkia icon_image(
435 new VectorIconSource(icon, dip_size, color, badge_icon), 508 new VectorIconSource(icon, dip_size, color, badge_icon),
436 gfx::Size(dip_size, dip_size)); 509 Size(dip_size, dip_size));
437 images_.insert(std::make_pair(description, icon_image)); 510 images_.insert(std::make_pair(description, icon_image));
438 return icon_image; 511 return icon_image;
439 } 512 }
440 513
441 private: 514 private:
442 struct IconDescription { 515 struct IconDescription {
443 IconDescription(const VectorIcon* icon, 516 IconDescription(const VectorIcon* icon,
444 int dip_size, 517 int dip_size,
445 SkColor color, 518 SkColor color,
446 const VectorIcon* badge_icon) 519 const VectorIcon* badge_icon)
447 : icon(icon), 520 : icon(icon),
448 dip_size(dip_size), 521 dip_size(dip_size),
449 color(color), 522 color(color),
450 badge_icon(badge_icon) {} 523 badge_icon(badge_icon) {}
451 524
452 bool operator<(const IconDescription& other) const { 525 bool operator<(const IconDescription& other) const {
453 return std::tie(icon, dip_size, color, badge_icon) < 526 return std::tie(icon, dip_size, color, badge_icon) <
454 std::tie(other.icon, other.dip_size, other.color, 527 std::tie(other.icon, other.dip_size, other.color,
455 other.badge_icon); 528 other.badge_icon);
456 } 529 }
457 530
458 const gfx::VectorIcon* icon; 531 const VectorIcon* icon;
459 int dip_size; 532 int dip_size;
460 SkColor color; 533 SkColor color;
461 const gfx::VectorIcon* badge_icon; 534 const VectorIcon* badge_icon;
462 }; 535 };
463 536
464 std::map<IconDescription, ImageSkia> images_; 537 std::map<IconDescription, ImageSkia> images_;
465 538
466 DISALLOW_COPY_AND_ASSIGN(VectorIconCache); 539 DISALLOW_COPY_AND_ASSIGN(VectorIconCache);
467 }; 540 };
468 541
469 static base::LazyInstance<VectorIconCache>::DestructorAtExit g_icon_cache = 542 static base::LazyInstance<VectorIconCache>::DestructorAtExit g_icon_cache =
470 LAZY_INSTANCE_INITIALIZER; 543 LAZY_INSTANCE_INITIALIZER;
471 544
472 } // namespace 545 } // namespace
473 546
474 const VectorIcon kNoneIcon = {}; 547 const VectorIcon kNoneIcon = {};
475 548
476 void PaintVectorIcon(Canvas* canvas, const VectorIcon& icon, SkColor color) { 549 void PaintVectorIcon(Canvas* canvas,
477 PaintVectorIcon(canvas, icon, GetDefaultSizeOfVectorIcon(icon), color); 550 const VectorIcon& icon,
551 SkColor color,
552 const base::TimeDelta* elapsed_time) {
553 PaintVectorIcon(canvas, icon, GetDefaultSizeOfVectorIcon(icon), color,
554 elapsed_time);
478 } 555 }
479 556
480 void PaintVectorIcon(Canvas* canvas, 557 void PaintVectorIcon(Canvas* canvas,
481 const VectorIcon& icon, 558 const VectorIcon& icon,
482 int dip_size, 559 int dip_size,
483 SkColor color) { 560 SkColor color,
561 const base::TimeDelta* elapsed_time) {
484 DCHECK(!icon.is_empty()); 562 DCHECK(!icon.is_empty());
485 const PathElement* path = (canvas->image_scale() == 1.f && icon.path_1x_) 563 const PathElement* path = (canvas->image_scale() == 1.f && icon.path_1x_)
486 ? icon.path_1x_ 564 ? icon.path_1x_
487 : icon.path_; 565 : icon.path_;
488 PaintPath(canvas, path, dip_size, color); 566 PaintPath(canvas, path, dip_size, color, elapsed_time);
489 } 567 }
490 568
491 ImageSkia CreateVectorIcon(const VectorIcon& icon, SkColor color) { 569 ImageSkia CreateVectorIcon(const VectorIcon& icon, SkColor color) {
492 return CreateVectorIcon(icon, GetDefaultSizeOfVectorIcon(icon), color); 570 return CreateVectorIcon(icon, GetDefaultSizeOfVectorIcon(icon), color);
493 } 571 }
494 572
495 ImageSkia CreateVectorIcon(const VectorIcon& icon, 573 ImageSkia CreateVectorIcon(const VectorIcon& icon,
496 int dip_size, 574 int dip_size,
497 SkColor color) { 575 SkColor color) {
498 return CreateVectorIconWithBadge(icon, dip_size, color, kNoneIcon); 576 return CreateVectorIconWithBadge(icon, dip_size, color, kNoneIcon);
499 } 577 }
500 578
501 ImageSkia CreateVectorIconWithBadge(const VectorIcon& icon, 579 ImageSkia CreateVectorIconWithBadge(const VectorIcon& icon,
502 int dip_size, 580 int dip_size,
503 SkColor color, 581 SkColor color,
504 const VectorIcon& badge_icon) { 582 const VectorIcon& badge_icon) {
505 return icon.is_empty() ? gfx::ImageSkia() 583 return icon.is_empty() ? ImageSkia()
506 : g_icon_cache.Get().GetOrCreateIcon( 584 : g_icon_cache.Get().GetOrCreateIcon(
507 icon, dip_size, color, badge_icon); 585 icon, dip_size, color, badge_icon);
508 } 586 }
509 587
510 ImageSkia CreateVectorIconFromSource(const std::string& source, 588 ImageSkia CreateVectorIconFromSource(const std::string& source,
511 int dip_size, 589 int dip_size,
512 SkColor color) { 590 SkColor color) {
513 return CanvasImageSource::MakeImageSkia<VectorIconSource>(source, dip_size, 591 return CanvasImageSource::MakeImageSkia<VectorIconSource>(source, dip_size,
514 color); 592 color);
515 } 593 }
516 594
517 int GetDefaultSizeOfVectorIcon(const gfx::VectorIcon& icon) { 595 int GetDefaultSizeOfVectorIcon(const VectorIcon& icon) {
518 const PathElement* one_x_path = icon.path_1x_ ? icon.path_1x_ : icon.path_; 596 const PathElement* one_x_path = icon.path_1x_ ? icon.path_1x_ : icon.path_;
519 return one_x_path[0].command == CANVAS_DIMENSIONS ? one_x_path[1].arg 597 return one_x_path[0].command == CANVAS_DIMENSIONS ? one_x_path[1].arg
520 : kReferenceSizeDip; 598 : kReferenceSizeDip;
521 } 599 }
522 600
601 base::TimeDelta GetDurationOfAnimation(const VectorIcon& icon) {
tdanderson 2017/05/08 21:22:54 I don't see any place in this CL where you are usi
Evan Stade 2017/05/08 22:27:41 You could make the same argument for the transitio
tdanderson 2017/05/09 20:31:40 Acknowledged.
602 base::TimeDelta last_motion;
603 PathParser parser(icon.path_);
604 while (parser.Advance()) {
605 if (parser.CurrentCommand() != TRANSITION_END)
606 continue;
607
608 auto end_time = base::TimeDelta::FromMillisecondsD(parser.GetArgument(0)) +
609 base::TimeDelta::FromMillisecondsD(parser.GetArgument(1));
610 if (end_time > last_motion)
611 last_motion = end_time;
612 }
613 return last_motion;
614 }
615
523 } // namespace gfx 616 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698