// // 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