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 1e747ad40793b1a4cc7effb2cc8c5df091192b69..4b854bcfbe4b1bf089ab06e6bbaf58f9954d4cd4 100644 |
--- a/chrome/browser/ui/views/tabs/tab.cc |
+++ b/chrome/browser/ui/views/tabs/tab.cc |
@@ -150,6 +150,7 @@ struct ImageCacheEntryMetadata { |
ImageCacheEntryMetadata(int resource_id, |
SkColor fill_color, |
SkColor stroke_color, |
+ bool use_fill_and_stroke_images, |
ui::ScaleFactor scale_factor, |
const gfx::Size& size); |
@@ -160,18 +161,22 @@ struct ImageCacheEntryMetadata { |
int resource_id; // Only needed by pre-MD |
SkColor fill_color; // Both colors only needed by MD |
SkColor stroke_color; |
+ bool use_fill_and_stroke_images; |
ui::ScaleFactor scale_factor; |
gfx::Size size; |
}; |
-ImageCacheEntryMetadata::ImageCacheEntryMetadata(int resource_id, |
- SkColor fill_color, |
- SkColor stroke_color, |
- ui::ScaleFactor scale_factor, |
- const gfx::Size& size) |
+ImageCacheEntryMetadata::ImageCacheEntryMetadata( |
+ int resource_id, |
+ SkColor fill_color, |
+ SkColor stroke_color, |
+ bool use_fill_and_stroke_images, |
+ ui::ScaleFactor scale_factor, |
+ const gfx::Size& size) |
: resource_id(resource_id), |
fill_color(fill_color), |
stroke_color(stroke_color), |
+ use_fill_and_stroke_images(use_fill_and_stroke_images), |
scale_factor(scale_factor), |
size(size) { |
DCHECK_NE(ui::SCALE_FACTOR_NONE, scale_factor); |
@@ -190,8 +195,9 @@ ImageCacheEntryMetadata::~ImageCacheEntryMetadata() {} |
bool ImageCacheEntryMetadata::operator==( |
const ImageCacheEntryMetadata& rhs) const { |
return resource_id == rhs.resource_id && fill_color == rhs.fill_color && |
- stroke_color == rhs.stroke_color && scale_factor == rhs.scale_factor && |
- size == rhs.size; |
+ stroke_color == rhs.stroke_color && |
+ use_fill_and_stroke_images == rhs.use_fill_and_stroke_images && |
+ scale_factor == rhs.scale_factor && size == rhs.size; |
} |
//////////////////////////////////////////////////////////////////////////////// |
@@ -200,16 +206,19 @@ bool ImageCacheEntryMetadata::operator==( |
// A cached image and the metadata used to generate it. |
struct ImageCacheEntry { |
ImageCacheEntry(const ImageCacheEntryMetadata& metadata, |
- const gfx::ImageSkia& image); |
+ const gfx::ImageSkia& fill_image, |
+ const gfx::ImageSkia& stroke_image); |
~ImageCacheEntry(); |
ImageCacheEntryMetadata metadata; |
- gfx::ImageSkia image; |
+ gfx::ImageSkia fill_image; |
+ gfx::ImageSkia stroke_image; |
}; |
ImageCacheEntry::ImageCacheEntry(const ImageCacheEntryMetadata& metadata, |
- const gfx::ImageSkia& image) |
- : metadata(metadata), image(image) {} |
+ const gfx::ImageSkia& fill_image, |
+ const gfx::ImageSkia& stroke_image) |
+ : metadata(metadata), fill_image(fill_image), stroke_image(stroke_image) {} |
ImageCacheEntry::~ImageCacheEntry() {} |
@@ -472,7 +481,8 @@ void PaintTabFill(gfx::Canvas* canvas, |
tab_insets.top(), rect.width(), rect.height()); |
} |
-void PaintTabBackgroundUsingParams(gfx::Canvas* canvas, |
+void PaintTabBackgroundUsingParams(gfx::Canvas* fill_canvas, |
+ gfx::Canvas* stroke_canvas, |
views::GlowHoverController* hc, |
const PaintBackgroundParams& params) { |
const gfx::Rect& rect = params.rect; |
@@ -486,45 +496,57 @@ void PaintTabBackgroundUsingParams(gfx::Canvas* canvas, |
SkColorSetA(params.toolbar_color, draw_hover ? hc->GetAlpha() : 255); |
if (ui::MaterialDesignController::IsModeMaterial()) { |
- gfx::ScopedCanvas scoped_canvas(canvas); |
- const float scale = canvas->UndoDeviceScaleFactor(); |
- |
- // Draw the fill. |
- gfx::Path fill = GetFillPath(scale, rect.size()); |
+ gfx::Path fill; |
SkPaint paint; |
paint.setAntiAlias(true); |
+ |
+ // Draw the fill. |
{ |
- gfx::ScopedCanvas clip_scoper(canvas); |
- canvas->ClipPath(fill, true); |
- if (!params.fill_image.isNull()) { |
- gfx::ScopedCanvas scale_scoper(canvas); |
- canvas->sk_canvas()->scale(scale, scale); |
- canvas->TileImageInt(params.fill_image, rect.x(), rect.y(), 0, 0, |
- rect.width(), rect.height()); |
- } else { |
- paint.setColor(params.is_active ? params.toolbar_color |
- : params.background_tab_color); |
- canvas->DrawRect( |
- gfx::ScaleToEnclosingRect(gfx::Rect(rect.size()), scale), paint); |
- } |
- if (draw_hover) { |
- hover_location.scale(SkFloatToScalar(scale)); |
- DrawHighlight(canvas, hover_location, radius * scale, hover_color); |
+ gfx::ScopedCanvas scoped_canvas(fill_canvas); |
+ const float scale = fill_canvas->UndoDeviceScaleFactor(); |
+ |
+ fill = GetFillPath(scale, rect.size()); |
+ { |
+ gfx::ScopedCanvas clip_scoper(fill_canvas); |
+ fill_canvas->ClipPath(fill, true); |
+ if (!params.fill_image.isNull()) { |
+ gfx::ScopedCanvas scale_scoper(fill_canvas); |
+ fill_canvas->sk_canvas()->scale(scale, scale); |
+ fill_canvas->TileImageInt(params.fill_image, rect.x(), rect.y(), 0, 0, |
+ rect.width(), rect.height()); |
+ } else { |
+ paint.setColor(params.is_active ? params.toolbar_color |
+ : params.background_tab_color); |
+ fill_canvas->DrawRect( |
+ gfx::ScaleToEnclosingRect(gfx::Rect(rect.size()), scale), |
+ paint); |
+ } |
+ if (draw_hover) { |
+ hover_location.scale(SkFloatToScalar(scale)); |
+ DrawHighlight(fill_canvas, hover_location, radius * scale, |
+ hover_color); |
+ } |
} |
} |
// Draw the stroke. |
- gfx::Path stroke = GetBorderPath(scale, false, false, rect.size()); |
- Op(stroke, fill, kDifference_SkPathOp, &stroke); |
- if (!params.is_active) { |
- // Clip out the bottom line; this will be drawn for us by |
- // TabStrip::PaintChildren(). |
- canvas->ClipRect(gfx::RectF(rect.width() * scale, |
- rect.height() * scale - 1)); |
+ { |
+ gfx::ScopedCanvas scoped_canvas(stroke_canvas); |
+ const float scale = stroke_canvas->UndoDeviceScaleFactor(); |
+ |
+ gfx::Path stroke = GetBorderPath(scale, false, false, rect.size()); |
+ Op(stroke, fill, kDifference_SkPathOp, &stroke); |
+ if (!params.is_active) { |
+ // Clip out the bottom line; this will be drawn for us by |
+ // TabStrip::PaintChildren(). |
+ stroke_canvas->ClipRect(gfx::RectF(rect.width() * scale, |
+ rect.height() * scale - 1)); |
+ } |
+ paint.setColor(params.stroke_color); |
+ stroke_canvas->DrawPath(stroke, paint); |
} |
- paint.setColor(params.stroke_color); |
- canvas->DrawPath(stroke, paint); |
} else { |
+ gfx::Canvas* canvas = stroke_canvas; |
if (draw_hover) { |
// Draw everything to a temporary canvas so we can extract an image for |
// use in masking the hover glow. |
@@ -652,35 +674,6 @@ class Tab::TabCloseButton : public views::ImageButton, |
const char* GetClassName() const override { return kTabCloseButtonName; } |
private: |
- // Returns the rectangular bounds of parent tab's visible region in the |
- // local coordinate space of |this|. |
- gfx::Rect GetTabBounds() const { |
- gfx::Path tab_mask; |
- tab_->GetHitTestMask(&tab_mask); |
- |
- gfx::RectF tab_bounds_f(gfx::SkRectToRectF(tab_mask.getBounds())); |
- views::View::ConvertRectToTarget(tab_, this, &tab_bounds_f); |
- return gfx::ToEnclosingRect(tab_bounds_f); |
- } |
- |
- // Returns the rectangular bounds of the tab close button in the local |
- // coordinate space of |this|, not including clipped regions on the top |
- // or bottom of the button. |tab_bounds| is the rectangular bounds of |
- // the parent tab's visible region in the local coordinate space of |this|. |
- gfx::Rect GetTabCloseButtonBounds(const gfx::Rect& tab_bounds) const { |
- gfx::Rect button_bounds(GetContentsBounds()); |
- button_bounds.set_x(GetMirroredXForRect(button_bounds)); |
- |
- int top_overflow = tab_bounds.y() - button_bounds.y(); |
- int bottom_overflow = button_bounds.bottom() - tab_bounds.bottom(); |
- if (top_overflow > 0) |
- button_bounds.set_y(tab_bounds.y()); |
- else if (bottom_overflow > 0) |
- button_bounds.set_height(button_bounds.height() - bottom_overflow); |
- |
- return button_bounds; |
- } |
- |
// views::MaskedTargeterDelegate: |
View* TargetForRect(View* root, const gfx::Rect& rect) override { |
CHECK_EQ(root, this); |
@@ -711,45 +704,10 @@ class Tab::TabCloseButton : public views::ImageButton, |
// We need to define this so hit-testing won't include the border region. |
bool GetHitTestMask(gfx::Path* mask) const override { |
- DCHECK(mask); |
- mask->reset(); |
- |
- // The parent tab may be partially occluded by another tab if we are |
- // in stacked tab mode, which means that the tab close button may also |
- // be partially occluded. Define the hit test mask of the tab close |
- // button to be the intersection of the parent tab's visible bounds |
- // and the bounds of the tab close button. |
- gfx::Rect tab_bounds(GetTabBounds()); |
- gfx::Rect button_bounds(GetTabCloseButtonBounds(tab_bounds)); |
- gfx::Rect intersection(gfx::IntersectRects(tab_bounds, button_bounds)); |
- |
- if (!intersection.IsEmpty()) { |
- mask->addRect(RectToSkRect(intersection)); |
- return true; |
- } |
- |
- return false; |
- } |
- |
- bool DoesIntersectRect(const View* target, |
- const gfx::Rect& rect) const override { |
- CHECK_EQ(target, this); |
- |
- // If the request is not made in response to a gesture, use the |
- // default implementation. |
- if (views::UsePointBasedTargeting(rect)) |
- return MaskedTargeterDelegate::DoesIntersectRect(target, rect); |
- |
- // The hit test request is in response to a gesture. Return false if any |
- // part of the tab close button is hidden from the user. |
- // TODO(tdanderson): Consider always returning the intersection if the |
- // non-rectangular shape of the tab can be accounted for. |
- gfx::Rect tab_bounds(GetTabBounds()); |
- gfx::Rect button_bounds(GetTabCloseButtonBounds(tab_bounds)); |
- if (!tab_bounds.Contains(button_bounds)) |
- return false; |
- |
- return MaskedTargeterDelegate::DoesIntersectRect(target, rect); |
+ gfx::Rect button_bounds(GetContentsBounds()); |
+ button_bounds.set_x(GetMirroredXForRect(button_bounds)); |
+ mask->addRect(gfx::RectToSkRect(button_bounds)); |
+ return true; |
} |
Tab* tab_; |
@@ -1137,27 +1095,13 @@ void Tab::ShowContextMenuForView(views::View* source, |
// Tab, views::MaskedTargeterDelegate overrides: |
bool Tab::GetHitTestMask(gfx::Path* mask) const { |
- const float scale = GetWidget()->GetCompositor()->device_scale_factor(); |
// When the window is maximized we don't want to shave off the edges or top |
// shadow of the tab, such that the user can click anywhere along the top |
// edge of the screen to select a tab. Ditto for immersive fullscreen. |
const views::Widget* widget = GetWidget(); |
- const bool extend_to_top = |
- widget && (widget->IsMaximized() || widget->IsFullscreen()); |
- *mask = GetBorderPath(scale, true, extend_to_top, size()); |
- |
- // It is possible for a portion of the tab to be occluded if tabs are |
- // stacked, so modify the hit test mask to only include the visible |
- // region of the tab. |
- gfx::Rect clip; |
- controller_->ShouldPaintTab(this, &clip); |
- if (clip.size().GetArea()) { |
- SkRect intersection(mask->getBounds()); |
- mask->reset(); |
- if (!intersection.intersect(RectToSkRect(clip))) |
- return false; |
- mask->addRect(intersection); |
- } |
+ *mask = GetBorderPath( |
+ GetWidget()->GetCompositor()->device_scale_factor(), true, |
+ widget && (widget->IsMaximized() || widget->IsFullscreen()), size()); |
return true; |
} |
@@ -1178,21 +1122,16 @@ void Tab::OnPaint(gfx::Canvas* canvas) { |
if (width() < GetMinimumInactiveSize().width() && !data().pinned) |
return; |
- gfx::Rect clip; |
- if (!controller_->ShouldPaintTab(this, &clip)) |
+ gfx::Path clip; |
+ if (!controller_->ShouldPaintTab( |
+ this, base::Bind(&GetBorderPath, canvas->image_scale(), true, false), |
+ &clip)) |
return; |
- if (!clip.IsEmpty()) { |
- canvas->Save(); |
- canvas->ClipRect(clip); |
- } |
if (controller_->IsImmersiveStyle()) |
PaintImmersiveTab(canvas); |
else |
- PaintTab(canvas); |
- |
- if (!clip.IsEmpty()) |
- canvas->Restore(); |
+ PaintTab(canvas, clip); |
} |
void Tab::Layout() { |
@@ -1481,22 +1420,22 @@ void Tab::DataChanged(const TabRendererData& old) { |
StopPulse(); |
} |
-void Tab::PaintTab(gfx::Canvas* canvas) { |
+void Tab::PaintTab(gfx::Canvas* canvas, const gfx::Path& clip) { |
const int kActiveTabFillId = IDR_THEME_TOOLBAR; |
const bool has_custom_image = |
GetThemeProvider()->HasCustomImage(kActiveTabFillId); |
const int y_offset = -GetYInsetForActiveTabBackground(); |
if (IsActive()) { |
- PaintTabBackgroundUsingFillId(canvas, true, kActiveTabFillId, |
+ PaintTabBackgroundUsingFillId(canvas, canvas, true, kActiveTabFillId, |
has_custom_image, y_offset); |
} else { |
- PaintInactiveTabBackground(canvas); |
+ PaintInactiveTabBackground(canvas, clip); |
const double throb_value = GetThrobValue(); |
if (throb_value > 0) { |
canvas->SaveLayerAlpha(gfx::ToRoundedInt(throb_value * 0xff), |
GetLocalBounds()); |
- PaintTabBackgroundUsingFillId(canvas, true, kActiveTabFillId, |
+ PaintTabBackgroundUsingFillId(canvas, canvas, true, kActiveTabFillId, |
has_custom_image, y_offset); |
canvas->Restore(); |
} |
@@ -1550,7 +1489,8 @@ void Tab::PaintImmersiveTab(gfx::Canvas* canvas) { |
} |
} |
-void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas) { |
+void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas, |
+ const gfx::Path& clip) { |
bool has_custom_image; |
int fill_id = controller_->GetBackgroundResourceId(&has_custom_image); |
@@ -1566,31 +1506,58 @@ void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas) { |
// 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 || hover_controller_.ShouldDraw()) { |
- PaintTabBackgroundUsingFillId(canvas, false, fill_id, has_custom_image, |
- y_offset); |
+ PaintTabBackgroundUsingFillId(canvas, canvas, false, fill_id, |
+ has_custom_image, y_offset); |
return; |
} |
+ // 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() && |
+ ui::MaterialDesignController::IsModeMaterial(); |
+ |
const ImageCacheEntryMetadata metadata( |
fill_id, tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB), |
- controller_->GetToolbarTopSeparatorColor(), |
+ controller_->GetToolbarTopSeparatorColor(), use_fill_and_stroke_images, |
ui::GetSupportedScaleFactor(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); |
- PaintTabBackgroundUsingFillId(&tmp_canvas, false, fill_id, false, y_offset); |
- g_image_cache->emplace_front(metadata, |
- gfx::ImageSkia(tmp_canvas.ExtractImageRep())); |
+ if (use_fill_and_stroke_images) { |
+ gfx::Canvas tmp_fill_canvas(size(), canvas->image_scale(), false); |
+ PaintTabBackgroundUsingFillId(&tmp_fill_canvas, &tmp_canvas, false, |
+ fill_id, false, y_offset); |
+ g_image_cache->emplace_front( |
+ metadata, gfx::ImageSkia(tmp_fill_canvas.ExtractImageRep()), |
+ gfx::ImageSkia(tmp_canvas.ExtractImageRep())); |
+ } else { |
+ PaintTabBackgroundUsingFillId(&tmp_canvas, &tmp_canvas, false, fill_id, |
+ false, y_offset); |
+ 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(); |
} |
- canvas->DrawImageInt(it->image, 0, 0); |
+ |
+ 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, SkRegion::kDifference_Op, true); |
+ } |
+ canvas->DrawImageInt(it->stroke_image, 0, 0); |
} |
-void Tab::PaintTabBackgroundUsingFillId(gfx::Canvas* canvas, |
+void Tab::PaintTabBackgroundUsingFillId(gfx::Canvas* fill_canvas, |
+ gfx::Canvas* stroke_canvas, |
bool is_active, |
int fill_id, |
bool has_custom_image, |
@@ -1612,7 +1579,7 @@ void Tab::PaintTabBackgroundUsingFillId(gfx::Canvas* canvas, |
GetThemeProvider()->GetColor(ThemeProperties::COLOR_TOOLBAR), |
GetThemeProvider()->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB)); |
- PaintTabBackgroundUsingParams(canvas, hc, params); |
+ PaintTabBackgroundUsingParams(fill_canvas, stroke_canvas, hc, params); |
} |
void Tab::PaintPinnedTabTitleChangedIndicatorAndIcon( |