Skip to content

Commit efb54d2

Browse files
committed
Tabs: Tune automatic colors and use LAB for deriving colors
Tune how automatic colors are derived. It used to use a custom brightness calculation formula to determine in background color is light or dark, then then simply interpolate the background color to either white or black to drive the fill and tab colors. There were a couple issues: - Really dark background color would end up interpolating more than a moderately dark color, leading to the fill color being desaturated and also perceptually far from background color leading to a high-constrast look. Ideally we want to have a fixed offset in terms of perceptual difference between fill and background color. - The brightness calculation was wrong and flipped the green and blue coefficients. Also, calculating perceptual brightness in sRGB gamma space is inherently wrong as the components are not linear. - Blending colors in sRGB space are also not ideal because we are blending in gamma space (not linear), and also perceptually each component will contribute differently. To fix this, use CIE LAB colors and just change the L* component of the colors in fixed offsets to calculate a new color that's a brighter/darker version fo the previous one. This gives a more stable result over a large range of input colors. The new fill / tab colors are a little more saturated as a result which subjectively may or may not be more pleasant but with automatic colors it won't be perfect, and the user who cares would likely use Vim colorscheme mode instead.
1 parent 8bac906 commit efb54d2

File tree

1 file changed

+49
-27
lines changed

1 file changed

+49
-27
lines changed

src/MacVim/MMTabline/MMTabline.m

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,6 @@
2828
return button;
2929
}
3030

