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

Side by Side 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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 #import "chrome/browser/ui/cocoa/tabs/tab_view.h" 5 #import "chrome/browser/ui/cocoa/tabs/tab_view.h"
6 6
7 #include "base/i18n/rtl.h" 7 #include "base/i18n/rtl.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/mac/sdk_forward_declarations.h" 9 #include "base/mac/sdk_forward_declarations.h"
10 #include "base/strings/sys_string_conversions.h" 10 #include "base/strings/sys_string_conversions.h"
11 #include "chrome/browser/themes/theme_service.h" 11 #include "chrome/browser/themes/theme_service.h"
12 #import "chrome/browser/ui/cocoa/tabs/media_indicator_button_cocoa.h" 12 #import "chrome/browser/ui/cocoa/tabs/media_indicator_button_cocoa.h"
13 #import "chrome/browser/ui/cocoa/tabs/tab_controller.h" 13 #import "chrome/browser/ui/cocoa/tabs/tab_controller.h"
14 #import "chrome/browser/ui/cocoa/tabs/tab_window_controller.h" 14 #import "chrome/browser/ui/cocoa/tabs/tab_window_controller.h"
15 #import "chrome/browser/ui/cocoa/themed_window.h" 15 #import "chrome/browser/ui/cocoa/themed_window.h"
16 #import "chrome/browser/ui/cocoa/view_id_util.h" 16 #import "chrome/browser/ui/cocoa/view_id_util.h"
17 #include "chrome/grit/generated_resources.h" 17 #include "chrome/grit/generated_resources.h"
18 #include "grit/theme_resources.h" 18 #include "grit/theme_resources.h"
19 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMFadeTruncatingTextFiel dCell.h" 19 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMFadeTruncatingTextFiel dCell.h"
20 #import "ui/base/cocoa/nsgraphics_context_additions.h" 20 #import "ui/base/cocoa/nsgraphics_context_additions.h"
21 #import "ui/base/cocoa/nsview_additions.h" 21 #import "ui/base/cocoa/nsview_additions.h"
22 #include "ui/base/cocoa/three_part_image.h"
22 #include "ui/base/l10n/l10n_util.h" 23 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/base/resource/resource_bundle.h" 24 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" 25 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
25 26
26 27
27 const int kMaskHeight = 29; // Height of the mask bitmap.
28 const int kFillHeight = 25; // Height of the "mask on" part of the mask bitmap. 28 const int kFillHeight = 25; // Height of the "mask on" part of the mask bitmap.
29 29
30 // The amount of time in seconds during which each type of glow increases, holds 30 // The amount of time in seconds during which each type of glow increases, holds
31 // steady, and decreases, respectively. 31 // steady, and decreases, respectively.
32 const NSTimeInterval kHoverShowDuration = 0.2; 32 const NSTimeInterval kHoverShowDuration = 0.2;
33 const NSTimeInterval kHoverHoldDuration = 0.02; 33 const NSTimeInterval kHoverHoldDuration = 0.02;
34 const NSTimeInterval kHoverHideDuration = 0.4; 34 const NSTimeInterval kHoverHideDuration = 0.4;
35 const NSTimeInterval kAlertShowDuration = 0.4; 35 const NSTimeInterval kAlertShowDuration = 0.4;
36 const NSTimeInterval kAlertHoldDuration = 0.4; 36 const NSTimeInterval kAlertHoldDuration = 0.4;
37 const NSTimeInterval kAlertHideDuration = 0.4; 37 const NSTimeInterval kAlertHideDuration = 0.4;
38 38
39 // The default time interval in seconds between glow updates (when 39 // The default time interval in seconds between glow updates (when
40 // increasing/decreasing). 40 // increasing/decreasing).
41 const NSTimeInterval kGlowUpdateInterval = 0.025; 41 const NSTimeInterval kGlowUpdateInterval = 0.025;
42 42
43 // This is used to judge whether the mouse has moved during rapid closure; if it 43 // This is used to judge whether the mouse has moved during rapid closure; if it
44 // has moved less than the threshold, we want to close the tab. 44 // has moved less than the threshold, we want to close the tab.
45 const CGFloat kRapidCloseDist = 2.5; 45 const CGFloat kRapidCloseDist = 2.5;
46 46
47 namespace {
48
49 ui::ThreePartImage* GetMaskImage() {
50 static ui::ThreePartImage* mask =
51 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
52 return mask;
53 }
54
55 ui::ThreePartImage* GetStrokeImage(bool active) {
56 static ui::ThreePartImage* activeStroke = new ui::ThreePartImage(
57 IDR_TAB_ACTIVE_LEFT, IDR_TAB_ACTIVE_CENTER, IDR_TAB_ACTIVE_RIGHT);
58 static ui::ThreePartImage* inactiveStroke = new ui::ThreePartImage(
59 IDR_TAB_INACTIVE_LEFT, IDR_TAB_INACTIVE_CENTER, IDR_TAB_INACTIVE_RIGHT);
60
61 return active ? activeStroke : inactiveStroke;
62 }
63
64 } // namespace
65
47 @interface TabView(Private) 66 @interface TabView(Private)
48 67
49 - (void)resetLastGlowUpdateTime; 68 - (void)resetLastGlowUpdateTime;
50 - (NSTimeInterval)timeElapsedSinceLastGlowUpdate; 69 - (NSTimeInterval)timeElapsedSinceLastGlowUpdate;
51 - (void)adjustGlowValue; 70 - (void)adjustGlowValue;
52 - (CGImageRef)tabClippingMask;
53 71
54 @end // TabView(Private) 72 @end // TabView(Private)
55 73
56 @implementation TabView 74 @implementation TabView
57 75
58 @synthesize state = state_; 76 @synthesize state = state_;
59 @synthesize hoverAlpha = hoverAlpha_; 77 @synthesize hoverAlpha = hoverAlpha_;
60 @synthesize alertAlpha = alertAlpha_; 78 @synthesize alertAlpha = alertAlpha_;
61 @synthesize closing = closing_; 79 @synthesize closing = closing_;
62 80
(...skipping 12 matching lines...) Expand all
75 [titleView_ setAutoresizingMask:NSViewWidthSizable]; 93 [titleView_ setAutoresizingMask:NSViewWidthSizable];
76 base::scoped_nsobject<GTMFadeTruncatingTextFieldCell> labelCell( 94 base::scoped_nsobject<GTMFadeTruncatingTextFieldCell> labelCell(
77 [[GTMFadeTruncatingTextFieldCell alloc] initTextCell:@"Label"]); 95 [[GTMFadeTruncatingTextFieldCell alloc] initTextCell:@"Label"]);
78 [labelCell setControlSize:NSSmallControlSize]; 96 [labelCell setControlSize:NSSmallControlSize];
79 CGFloat fontSize = [NSFont systemFontSizeForControlSize:NSSmallControlSize]; 97 CGFloat fontSize = [NSFont systemFontSizeForControlSize:NSSmallControlSize];
80 NSFont* font = [NSFont fontWithName:[[labelCell font] fontName] 98 NSFont* font = [NSFont fontWithName:[[labelCell font] fontName]
81 size:fontSize]; 99 size:fontSize];
82 [labelCell setFont:font]; 100 [labelCell setFont:font];
83 [titleView_ setCell:labelCell]; 101 [titleView_ setCell:labelCell];
84 titleViewCell_ = labelCell; 102 titleViewCell_ = labelCell;
103
104 [self setWantsLayer:YES]; // -drawFill: needs a layer.
85 } 105 }
86 return self; 106 return self;
87 } 107 }
88 108
89 - (void)dealloc { 109 - (void)dealloc {
90 // Cancel any delayed requests that may still be pending (drags or hover). 110 // Cancel any delayed requests that may still be pending (drags or hover).
91 [NSObject cancelPreviousPerformRequestsWithTarget:self]; 111 [NSObject cancelPreviousPerformRequestsWithTarget:self];
92 [super dealloc]; 112 [super dealloc];
93 } 113 }
94 114
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
150 } 170 }
151 171
152 // Determines which view a click in our frame actually hit. It's either this 172 // Determines which view a click in our frame actually hit. It's either this
153 // view or one of the child buttons. 173 // view or one of the child buttons.
154 - (NSView*)hitTest:(NSPoint)aPoint { 174 - (NSView*)hitTest:(NSPoint)aPoint {
155 NSView* const defaultHitTestResult = [super hitTest:aPoint]; 175 NSView* const defaultHitTestResult = [super hitTest:aPoint];
156 if ([defaultHitTestResult isKindOfClass:[NSButton class]]) 176 if ([defaultHitTestResult isKindOfClass:[NSButton class]])
157 return defaultHitTestResult; 177 return defaultHitTestResult;
158 178
159 NSPoint viewPoint = [self convertPoint:aPoint fromView:[self superview]]; 179 NSPoint viewPoint = [self convertPoint:aPoint fromView:[self superview]];
160 NSRect pointRect = NSMakeRect(viewPoint.x, viewPoint.y, 1, 1); 180 NSRect maskRect = [self bounds];
161 181 maskRect.size.height = kFillHeight;
162 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 182 return GetMaskImage()->HitTest(viewPoint, maskRect) ? self : nil;
163 NSImage* left = rb.GetNativeImageNamed(IDR_TAB_ALPHA_LEFT).ToNSImage();
164 if (viewPoint.x < [left size].width) {
165 NSRect imageRect = NSMakeRect(0, 0, [left size].width, [left size].height);
166 if ([left hitTestRect:pointRect withImageDestinationRect:imageRect
167 context:nil hints:nil flipped:NO]) {
168 return self;
169 }
170 return nil;
171 }
172
173 NSImage* right = rb.GetNativeImageNamed(IDR_TAB_ALPHA_RIGHT).ToNSImage();
174 CGFloat rightX = NSWidth([self bounds]) - [right size].width;
175 if (viewPoint.x > rightX) {
176 NSRect imageRect = NSMakeRect(
177 rightX, 0, [right size].width, [right size].height);
178 if ([right hitTestRect:pointRect withImageDestinationRect:imageRect
179 context:nil hints:nil flipped:NO]) {
180 return self;
181 }
182 return nil;
183 }
184
185 if (viewPoint.y < kFillHeight)
186 return self;
187 return nil;
188 } 183 }
189 184
190 // Returns |YES| if this tab can be torn away into a new window. 185 // Returns |YES| if this tab can be torn away into a new window.
191 - (BOOL)canBeDragged { 186 - (BOOL)canBeDragged {
192 return [controller_ tabCanBeDragged:controller_]; 187 return [controller_ tabCanBeDragged:controller_];
193 } 188 }
194 189
195 // Handle clicks and drags in this button. We get here because we have 190 // Handle clicks and drags in this button. We get here because we have
196 // overridden acceptsFirstMouse: and the click is within our bounds. 191 // overridden acceptsFirstMouse: and the click is within our bounds.
197 - (void)mouseDown:(NSEvent*)theEvent { 192 - (void)mouseDown:(NSEvent*)theEvent {
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
305 }, 300 },
306 }; 301 };
307 302
308 // Themes don't have an inactive image so only look for one if there's no 303 // Themes don't have an inactive image so only look for one if there's no
309 // theme. 304 // theme.
310 bool active = 305 bool active =
311 [[self window] isMainWindow] || !themeProvider->UsingDefaultTheme(); 306 [[self window] isMainWindow] || !themeProvider->UsingDefaultTheme();
312 return themeProvider->GetNSImageColorNamed(bitmapResources[active][selected]); 307 return themeProvider->GetNSImageColorNamed(bitmapResources[active][selected]);
313 } 308 }
314 309
315 // Draws the active tab background.
316 - (void)drawFillForActiveTab:(NSRect)dirtyRect {
317 NSColor* backgroundImageColor = [self backgroundColorForSelected:YES];
318 [backgroundImageColor set];
319
320 // Themes can have partially transparent images. NSRectFill() is measurably
321 // faster though, so call it for the known-safe default theme.
322 ThemeService* themeProvider =
323 static_cast<ThemeService*>([[self window] themeProvider]);
324 if (themeProvider && themeProvider->UsingDefaultTheme())
325 NSRectFill(dirtyRect);
326 else
327 NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver);
328 }
329
330 // Draws the tab background. 310 // Draws the tab background.
331 - (void)drawFill:(NSRect)dirtyRect { 311 - (void)drawFill:(NSRect)dirtyRect {
332 gfx::ScopedNSGraphicsContextSaveGState scopedGState; 312 gfx::ScopedNSGraphicsContextSaveGState scopedGState;
313 NSRect bounds = [self bounds];
314
315 NSRect clippingRect = bounds;
316 clippingRect.size.height = kFillHeight;
317 if (state_ != NSOnState) {
318 // Background tabs should not paint over the tab strip separator, which is
319 // two pixels high in both lodpi and hidpi.
320 clippingRect.origin.y = 2 * [self cr_lineWidth];
321 clippingRect.size.height -= clippingRect.origin.y;
322 }
323 NSRectClip(clippingRect);
324
325 NSPoint position = [[self window]
326 themeImagePositionForAlignment:THEME_IMAGE_ALIGN_WITH_TAB_STRIP];
327 [[NSGraphicsContext currentContext] cr_setPatternPhase:position forView:self];
328
329 [[self backgroundColorForSelected:(state_ != NSOffState)] set];
330 NSRectFill(dirtyRect);
331
332 if (state_ == NSOffState)
333 [self drawGlow:dirtyRect];
334
335 // If we filled outside the middle rect, we need to erase what we filled
336 // outside the tab's shape.
337 // This only works if we are drawing to our own backing layer.
338 if (!NSContainsRect(GetMaskImage()->GetMiddleRect(bounds), dirtyRect)) {
339 DCHECK([self layer]);
340 GetMaskImage()->DrawInRect(bounds, NSCompositeDestinationIn, 1.0);
341 }
342 }
343
344 // Draw the glow for hover and the overlay for alerts.
345 - (void)drawGlow:(NSRect)dirtyRect {
333 NSGraphicsContext* context = [NSGraphicsContext currentContext]; 346 NSGraphicsContext* context = [NSGraphicsContext currentContext];
334 CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]); 347 CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]);
335 348
336 ThemeService* themeProvider =
337 static_cast<ThemeService*>([[self window] themeProvider]);
338 NSPoint position = [[self window]
339 themeImagePositionForAlignment: THEME_IMAGE_ALIGN_WITH_TAB_STRIP];
340 [context cr_setPatternPhase:position forView:self];
341
342 CGImageRef mask([self tabClippingMask]);
343 CGRect maskBounds = CGRectMake(0, 0, maskCacheWidth_, kMaskHeight);
344 CGContextClipToMask(cgContext, maskBounds, mask);
345
346 // There is only 1 active tab at a time.
347 // It has a different fill color which draws over the separator line.
348 if (state_ == NSOnState) {
349 [self drawFillForActiveTab:dirtyRect];
350 return;
351 }
352
353 // Background tabs should not paint over the tab strip separator, which is
354 // two pixels high in both lodpi and hidpi.
355 if (dirtyRect.origin.y < 1)
356 dirtyRect.origin.y = 2 * [self cr_lineWidth];
357
358 // There can be multiple selected tabs.
359 // They have the same fill color as the active tab, but do not draw over
360 // the separator.
361 if (state_ == NSMixedState) {
362 [self drawFillForActiveTab:dirtyRect];
363 return;
364 }
365
366 // Draw the tab background.
367 NSColor* backgroundImageColor = [self backgroundColorForSelected:NO];
368 [backgroundImageColor set];
369
370 // Themes can have partially transparent images. NSRectFill() is measurably
371 // faster though, so call it for the known-safe default theme.
372 bool usingDefaultTheme = themeProvider && themeProvider->UsingDefaultTheme();
373 if (usingDefaultTheme)
374 NSRectFill(dirtyRect);
375 else
376 NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver);
377
378 // Draw the glow for hover and the overlay for alerts.
379 CGFloat hoverAlpha = [self hoverAlpha]; 349 CGFloat hoverAlpha = [self hoverAlpha];
380 CGFloat alertAlpha = [self alertAlpha]; 350 CGFloat alertAlpha = [self alertAlpha];
381 if (hoverAlpha > 0 || alertAlpha > 0) { 351 if (hoverAlpha > 0 || alertAlpha > 0) {
382 gfx::ScopedNSGraphicsContextSaveGState contextSave;
383 CGContextBeginTransparencyLayer(cgContext, 0); 352 CGContextBeginTransparencyLayer(cgContext, 0);
384 353
385 // The alert glow overlay is like the selected state but at most at most 80% 354 // The alert glow overlay is like the selected state but at most at most 80%
386 // opaque. The hover glow brings up the overlay's opacity at most 50%. 355 // opaque. The hover glow brings up the overlay's opacity at most 50%.
387 CGFloat backgroundAlpha = 0.8 * alertAlpha; 356 CGFloat backgroundAlpha = 0.8 * alertAlpha;
388 backgroundAlpha += (1 - backgroundAlpha) * 0.5 * hoverAlpha; 357 backgroundAlpha += (1 - backgroundAlpha) * 0.5 * hoverAlpha;
389 CGContextSetAlpha(cgContext, backgroundAlpha); 358 CGContextSetAlpha(cgContext, backgroundAlpha);
390 359
391 [self drawFillForActiveTab:dirtyRect]; 360 [[self backgroundColorForSelected:YES] set];
361 NSRectFill(dirtyRect);
392 362
393 // ui::ThemeProvider::HasCustomImage is true only if the theme provides the 363 // ui::ThemeProvider::HasCustomImage is true only if the theme provides the
394 // image. However, even if the theme doesn't provide a tab background, the 364 // image. However, even if the theme doesn't provide a tab background, the
395 // theme machinery will make one if given a frame image. See 365 // theme machinery will make one if given a frame image. See
396 // BrowserThemePack::GenerateTabBackgroundImages for details. 366 // BrowserThemePack::GenerateTabBackgroundImages for details.
367 ui::ThemeProvider* themeProvider = [[self window] themeProvider];
397 BOOL hasCustomTheme = themeProvider && 368 BOOL hasCustomTheme = themeProvider &&
398 (themeProvider->HasCustomImage(IDR_THEME_TAB_BACKGROUND) || 369 (themeProvider->HasCustomImage(IDR_THEME_TAB_BACKGROUND) ||
399 themeProvider->HasCustomImage(IDR_THEME_FRAME)); 370 themeProvider->HasCustomImage(IDR_THEME_FRAME));
400 // Draw a mouse hover gradient for the default themes. 371 // Draw a mouse hover gradient for the default themes.
401 if (hoverAlpha > 0) { 372 if (hoverAlpha > 0) {
402 if (themeProvider && !hasCustomTheme) { 373 if (themeProvider && !hasCustomTheme) {
403 base::scoped_nsobject<NSGradient> glow([NSGradient alloc]); 374 base::scoped_nsobject<NSGradient> glow([NSGradient alloc]);
404 [glow initWithStartingColor:[NSColor colorWithCalibratedWhite:1.0 375 [glow initWithStartingColor:[NSColor colorWithCalibratedWhite:1.0
405 alpha:1.0 * hoverAlpha] 376 alpha:1.0 * hoverAlpha]
406 endingColor:[NSColor colorWithCalibratedWhite:1.0 377 endingColor:[NSColor colorWithCalibratedWhite:1.0
407 alpha:0.0]]; 378 alpha:0.0]];
408 NSRect rect = [self bounds]; 379 NSRect rect = [self bounds];
409 NSPoint point = hoverPoint_; 380 NSPoint point = hoverPoint_;
410 point.y = NSHeight(rect); 381 point.y = NSHeight(rect);
411 [glow drawFromCenter:point 382 [glow drawFromCenter:point
412 radius:0.0 383 radius:0.0
413 toCenter:point 384 toCenter:point
414 radius:NSWidth(rect) / 3.0 385 radius:NSWidth(rect) / 3.0
415 options:NSGradientDrawsBeforeStartingLocation]; 386 options:NSGradientDrawsBeforeStartingLocation];
416 } 387 }
417 } 388 }
418 389
419 CGContextEndTransparencyLayer(cgContext); 390 CGContextEndTransparencyLayer(cgContext);
420 } 391 }
421 } 392 }
422 393
423 // Draws the tab outline. 394 // Draws the tab outline.
424 - (void)drawStroke:(NSRect)dirtyRect { 395 - (void)drawStroke:(NSRect)dirtyRect {
425 BOOL focused = [[self window] isMainWindow]; 396 CGFloat alpha = [[self window] isMainWindow] ? 1.0 : tabs::kImageNoFocusAlpha;
426 CGFloat alpha = focused ? 1.0 : tabs::kImageNoFocusAlpha; 397 GetStrokeImage(state_ == NSOnState)
427 398 ->DrawInRect([self bounds], NSCompositeSourceOver, alpha);
428 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
429 float height =
430 [rb.GetNativeImageNamed(IDR_TAB_ACTIVE_LEFT).ToNSImage() size].height;
431 if (state_ == NSOnState) {
432 NSDrawThreePartImage(NSMakeRect(0, 0, NSWidth([self bounds]), height),
433 rb.GetNativeImageNamed(IDR_TAB_ACTIVE_LEFT).ToNSImage(),
434 rb.GetNativeImageNamed(IDR_TAB_ACTIVE_CENTER).ToNSImage(),
435 rb.GetNativeImageNamed(IDR_TAB_ACTIVE_RIGHT).ToNSImage(),
436 /*vertical=*/NO,
437 NSCompositeSourceOver,
438 alpha,
439 /*flipped=*/NO);
440 } else {
441 NSDrawThreePartImage(NSMakeRect(0, 0, NSWidth([self bounds]), height),
442 rb.GetNativeImageNamed(IDR_TAB_INACTIVE_LEFT).ToNSImage(),
443 rb.GetNativeImageNamed(IDR_TAB_INACTIVE_CENTER).ToNSImage(),
444 rb.GetNativeImageNamed(IDR_TAB_INACTIVE_RIGHT).ToNSImage(),
445 /*vertical=*/NO,
446 NSCompositeSourceOver,
447 alpha,
448 /*flipped=*/NO);
449 }
450 } 399 }
451 400
452 - (void)drawRect:(NSRect)dirtyRect { 401 - (void)drawRect:(NSRect)dirtyRect {
453 // Close button and image are drawn by subviews.
454 [self drawFill:dirtyRect]; 402 [self drawFill:dirtyRect];
455 [self drawStroke:dirtyRect]; 403 [self drawStroke:dirtyRect];
456 404
457 // We draw the title string directly instead of using a NSTextField subview. 405 // We draw the title string directly instead of using a NSTextField subview.
458 // This is so that we can get font smoothing to work on earlier OS, and even 406 // This is so that we can get font smoothing to work on earlier OS, and even
459 // when the tab background is a pattern image (when using themes). 407 // when the tab background is a pattern image (when using themes).
460 if (![titleView_ isHidden]) { 408 if (![titleView_ isHidden]) {
461 gfx::ScopedNSGraphicsContextSaveGState scopedGState; 409 gfx::ScopedNSGraphicsContextSaveGState scopedGState;
462 NSGraphicsContext* context = [NSGraphicsContext currentContext]; 410 NSGraphicsContext* context = [NSGraphicsContext currentContext];
463 CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]); 411 CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]);
(...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after
742 } 690 }
743 } 691 }
744 692
745 if (nextUpdate < kNoUpdate) 693 if (nextUpdate < kNoUpdate)
746 [self performSelector:_cmd withObject:nil afterDelay:nextUpdate]; 694 [self performSelector:_cmd withObject:nil afterDelay:nextUpdate];
747 695
748 [self resetLastGlowUpdateTime]; 696 [self resetLastGlowUpdateTime];
749 [self setNeedsDisplay:YES]; 697 [self setNeedsDisplay:YES];
750 } 698 }
751 699
752 - (CGImageRef)tabClippingMask {
753 // NOTE: NSHeight([self bounds]) doesn't match the height of the bitmaps.
754 CGFloat scale = 1;
755 if ([[self window] respondsToSelector:@selector(backingScaleFactor)])
756 scale = [[self window] backingScaleFactor];
757
758 NSRect bounds = [self bounds];
759 CGFloat tabWidth = NSWidth(bounds);
760 if (tabWidth == maskCacheWidth_ && scale == maskCacheScale_)
761 return maskCache_.get();
762
763 maskCacheWidth_ = tabWidth;
764 maskCacheScale_ = scale;
765
766 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
767 NSImage* leftMask = rb.GetNativeImageNamed(IDR_TAB_ALPHA_LEFT).ToNSImage();
768 NSImage* rightMask = rb.GetNativeImageNamed(IDR_TAB_ALPHA_RIGHT).ToNSImage();
769
770 CGFloat leftWidth = leftMask.size.width;
771 CGFloat rightWidth = rightMask.size.width;
772
773 // Image masks must be in the DeviceGray colorspace. Create a context and
774 // draw the mask into it.
775 base::ScopedCFTypeRef<CGColorSpaceRef> colorspace(
776 CGColorSpaceCreateDeviceGray());
777 base::ScopedCFTypeRef<CGContextRef> maskContext(
778 CGBitmapContextCreate(NULL, tabWidth * scale, kMaskHeight * scale,
779 8, tabWidth * scale, colorspace, 0));
780 CGContextScaleCTM(maskContext, scale, scale);
781 NSGraphicsContext* maskGraphicsContext =
782 [NSGraphicsContext graphicsContextWithGraphicsPort:maskContext
783 flipped:NO];
784
785 gfx::ScopedNSGraphicsContextSaveGState scopedGState;
786 [NSGraphicsContext setCurrentContext:maskGraphicsContext];
787
788 // Draw mask image.
789 [[NSColor blackColor] setFill];
790 CGContextFillRect(maskContext, CGRectMake(0, 0, tabWidth, kMaskHeight));
791
792 NSDrawThreePartImage(NSMakeRect(0, 0, tabWidth, kMaskHeight),
793 leftMask, nil, rightMask, /*vertical=*/NO, NSCompositeSourceOver, 1.0,
794 /*flipped=*/NO);
795
796 CGFloat middleWidth = tabWidth - leftWidth - rightWidth;
797 NSRect middleRect = NSMakeRect(leftWidth, 0, middleWidth, kFillHeight);
798 [[NSColor whiteColor] setFill];
799 NSRectFill(middleRect);
800
801 maskCache_.reset(CGBitmapContextCreateImage(maskContext));
802 return maskCache_;
803 }
804
805 @end // @implementation TabView(Private) 700 @end // @implementation TabView(Private)
OLDNEW
« 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