blob: 54ef77bb00e7ef2b3d69c066e5760499d2146caa [file] [log] [blame]
//
// GTMNSColor+Luminance.m
//
// Copyright 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
//
#import "GTMDefines.h"
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
#import "GTMNSColor+Luminance.h"
static const CGFloat kGTMLuminanceDarkCutoff = 0.6;
@implementation NSColorSpace (GTMNSColorSpaceLuminanceHelpers)
// TODO(alcor): we may want to keep one of these around for performance reasons
+ (NSColorSpace *)gtm_labColorSpace {
// Observer= 2°, Illuminant= D65
// TODO(alcor): these should come from ColorSync
CGFloat whitePoint[3] = {0.95047, 1.0, 1.08883};
CGFloat blackPoint[3] = {0, 0, 0};
CGFloat range[4] = {-127, 127, -127, 127};
CGColorSpaceRef cs = CGColorSpaceCreateLab(whitePoint, blackPoint, range);
NSColorSpace *space = nil;
if (cs) {
space = [[[NSColorSpace alloc] initWithCGColorSpace:cs] autorelease];
CGColorSpaceRelease(cs);
}
return space;
}
@end
@implementation NSColor (GTMLuminance)
- (NSColor *)labColor {
return [self colorUsingColorSpace:[NSColorSpace gtm_labColorSpace]];
}
- (CGFloat)gtm_luminance {
CGFloat lab[4];
lab[0] = 0.0;
[[self labColor] getComponents:lab];
return lab[0] / 100.0;
}
- (NSColor *)gtm_colorByAdjustingLuminance:(CGFloat)luminance
saturation:(CGFloat)saturation {
CGFloat lab[4];
[[self labColor] getComponents:lab];
lab[0] *= 1.0 + luminance;
// If luminance is greater than 100, we desaturate it so that we don't get
// wild colors coming out of the forumula
if (lab[0] > 100) {
CGFloat clipping = lab[0] - 100;
CGFloat desaturation = (50.0 - clipping) / 50.0;
saturation = MIN(saturation, desaturation);
}
lab[1] *= saturation;
lab[2] *= saturation;
return [NSColor colorWithColorSpace:[NSColorSpace gtm_labColorSpace]
components:lab
count:sizeof(lab) / sizeof(lab[0])];
}
- (NSColor *)gtm_colorByAdjustingLuminance:(CGFloat)luminance {
return [self gtm_colorByAdjustingLuminance:luminance saturation:1.0];
}
// TODO(alcor): these constants are largely made up, come up with a consistent
// set of values or at least guidelines
- (NSColor *)gtm_colorAdjustedFor:(GTMColorationUse)use {
NSColor *color = nil;
switch (use) {
case GTMColorationBaseHighlight:
color = [self gtm_colorByAdjustingLuminance:0.15];
break;
case GTMColorationBaseMidtone:
color = self;
break;
case GTMColorationBaseShadow:
color = [self gtm_colorByAdjustingLuminance:-0.15];
break;
case GTMColorationBasePenumbra:
color = [self gtm_colorByAdjustingLuminance:-0.10];
break;
case GTMColorationLightHighlight:
color = [self gtm_colorByAdjustingLuminance:0.25];
color = [color blendedColorWithFraction:0.9 ofColor:[NSColor whiteColor]];
break;
case GTMColorationLightMidtone:
color = [self blendedColorWithFraction:0.8 ofColor:[NSColor whiteColor]];
break;
case GTMColorationLightShadow:
color = [self blendedColorWithFraction:0.7 ofColor:[NSColor whiteColor]];
color = [color gtm_colorByAdjustingLuminance:-0.02];
break;
case GTMColorationLightPenumbra:
color = [self blendedColorWithFraction:0.8 ofColor:[NSColor whiteColor]];
color = [color gtm_colorByAdjustingLuminance:-0.01];
break;
case GTMColorationDarkHighlight:
color = [self gtm_colorByAdjustingLuminance:-0.20];
break;
case GTMColorationDarkMidtone:
color = [self gtm_colorByAdjustingLuminance:-0.25];
break;
case GTMColorationDarkShadow:
color = [self gtm_colorByAdjustingLuminance:-0.30 saturation:1.4];
break;
case GTMColorationDarkPenumbra:
color = [self gtm_colorByAdjustingLuminance:-0.25];
break;
default:
_GTMDevLog(@"Invalid Coloration Use %lu", (unsigned long)use);
color = self;
break;
}
return color;
}
const CGFloat kDefaultFade = 0.3;
- (NSColor *)gtm_colorAdjustedFor:(GTMColorationUse)use faded:(BOOL)fade {
NSColor *color = [self gtm_colorAdjustedFor:use];
if (fade) {
CGFloat luminance = [color gtm_luminance];
color = [color gtm_colorByAdjustingLuminance:
kDefaultFade * (1.0 - luminance)
saturation:kDefaultFade];
}
return color;
}
- (BOOL)gtm_isDarkColor {
return [self gtm_luminance] < kGTMLuminanceDarkCutoff;
}
- (NSColor *)gtm_legibleTextColor {
return [self gtm_isDarkColor] ? [NSColor whiteColor] : [NSColor blackColor];
}
@end
#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5