31-
static CGFloat calculateBrightness(NSColor *color) {
32-
if (color.colorSpace.colorSpaceModel == NSColorSpaceModelRGB) {
33-
// Calculate brightness according to a formula from
34-
// the W3C that gives brightness as the eye perceives it. Then lighten
35-
// or darken the default colors based on whether brightness is greater
36-
// than 50% to achieve good visual contrast.
37-
// www.w3.org/WAI/ER/WD-AERT/#color-contrast
38-
CGFloat r, g, b;
39-
[color getRed:&r green:&g blue:&b alpha:NULL];
40-
return r * 0.299 + g * 0.114 + b * 0.587;
41-
} else if (color.colorSpace.colorSpaceModel == NSColorSpaceModelGray) {
42-
return color.whiteComponent;
43-
}
44-
return 1;
45-
}
46-
4731
@implementation MMTabline
4832
{
4933
NSView *_tabsContainer;
@@ -299,7 +283,11 @@ - (NSColor *)tablineStrokeColor
299283

300284
// High-contrast modes. Should stroke to make it easier to read.
301285
NSColor *bgColor = self.tablineBgColor;
302-
CGFloat brightness = calculateBrightness(bgColor);
286+
CGFloat brightness = 1;
287+
if (bgColor.colorSpace.colorSpaceModel == NSColorSpaceModelRGB)
288+
brightness = bgColor.brightnessComponent;
289+
else if (bgColor.colorSpace.colorSpaceModel == NSColorSpaceModelGray)
290+
brightness = bgColor.whiteComponent;
303291
if (brightness > 0.5)
304292
return NSColor.blackColor;
305293
else
@@ -565,29 +553,63 @@ - (void)setAutoColorsSelBg:(NSColor *)back fg:(NSColor *)fore;
565553
{
566554
// Set the colors for the tabline based on the default background and
567555
// foreground colors.
568-
const CGFloat brightness = calculateBrightness(back);
569556

557+
// Calculate CIE Lab color. This is used for deriving other colors that are
558+
// brighter / darker versions of the background color. Using Lab gives better
559+
// results than simpy blending non-linearized RGB values.
560+
// Note: We don't use CGColorSpaceCreateWithName(kCGColorSpaceGenericLab)
561+
// because the API is only available on macOS 10.13+.
562+
const CGFloat whitePoint[3] = {0.95947,1,1.08883}; // D65 white point
563+
const CGFloat blackPoint[3] = {0,0,0};
564+
const CGFloat ranges[4] = {-127, 127, -127, 127};
565+
CGColorSpaceRef labRef = CGColorSpaceCreateLab(whitePoint, blackPoint, ranges);
566+
NSColorSpace *lab = [[NSColorSpace alloc] initWithCGColorSpace:labRef];
567+
NSColor *backLab = [back colorUsingColorSpace:lab];
568+
CGColorSpaceRelease(labRef);
569+
if (backLab.numberOfComponents > 4)
570+
backLab = nil; // don't know how this could happen, but just to be safe
571+
572+
CGFloat backComponents[4] = { 1, 0, 0, 1 }; // L*/a*/b*/alpha. L* is perceptual lightness from 0-100.
573+
[backLab getComponents:backComponents];
574+
CGFloat newComponents[4];
575+
memcpy(newComponents, backComponents, sizeof(newComponents));
576+
577+
// Contrast (different in lightness) for fill bg and tab bg colors relative to the background color
578+
// Note that this is not perceptively accurate to just add a fixed offset to the L* value but it's
579+
// good enough for our purpose.
580+
const CGFloat fillContrastDark = 17.0;
581+
const CGFloat bgContrastDark = fillContrastDark * 0.71;
582+
const CGFloat fillContrastLight = -19.0;
583+
const CGFloat bgContrastLight = fillContrastLight * 0.70;
584+
585+
const CGFloat fillContrast = backComponents[0] >= 40 ? fillContrastLight : fillContrastDark;
586+
const CGFloat bgContrast = backComponents[0] >= 40 ? bgContrastLight : bgContrastDark;
587+
588+
// Assign the colors
570589
_tablineSelBgColor = back;
571590

572-
_tablineSelFgColor = (brightness > 0.5)
573-
? [fore blendedColorWithFraction:0.6 ofColor:NSColor.blackColor]
574-
: [fore blendedColorWithFraction:0.6 ofColor:NSColor.whiteColor];
591+
_tablineSelFgColor = [fore blendedColorWithFraction:0.6
592+
ofColor:(backComponents[0] >= 50 ? NSColor.blackColor : NSColor.whiteColor)];
575593
_addTabButton.fgColor = _tablineSelFgColor;
576594
_backwardScrollButton.fgColor = _tablineSelFgColor;
577595
_forwardScrollButton.fgColor = _tablineSelFgColor;
578596

579-
_tablineBgColor = (brightness > 0.5)
580-
? [back blendedColorWithFraction:0.16 ofColor:NSColor.blackColor]
581-
: [back blendedColorWithFraction:0.13 ofColor:NSColor.whiteColor];
597+
newComponents[0] = backComponents[0] + bgContrast;
598+
_tablineBgColor = [[NSColor colorWithColorSpace:lab
599+
components:newComponents
600+
count:4]
601+
colorUsingColorSpace:NSColorSpace.sRGBColorSpace];
582602

583603
_tablineFgColor = [_tablineSelFgColor blendedColorWithFraction:0.5 ofColor:_tablineBgColor];
584604

585605
_tablineUnfocusedFgColor = [_tablineFgColor blendedColorWithFraction:0.4 ofColor:_tablineBgColor];
586606
_tablineUnfocusedSelFgColor = [_tablineSelFgColor blendedColorWithFraction:0.38 ofColor:_tablineSelBgColor];
587607

588-
_tablineFillBgColor = (brightness > 0.5)
589-
? [back blendedColorWithFraction:0.25 ofColor:NSColor.blackColor]
590-
: [back blendedColorWithFraction:0.18 ofColor:NSColor.whiteColor];
608+
newComponents[0] = backComponents[0] + fillContrast;
609+
_tablineFillBgColor = [[NSColor colorWithColorSpace:lab
610+
components:newComponents
611+
count:4]
612+
colorUsingColorSpace:NSColorSpace.sRGBColorSpace];
591613

592614
[self updateTabStates];
593615
self.needsDisplay = YES;

0 commit comments

Comments
 (0)