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

Unified Diff: chrome/browser/ui/cocoa/tabs/tab_view.mm

Issue 918533005: Mac: Optimize TabView drawing. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix for rsesek Created 5 years, 10 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/ui/cocoa/tabs/tab_view.h ('k') | chrome/browser/ui/cocoa/tabs/tab_view_unittest.mm » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/ui/cocoa/tabs/tab_view.mm
diff --git a/chrome/browser/ui/cocoa/tabs/tab_view.mm b/chrome/browser/ui/cocoa/tabs/tab_view.mm
index cad76744d3ea0f3e22403f3653185a39d494e10a..bd5b053e29234616eecb871ae3edf81d6198cd21 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_view.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_view.mm
@@ -19,12 +19,12 @@
#import "third_party/google_toolbox_for_mac/src/AppKit/GTMFadeTruncatingTextFieldCell.h"
#import "ui/base/cocoa/nsgraphics_context_additions.h"
#import "ui/base/cocoa/nsview_additions.h"
+#include "ui/base/cocoa/three_part_image.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
-const int kMaskHeight = 29; // Height of the mask bitmap.
const int kFillHeight = 25; // Height of the "mask on" part of the mask bitmap.
// The amount of time in seconds during which each type of glow increases, holds
@@ -44,12 +44,30 @@ const NSTimeInterval kGlowUpdateInterval = 0.025;
// has moved less than the threshold, we want to close the tab.
const CGFloat kRapidCloseDist = 2.5;
+namespace {
+
+ui::ThreePartImage* GetMaskImage() {
+ static ui::ThreePartImage* mask =
+ new ui::ThreePartImage(IDR_TAB_ALPHA_LEFT, 0, IDR_TAB_ALPHA_RIGHT);
Nico 2015/02/13 04:17:27 nit: We have CR_DEFINE_STATIC_LOCAL for this (also
+ return mask;
+}
+
+ui::ThreePartImage* GetStrokeImage(bool active) {
+ static ui::ThreePartImage* activeStroke = new ui::ThreePartImage(
+ IDR_TAB_ACTIVE_LEFT, IDR_TAB_ACTIVE_CENTER, IDR_TAB_ACTIVE_RIGHT);
+ static ui::ThreePartImage* inactiveStroke = new ui::ThreePartImage(
+ IDR_TAB_INACTIVE_LEFT, IDR_TAB_INACTIVE_CENTER, IDR_TAB_INACTIVE_RIGHT);
+
+ return active ? activeStroke : inactiveStroke;
+}
+
+} // namespace
+
@interface TabView(Private)
- (void)resetLastGlowUpdateTime;
- (NSTimeInterval)timeElapsedSinceLastGlowUpdate;
- (void)adjustGlowValue;
-- (CGImageRef)tabClippingMask;
@end // TabView(Private)
@@ -82,6 +100,8 @@ const CGFloat kRapidCloseDist = 2.5;
[labelCell setFont:font];
[titleView_ setCell:labelCell];
titleViewCell_ = labelCell;
+
+ [self setWantsLayer:YES]; // -drawFill: needs a layer.
}
return self;
}
@@ -157,34 +177,9 @@ const CGFloat kRapidCloseDist = 2.5;
return defaultHitTestResult;
NSPoint viewPoint = [self convertPoint:aPoint fromView:[self superview]];
- NSRect pointRect = NSMakeRect(viewPoint.x, viewPoint.y, 1, 1);
-
- ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
- NSImage* left = rb.GetNativeImageNamed(IDR_TAB_ALPHA_LEFT).ToNSImage();
- if (viewPoint.x < [left size].width) {
- NSRect imageRect = NSMakeRect(0, 0, [left size].width, [left size].height);
- if ([left hitTestRect:pointRect withImageDestinationRect:imageRect
- context:nil hints:nil flipped:NO]) {
- return self;
- }
- return nil;
- }
-
- NSImage* right = rb.GetNativeImageNamed(IDR_TAB_ALPHA_RIGHT).ToNSImage();
- CGFloat rightX = NSWidth([self bounds]) - [right size].width;
- if (viewPoint.x > rightX) {
- NSRect imageRect = NSMakeRect(
- rightX, 0, [right size].width, [right size].height);
- if ([right hitTestRect:pointRect withImageDestinationRect:imageRect
- context:nil hints:nil flipped:NO]) {
- return self;
- }
- return nil;
- }
-
- if (viewPoint.y < kFillHeight)
- return self;
- return nil;
+ NSRect maskRect = [self bounds];
+ maskRect.size.height = kFillHeight;
+ return GetMaskImage()->HitTest(viewPoint, maskRect) ? self : nil;
}
// Returns |YES| if this tab can be torn away into a new window.
@@ -312,74 +307,48 @@ const CGFloat kRapidCloseDist = 2.5;
return themeProvider->GetNSImageColorNamed(bitmapResources[active][selected]);
}
-// Draws the active tab background.
-- (void)drawFillForActiveTab:(NSRect)dirtyRect {
- NSColor* backgroundImageColor = [self backgroundColorForSelected:YES];
- [backgroundImageColor set];
-
- // Themes can have partially transparent images. NSRectFill() is measurably
- // faster though, so call it for the known-safe default theme.
- ThemeService* themeProvider =
- static_cast<ThemeService*>([[self window] themeProvider]);
- if (themeProvider && themeProvider->UsingDefaultTheme())
- NSRectFill(dirtyRect);
- else
- NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver);
-}
-
// Draws the tab background.
- (void)drawFill:(NSRect)dirtyRect {
gfx::ScopedNSGraphicsContextSaveGState scopedGState;
- NSGraphicsContext* context = [NSGraphicsContext currentContext];
- CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]);
+ NSRect bounds = [self bounds];
+
+ NSRect clippingRect = bounds;
+ clippingRect.size.height = kFillHeight;
+ if (state_ != NSOnState) {
+ // Background tabs should not paint over the tab strip separator, which is
+ // two pixels high in both lodpi and hidpi.
+ clippingRect.origin.y = 2 * [self cr_lineWidth];
+ clippingRect.size.height -= clippingRect.origin.y;
+ }
+ NSRectClip(clippingRect);
- ThemeService* themeProvider =
- static_cast<ThemeService*>([[self window] themeProvider]);
NSPoint position = [[self window]
- themeImagePositionForAlignment: THEME_IMAGE_ALIGN_WITH_TAB_STRIP];
- [context cr_setPatternPhase:position forView:self];
+ themeImagePositionForAlignment:THEME_IMAGE_ALIGN_WITH_TAB_STRIP];
+ [[NSGraphicsContext currentContext] cr_setPatternPhase:position forView:self];
- CGImageRef mask([self tabClippingMask]);
- CGRect maskBounds = CGRectMake(0, 0, maskCacheWidth_, kMaskHeight);
- CGContextClipToMask(cgContext, maskBounds, mask);
+ [[self backgroundColorForSelected:(state_ != NSOffState)] set];
+ NSRectFill(dirtyRect);
- // There is only 1 active tab at a time.
- // It has a different fill color which draws over the separator line.
- if (state_ == NSOnState) {
- [self drawFillForActiveTab:dirtyRect];
- return;
- }
-
- // Background tabs should not paint over the tab strip separator, which is
- // two pixels high in both lodpi and hidpi.
- if (dirtyRect.origin.y < 1)
- dirtyRect.origin.y = 2 * [self cr_lineWidth];
+ if (state_ == NSOffState)
+ [self drawGlow:dirtyRect];
- // There can be multiple selected tabs.
- // They have the same fill color as the active tab, but do not draw over
- // the separator.
- if (state_ == NSMixedState) {
- [self drawFillForActiveTab:dirtyRect];
- return;
+ // If we filled outside the middle rect, we need to erase what we filled
+ // outside the tab's shape.
+ // This only works if we are drawing to our own backing layer.
+ if (!NSContainsRect(GetMaskImage()->GetMiddleRect(bounds), dirtyRect)) {
+ DCHECK([self layer]);
+ GetMaskImage()->DrawInRect(bounds, NSCompositeDestinationIn, 1.0);
}
+}
- // Draw the tab background.
- NSColor* backgroundImageColor = [self backgroundColorForSelected:NO];
- [backgroundImageColor set];
-
- // Themes can have partially transparent images. NSRectFill() is measurably
- // faster though, so call it for the known-safe default theme.
- bool usingDefaultTheme = themeProvider && themeProvider->UsingDefaultTheme();
- if (usingDefaultTheme)
- NSRectFill(dirtyRect);
- else
- NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver);
+// Draw the glow for hover and the overlay for alerts.
+- (void)drawGlow:(NSRect)dirtyRect {
+ NSGraphicsContext* context = [NSGraphicsContext currentContext];
+ CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]);
- // Draw the glow for hover and the overlay for alerts.
CGFloat hoverAlpha = [self hoverAlpha];
CGFloat alertAlpha = [self alertAlpha];
if (hoverAlpha > 0 || alertAlpha > 0) {
- gfx::ScopedNSGraphicsContextSaveGState contextSave;
CGContextBeginTransparencyLayer(cgContext, 0);
// The alert glow overlay is like the selected state but at most at most 80%
@@ -388,12 +357,14 @@ const CGFloat kRapidCloseDist = 2.5;
backgroundAlpha += (1 - backgroundAlpha) * 0.5 * hoverAlpha;
CGContextSetAlpha(cgContext, backgroundAlpha);
- [self drawFillForActiveTab:dirtyRect];
+ [[self backgroundColorForSelected:YES] set];
+ NSRectFill(dirtyRect);
// ui::ThemeProvider::HasCustomImage is true only if the theme provides the
// image. However, even if the theme doesn't provide a tab background, the
// theme machinery will make one if given a frame image. See
// BrowserThemePack::GenerateTabBackgroundImages for details.
+ ui::ThemeProvider* themeProvider = [[self window] themeProvider];
BOOL hasCustomTheme = themeProvider &&
(themeProvider->HasCustomImage(IDR_THEME_TAB_BACKGROUND) ||
themeProvider->HasCustomImage(IDR_THEME_FRAME));
@@ -422,35 +393,12 @@ const CGFloat kRapidCloseDist = 2.5;
// Draws the tab outline.
- (void)drawStroke:(NSRect)dirtyRect {
- BOOL focused = [[self window] isMainWindow];
- CGFloat alpha = focused ? 1.0 : tabs::kImageNoFocusAlpha;
-
- ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
- float height =
- [rb.GetNativeImageNamed(IDR_TAB_ACTIVE_LEFT).ToNSImage() size].height;
- if (state_ == NSOnState) {
- NSDrawThreePartImage(NSMakeRect(0, 0, NSWidth([self bounds]), height),
- rb.GetNativeImageNamed(IDR_TAB_ACTIVE_LEFT).ToNSImage(),
- rb.GetNativeImageNamed(IDR_TAB_ACTIVE_CENTER).ToNSImage(),
- rb.GetNativeImageNamed(IDR_TAB_ACTIVE_RIGHT).ToNSImage(),
- /*vertical=*/NO,
- NSCompositeSourceOver,
- alpha,
- /*flipped=*/NO);
- } else {
- NSDrawThreePartImage(NSMakeRect(0, 0, NSWidth([self bounds]), height),
- rb.GetNativeImageNamed(IDR_TAB_INACTIVE_LEFT).ToNSImage(),
- rb.GetNativeImageNamed(IDR_TAB_INACTIVE_CENTER).ToNSImage(),
- rb.GetNativeImageNamed(IDR_TAB_INACTIVE_RIGHT).ToNSImage(),
- /*vertical=*/NO,
- NSCompositeSourceOver,
- alpha,
- /*flipped=*/NO);
- }
+ CGFloat alpha = [[self window] isMainWindow] ? 1.0 : tabs::kImageNoFocusAlpha;
+ GetStrokeImage(state_ == NSOnState)
+ ->DrawInRect([self bounds], NSCompositeSourceOver, alpha);
}
- (void)drawRect:(NSRect)dirtyRect {
- // Close button and image are drawn by subviews.
[self drawFill:dirtyRect];
[self drawStroke:dirtyRect];
@@ -749,57 +697,4 @@ const CGFloat kRapidCloseDist = 2.5;
[self setNeedsDisplay:YES];
}
-- (CGImageRef)tabClippingMask {
- // NOTE: NSHeight([self bounds]) doesn't match the height of the bitmaps.
- CGFloat scale = 1;
- if ([[self window] respondsToSelector:@selector(backingScaleFactor)])
- scale = [[self window] backingScaleFactor];
-
- NSRect bounds = [self bounds];
- CGFloat tabWidth = NSWidth(bounds);
- if (tabWidth == maskCacheWidth_ && scale == maskCacheScale_)
- return maskCache_.get();
-
- maskCacheWidth_ = tabWidth;
- maskCacheScale_ = scale;
-
- ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
- NSImage* leftMask = rb.GetNativeImageNamed(IDR_TAB_ALPHA_LEFT).ToNSImage();
- NSImage* rightMask = rb.GetNativeImageNamed(IDR_TAB_ALPHA_RIGHT).ToNSImage();
-
- CGFloat leftWidth = leftMask.size.width;
- CGFloat rightWidth = rightMask.size.width;
-
- // Image masks must be in the DeviceGray colorspace. Create a context and
- // draw the mask into it.
- base::ScopedCFTypeRef<CGColorSpaceRef> colorspace(
- CGColorSpaceCreateDeviceGray());
- base::ScopedCFTypeRef<CGContextRef> maskContext(
- CGBitmapContextCreate(NULL, tabWidth * scale, kMaskHeight * scale,
- 8, tabWidth * scale, colorspace, 0));
- CGContextScaleCTM(maskContext, scale, scale);
- NSGraphicsContext* maskGraphicsContext =
- [NSGraphicsContext graphicsContextWithGraphicsPort:maskContext
- flipped:NO];
-
- gfx::ScopedNSGraphicsContextSaveGState scopedGState;
- [NSGraphicsContext setCurrentContext:maskGraphicsContext];
-
- // Draw mask image.
- [[NSColor blackColor] setFill];
- CGContextFillRect(maskContext, CGRectMake(0, 0, tabWidth, kMaskHeight));
-
- NSDrawThreePartImage(NSMakeRect(0, 0, tabWidth, kMaskHeight),
- leftMask, nil, rightMask, /*vertical=*/NO, NSCompositeSourceOver, 1.0,
- /*flipped=*/NO);
-
- CGFloat middleWidth = tabWidth - leftWidth - rightWidth;
- NSRect middleRect = NSMakeRect(leftWidth, 0, middleWidth, kFillHeight);
- [[NSColor whiteColor] setFill];
- NSRectFill(middleRect);
-
- maskCache_.reset(CGBitmapContextCreateImage(maskContext));
- return maskCache_;
-}
-
@end // @implementation TabView(Private)
« no previous file with comments | « chrome/browser/ui/cocoa/tabs/tab_view.h ('k') | chrome/browser/ui/cocoa/tabs/tab_view_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698