Chromium Code Reviews| Index: chrome/browser/ui/views/tabs/tab.cc |
| diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc |
| index 2ffdb637649a0ae160fef1571625e00f30de891b..aa8535345443b96946352039e15751a8831472db 100644 |
| --- a/chrome/browser/ui/views/tabs/tab.cc |
| +++ b/chrome/browser/ui/views/tabs/tab.cc |
| @@ -14,7 +14,7 @@ |
| #include "base/metrics/user_metrics.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "build/build_config.h" |
| -#include "cc/paint/paint_shader.h" |
| +#include "cc/paint/paint_flags.h" |
| #include "chrome/app/vector_icons/vector_icons.h" |
| #include "chrome/browser/themes/theme_properties.h" |
| #include "chrome/browser/ui/browser.h" |
| @@ -93,95 +93,9 @@ const double kSelectedTabOpacity = 0.3; |
| // Inactive selected tabs have their throb value scaled by this. |
| const double kSelectedTabThrobScale = 0.95 - kSelectedTabOpacity; |
| -// Max number of images to cache. This has to be at least two since rounding |
| -// errors may lead to tabs in the same tabstrip having different sizes. |
| -// 8 = normal/incognito, active/inactive, 2 sizes within tabstrip. |
| -const size_t kMaxImageCacheSize = 8; |
| - |
| const char kTabCloseButtonName[] = "TabCloseButton"; |
| //////////////////////////////////////////////////////////////////////////////// |
| -// ImageCacheEntryMetadata |
| -// |
| -// All metadata necessary to uniquely identify a cached image. |
| -struct ImageCacheEntryMetadata { |
| - ImageCacheEntryMetadata(SkColor fill_color, |
| - SkColor stroke_color, |
| - bool use_fill_and_stroke_images, |
| - float scale_factor, |
| - const gfx::Size& size); |
| - |
| - ~ImageCacheEntryMetadata(); |
| - |
| - bool operator==(const ImageCacheEntryMetadata& rhs) const; |
| - |
| - SkColor fill_color; |
| - SkColor stroke_color; |
| - bool use_fill_and_stroke_images; |
| - float scale_factor; |
| - gfx::Size size; |
| -}; |
| - |
| -ImageCacheEntryMetadata::ImageCacheEntryMetadata( |
| - SkColor fill_color, |
| - SkColor stroke_color, |
| - bool use_fill_and_stroke_images, |
| - float scale_factor, |
| - const gfx::Size& size) |
| - : fill_color(fill_color), |
| - stroke_color(stroke_color), |
| - use_fill_and_stroke_images(use_fill_and_stroke_images), |
| - scale_factor(scale_factor), |
| - size(size) {} |
| - |
| -ImageCacheEntryMetadata::~ImageCacheEntryMetadata() {} |
| - |
| -bool ImageCacheEntryMetadata::operator==( |
| - const ImageCacheEntryMetadata& rhs) const { |
| - return fill_color == rhs.fill_color && stroke_color == rhs.stroke_color && |
| - use_fill_and_stroke_images == rhs.use_fill_and_stroke_images && |
| - scale_factor == rhs.scale_factor && size == rhs.size; |
| -} |
| - |
| -//////////////////////////////////////////////////////////////////////////////// |
| -// ImageCacheEntry and cache management |
| -// |
| -// A cached image and the metadata used to generate it. |
| -struct ImageCacheEntry { |
| - ImageCacheEntry(const ImageCacheEntryMetadata& metadata, |
| - const gfx::ImageSkia& fill_image, |
| - const gfx::ImageSkia& stroke_image); |
| - ~ImageCacheEntry(); |
| - |
| - ImageCacheEntryMetadata metadata; |
| - gfx::ImageSkia fill_image; |
| - gfx::ImageSkia stroke_image; |
| -}; |
| - |
| -ImageCacheEntry::ImageCacheEntry(const ImageCacheEntryMetadata& metadata, |
| - const gfx::ImageSkia& fill_image, |
| - const gfx::ImageSkia& stroke_image) |
| - : metadata(metadata), fill_image(fill_image), stroke_image(stroke_image) {} |
| - |
| -ImageCacheEntry::~ImageCacheEntry() {} |
| - |
| -typedef std::list<ImageCacheEntry> ImageCache; |
| - |
| -// As the majority of the tabs are inactive, and painting tabs is slowish, |
| -// we cache a handful of the inactive tab backgrounds here. |
| -static ImageCache* g_image_cache = nullptr; |
| - |
| -// Performs a one-time initialization of static resources such as tab images. |
| -void InitTabResources() { |
| - static bool initialized = false; |
| - if (initialized) |
| - return; |
| - |
| - initialized = true; |
| - g_image_cache = new ImageCache(); |
| -} |
| - |
| -//////////////////////////////////////////////////////////////////////////////// |
| // Drawing and utility functions |
| // Returns the width of the tab endcap at scale 1. More precisely, this is the |
| @@ -219,6 +133,28 @@ bool ShouldThemifyFaviconForUrl(const GURL& url) { |
| // Returns a path corresponding to the tab's content region inside the outer |
| // stroke. |
| gfx::Path GetFillPath(float scale, const gfx::Size& size) { |
| + struct FillPathCache { |
| + // The parameters used to build the path. |
| + float scale; |
| + gfx::Size size; |
| + |
| + // The constructed path based on the parameters. |
| + gfx::Path path; |
| + }; |
| + // A cache sorted from most recently used to least. |
| + static auto& cache = *new std::list<FillPathCache>; |
|
Peter Kasting
2017/04/06 18:49:45
Can we use a base::MRUCache instead of manually ro
danakj
2017/04/07 19:10:49
We could, I had looked there first, but it just lo
Peter Kasting
2017/04/07 23:34:02
Would it make sense to move MRUCache to one of the
|
| + static const int kCacheSize = 8; |
|
Peter Kasting
2017/04/06 18:49:45
Nit: constexpr size_t
danakj
2017/04/07 19:10:49
Done.
|
| + |
| + // Find a match in the cache and move it to the front and return it. |
| + for (auto it = cache.begin(); it != cache.end(); ++it) { |
| + if (it->scale == scale && it->size == size) { |
| + std::list<FillPathCache> hit; |
| + hit.splice(hit.begin(), cache, it, std::next(it)); |
| + cache.splice(cache.begin(), std::move(hit)); |
|
Peter Kasting
2017/04/07 23:34:02
Can we just use std::rotate? This should work wit
danakj
2017/04/18 16:02:42
I deleted the cache here anyhow, but I think rotat
|
| + return cache.front().path; |
| + } |
| + } |
| + |
| const float right = size.width() * scale; |
| // The bottom of the tab needs to be pixel-aligned or else when we call |
| // ClipPath with anti-aliasing enabled it can cause artifacts. |
| @@ -248,6 +184,16 @@ gfx::Path GetFillPath(float scale, const gfx::Size& size) { |
| fill.rCubicTo(-0.375 * scale, scale, -1.25 * scale, 1.5 * scale, -2 * scale, |
| 1.5 * scale); |
| fill.close(); |
| + |
| + // If the cache is max size we need to drop the least recently used from the |
| + // cache. Then add the new path to the front of the cache. |
| + if (cache.size() >= kCacheSize) |
| + cache.pop_back(); |
| + cache.emplace_front(); |
| + cache.front().scale = scale; |
|
Peter Kasting
2017/04/06 18:49:45
Nit: I'd rather define a constructor so we can jus
danakj
2017/04/07 19:10:49
Done. Made a constructor and a HasMatchingParamete
|
| + cache.front().size = size; |
| + cache.front().path = fill; |
| + |
| return fill; |
| } |
| @@ -534,7 +480,6 @@ Tab::Tab(TabController* controller, gfx::AnimationContainer* container) |
| showing_close_button_(false), |
| button_color_(SK_ColorTRANSPARENT) { |
| DCHECK(controller); |
| - InitTabResources(); |
| // So we get don't get enter/exit on children and don't prematurely stop the |
| // hover. |
| @@ -1123,8 +1068,8 @@ void Tab::PaintTab(gfx::Canvas* canvas, const gfx::Path& clip) { |
| : 0; |
| const int y_offset = -GetLayoutInsets(TAB).top(); |
| if (IsActive()) { |
| - PaintTabBackgroundUsingFillId(canvas, canvas, true, kActiveTabFillId, |
| - y_offset); |
| + PaintTabBackground(canvas, true /* active */, kActiveTabFillId, y_offset, |
| + nullptr /* clip */); |
| } else { |
| PaintInactiveTabBackground(canvas, clip); |
| @@ -1132,8 +1077,8 @@ void Tab::PaintTab(gfx::Canvas* canvas, const gfx::Path& clip) { |
| if (throb_value > 0) { |
| canvas->SaveLayerAlpha(gfx::ToRoundedInt(throb_value * 0xff), |
| GetLocalBounds()); |
| - PaintTabBackgroundUsingFillId(canvas, canvas, true, kActiveTabFillId, |
| - y_offset); |
| + PaintTabBackground(canvas, true /* active */, kActiveTabFillId, y_offset, |
| + nullptr /* clip */); |
| canvas->Restore(); |
| } |
| } |
| @@ -1146,130 +1091,97 @@ void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas, |
| const gfx::Path& clip) { |
| bool has_custom_image; |
| int fill_id = controller_->GetBackgroundResourceId(&has_custom_image); |
| - const ui::ThemeProvider* tp = GetThemeProvider(); |
| - // We only cache the image when it's the default image and we're not hovered, |
| - // to avoid caching a background image that isn't the same for all tabs. |
| if (has_custom_image) { |
| + const ui::ThemeProvider* tp = GetThemeProvider(); |
| + |
| // If the theme is providing a custom background image, then its top edge |
| // should be at the top of the tab. Otherwise, we assume that the background |
| // image is a composited foreground + frame image. Note that if the theme |
| // is only providing a custom frame image, |has_custom_image| will be true, |
| // but we should use the |background_offset_| here. |
| - const int y_offset = |
| - tp->HasCustomImage(fill_id) ? 0 : background_offset_.y(); |
| - PaintTabBackgroundUsingFillId(canvas, canvas, false, fill_id, y_offset); |
| - return; |
| + int y_offset = tp->HasCustomImage(fill_id) ? 0 : background_offset_.y(); |
| + PaintTabBackground(canvas, false /* active */, fill_id, y_offset, |
| + nullptr /* clip */); |
| + } else { |
| + PaintTabBackground(canvas, false /* active */, 0 /* fill_id */, |
| + 0 /* y_offset */, |
| + controller_->MaySetClip() ? &clip : nullptr); |
| } |
| - if (hover_controller_.ShouldDraw()) { |
| - PaintTabBackgroundUsingFillId(canvas, canvas, false, 0, 0); |
| - return; |
| +} |
| + |
| +void Tab::PaintTabBackground(gfx::Canvas* canvas, |
| + bool active, |
| + int fill_id, |
| + int y_offset, |
| + const gfx::Path* clip) { |
| + gfx::Path fill_path = GetFillPath(canvas->image_scale(), size()); |
| + |
| + PaintTabBackgroundFill(canvas, fill_path, active, fill_id, y_offset); |
| + { |
|
Peter Kasting
2017/04/06 18:49:45
Nit: You could save a line or two and reduce the i
danakj
2017/04/07 19:10:49
Done.
|
| + gfx::ScopedCanvas scoped_canvas(clip ? canvas : nullptr); |
| + if (clip) |
| + canvas->sk_canvas()->clipPath(*clip, SkClipOp::kDifference, true); |
| + PaintTabBackgroundStroke(canvas, fill_path, active); |
| } |
| +} |
| - // For efficiency, we don't use separate fill and stroke images unless we |
| - // really need to clip the stroke and not the fill (for stacked tabs). This |
| - // saves memory and avoids an extra image draw at the cost of recalculating |
| - // the images when MaySetClip() toggles. |
| - const bool use_fill_and_stroke_images = controller_->MaySetClip(); |
| - |
| - const ImageCacheEntryMetadata metadata( |
| - tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB), |
| - controller_->GetToolbarTopSeparatorColor(), use_fill_and_stroke_images, |
| - canvas->image_scale(), size()); |
| - auto it = std::find_if( |
| - g_image_cache->begin(), g_image_cache->end(), |
| - [&metadata](const ImageCacheEntry& e) { return e.metadata == metadata; }); |
| - if (it == g_image_cache->end()) { |
| - gfx::Canvas tmp_canvas(size(), canvas->image_scale(), false); |
| - if (use_fill_and_stroke_images) { |
| - gfx::Canvas tmp_fill_canvas(size(), canvas->image_scale(), false); |
| - PaintTabBackgroundUsingFillId(&tmp_fill_canvas, &tmp_canvas, false, 0, 0); |
| - g_image_cache->emplace_front( |
| - metadata, gfx::ImageSkia(tmp_fill_canvas.ExtractImageRep()), |
| - gfx::ImageSkia(tmp_canvas.ExtractImageRep())); |
| - } else { |
| - PaintTabBackgroundUsingFillId(&tmp_canvas, &tmp_canvas, false, 0, 0); |
| - g_image_cache->emplace_front( |
| - metadata, gfx::ImageSkia(), |
| - gfx::ImageSkia(tmp_canvas.ExtractImageRep())); |
| - } |
| - if (g_image_cache->size() > kMaxImageCacheSize) |
| - g_image_cache->pop_back(); |
| - it = g_image_cache->begin(); |
| +void Tab::PaintTabBackgroundFill(gfx::Canvas* canvas, |
| + const gfx::Path& fill_path, |
| + bool active, |
| + int fill_id, |
| + int y_offset) { |
| + const ui::ThemeProvider* tp = GetThemeProvider(); |
| + SkColor active_color = tp->GetColor(ThemeProperties::COLOR_TOOLBAR); |
| + SkColor inactive_color = tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB); |
| + |
| + gfx::ScopedCanvas scoped_canvas(canvas); |
| + const float scale = canvas->UndoDeviceScaleFactor(); |
| + |
| + canvas->ClipPath(fill_path, true); |
| + if (fill_id) { |
| + gfx::ScopedCanvas scale_scoper(canvas); |
| + canvas->sk_canvas()->scale(scale, scale); |
| + canvas->TileImageInt(*tp->GetImageSkiaNamed(fill_id), |
| + GetMirroredX() + background_offset_.x(), y_offset, 0, |
| + 0, width(), height()); |
| + } else { |
| + cc::PaintFlags flags; |
| + flags.setAntiAlias(true); |
| + flags.setColor(active ? active_color : inactive_color); |
| + canvas->DrawRect(gfx::ScaleToEnclosingRect(GetLocalBounds(), scale), flags); |
| } |
| - gfx::ScopedCanvas scoped_canvas( |
| - use_fill_and_stroke_images ? canvas : nullptr); |
| - if (use_fill_and_stroke_images) { |
| - canvas->DrawImageInt(it->fill_image, 0, 0); |
| - canvas->sk_canvas()->clipPath(clip, SkClipOp::kDifference, true); |
| + if (!active && hover_controller_.ShouldDraw()) { |
| + SkPoint hover_location(gfx::PointToSkPoint(hover_controller_.location())); |
| + hover_location.scale(SkFloatToScalar(scale)); |
| + const SkScalar kMinHoverRadius = 16; |
| + const SkScalar radius = |
| + std::max(SkFloatToScalar(width() / 4.f), kMinHoverRadius); |
| + DrawHighlight(canvas, hover_location, radius * scale, |
| + SkColorSetA(active_color, hover_controller_.GetAlpha())); |
| } |
| - canvas->DrawImageInt(it->stroke_image, 0, 0); |
| } |
| -void Tab::PaintTabBackgroundUsingFillId(gfx::Canvas* fill_canvas, |
| - gfx::Canvas* stroke_canvas, |
| - bool is_active, |
| - int fill_id, |
| - int y_offset) { |
| - gfx::Path fill; |
| - cc::PaintFlags flags; |
| - flags.setAntiAlias(true); |
| +void Tab::PaintTabBackgroundStroke(gfx::Canvas* canvas, |
| + const gfx::Path& fill_path, |
| + bool active) { |
| + SkColor color = controller_->GetToolbarTopSeparatorColor(); |
| - // Draw the fill. |
| - { |
| - gfx::ScopedCanvas scoped_canvas(fill_canvas); |
| - const float scale = fill_canvas->UndoDeviceScaleFactor(); |
| - const ui::ThemeProvider* tp = GetThemeProvider(); |
| - const SkColor toolbar_color = tp->GetColor(ThemeProperties::COLOR_TOOLBAR); |
| - |
| - fill = GetFillPath(scale, size()); |
| - { |
| - gfx::ScopedCanvas clip_scoper(fill_canvas); |
| - fill_canvas->ClipPath(fill, true); |
| - if (fill_id) { |
| - gfx::ScopedCanvas scale_scoper(fill_canvas); |
| - fill_canvas->sk_canvas()->scale(scale, scale); |
| - fill_canvas->TileImageInt(*tp->GetImageSkiaNamed(fill_id), |
| - GetMirroredX() + background_offset_.x(), |
| - y_offset, 0, 0, width(), height()); |
| - } else { |
| - flags.setColor( |
| - is_active ? toolbar_color |
| - : tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB)); |
| - fill_canvas->DrawRect( |
| - gfx::ScaleToEnclosingRect(GetLocalBounds(), scale), flags); |
| - } |
| + gfx::ScopedCanvas scoped_canvas(canvas); |
| + const float scale = canvas->UndoDeviceScaleFactor(); |
| - if (!is_active && hover_controller_.ShouldDraw()) { |
| - SkPoint hover_location( |
| - gfx::PointToSkPoint(hover_controller_.location())); |
| - hover_location.scale(SkFloatToScalar(scale)); |
| - const SkScalar kMinHoverRadius = 16; |
| - const SkScalar radius = |
| - std::max(SkFloatToScalar(width() / 4.f), kMinHoverRadius); |
| - DrawHighlight(fill_canvas, hover_location, radius * scale, |
| - SkColorSetA(toolbar_color, hover_controller_.GetAlpha())); |
| - } |
| - } |
| - } |
| - |
| - // Draw the stroke. |
| - { |
| - gfx::ScopedCanvas scoped_canvas(stroke_canvas); |
| - const float scale = stroke_canvas->UndoDeviceScaleFactor(); |
| - |
| - gfx::Path stroke = GetBorderPath(scale, false, false, size()); |
| - Op(stroke, fill, kDifference_SkPathOp, &stroke); |
| - if (!is_active) { |
| - // Clip out the bottom line; this will be drawn for us by |
| - // TabStrip::PaintChildren(). |
| - stroke_canvas->ClipRect( |
| - gfx::RectF(width() * scale, height() * scale - 1)); |
| - } |
| - flags.setColor(controller_->GetToolbarTopSeparatorColor()); |
| - stroke_canvas->DrawPath(stroke, flags); |
| + gfx::Path stroke = GetBorderPath(scale, false, false, size()); |
| + Op(stroke, fill_path, kDifference_SkPathOp, &stroke); |
| + if (!active) { |
| + // Clip out the bottom line; this will be drawn for us by |
| + // TabStrip::PaintChildren(). |
| + canvas->ClipRect(gfx::RectF(width() * scale, height() * scale - 1)); |
| } |
| + cc::PaintFlags flags; |
| + flags.setAntiAlias(true); |
| + flags.setColor(color); |
| + canvas->DrawPath(stroke, flags); |
| } |
| void Tab::PaintPinnedTabTitleChangedIndicatorAndIcon( |