You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1303 lines
35 KiB

// Copyright (C) 2013 by Benjamin Gordon
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and
// associated documentation files (the "Software"), to
// deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall
// be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "Colours.h"
// Swizzle
#import <objc/runtime.h>
#pragma mark - Create correct iOS/OSX implementation
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
@implementation UIColor (Colours)
#elif TARGET_OS_MAC
#import <AppKit/AppKit.h>
@implementation NSColor (Colours)
#endif
#pragma mark - Color from Hex
+ (instancetype)colorFromHexString:(NSString *)hexString
{
unsigned rgbValue = 0;
hexString = [hexString stringByReplacingOccurrencesOfString:@"#" withString:@""];
NSScanner *scanner = [NSScanner scannerWithString:hexString];
[scanner scanHexInt:&rgbValue];
return [[self class] colorWithR:((rgbValue & 0xFF0000) >> 16) G:((rgbValue & 0xFF00) >> 8) B:(rgbValue & 0xFF) A:1.0];
}
#pragma mark - Hex from Color
- (NSString *)hexString
{
NSArray *colorArray = [self rgbaArray];
int r = [colorArray[0] floatValue] * 255;
int g = [colorArray[1] floatValue] * 255;
int b = [colorArray[2] floatValue] * 255;
NSString *red = [NSString stringWithFormat:@"%02x", r];
NSString *green = [NSString stringWithFormat:@"%02x", g];
NSString *blue = [NSString stringWithFormat:@"%02x", b];
return [NSString stringWithFormat:@"#%@%@%@", red, green, blue];
}
#pragma mark - Color from RGBA
+ (instancetype)colorFromRGBAArray:(NSArray *)rgbaArray
{
if (rgbaArray.count < 4) {
return [[self class] clearColor];
}
return [[self class] colorWithRed:[rgbaArray[0] floatValue]
green:[rgbaArray[1] floatValue]
blue:[rgbaArray[2] floatValue]
alpha:[rgbaArray[3] floatValue]];
}
+ (instancetype)colorFromRGBADictionary:(NSDictionary *)rgbaDict
{
if (rgbaDict[kColoursRGBA_R] && rgbaDict[kColoursRGBA_G] && rgbaDict[kColoursRGBA_B] && rgbaDict[kColoursRGBA_A]) {
return [[self class] colorWithRed:[rgbaDict[kColoursRGBA_R] floatValue]
green:[rgbaDict[kColoursRGBA_G] floatValue]
blue:[rgbaDict[kColoursRGBA_B] floatValue]
alpha:[rgbaDict[kColoursRGBA_A] floatValue]];
}
return [[self class] clearColor];
}
#pragma mark - RGBA from Color
- (NSArray *)rgbaArray
{
CGFloat r=0,g=0,b=0,a=0;
if ([self respondsToSelector:@selector(getRed:green:blue:alpha:)]) {
[self getRed:&r green:&g blue:&b alpha:&a];
}
else {
const CGFloat *components = CGColorGetComponents(self.CGColor);
r = components[0];
g = components[1];
b = components[2];
a = components[3];
}
return @[@(r),
@(g),
@(b),
@(a)];
}
- (NSDictionary *)rgbaDictionary
{
CGFloat r=0,g=0,b=0,a=0;
if ([self respondsToSelector:@selector(getRed:green:blue:alpha:)]) {
[self getRed:&r green:&g blue:&b alpha:&a];
}
else {
const CGFloat *components = CGColorGetComponents(self.CGColor);
r = components[0];
g = components[1];
b = components[2];
a = components[3];
}
return @{kColoursRGBA_R:@(r),
kColoursRGBA_G:@(g),
kColoursRGBA_B:@(b),
kColoursRGBA_A:@(a)};
}
#pragma mark - HSBA from Color
- (NSArray *)hsbaArray
{
// Takes a [self class] and returns Hue,Saturation,Brightness,Alpha values in NSNumber form
CGFloat h=0,s=0,b=0,a=0;
if ([self respondsToSelector:@selector(getHue:saturation:brightness:alpha:)]) {
[self getHue:&h saturation:&s brightness:&b alpha:&a];
}
return @[@(h),
@(s),
@(b),
@(a)];
}
- (NSDictionary *)hsbaDictionary
{
CGFloat h=0,s=0,b=0,a=0;
if ([self respondsToSelector:@selector(getHue:saturation:brightness:alpha:)]) {
[self getHue:&h saturation:&s brightness:&b alpha:&a];
}
return @{kColoursHSBA_H:@(h),
kColoursHSBA_S:@(s),
kColoursHSBA_B:@(b),
kColoursHSBA_A:@(a)};
}
#pragma mark - Color from HSBA
+ (instancetype)colorFromHSBAArray:(NSArray *)hsbaArray
{
if (hsbaArray.count < 4) {
return [[self class] clearColor];
}
return [[self class] colorWithHue:[hsbaArray[0] doubleValue]
saturation:[hsbaArray[1] doubleValue]
brightness:[hsbaArray[2] doubleValue]
alpha:[hsbaArray[3] doubleValue]];
}
+ (instancetype)colorFromHSBADictionary:(NSDictionary *)hsbaDict
{
if (hsbaDict[kColoursHSBA_H] && hsbaDict[kColoursHSBA_S] && hsbaDict[kColoursHSBA_B] && hsbaDict[kColoursHSBA_A]) {
return [[self class] colorWithHue:[hsbaDict[kColoursHSBA_H] doubleValue]
saturation:[hsbaDict[kColoursHSBA_S] doubleValue]
brightness:[hsbaDict[kColoursHSBA_B] doubleValue]
alpha:[hsbaDict[kColoursHSBA_A] doubleValue]];
}
return [[self class] clearColor];
}
#pragma mark - LAB from Color
- (NSArray *)CIE_LabArray {
// Convert Color to XYZ format first
NSArray *rgba = [self rgbaArray];
CGFloat R = [rgba[0] floatValue];
CGFloat G = [rgba[1] floatValue];
CGFloat B = [rgba[2] floatValue];
// Create deltaR block
void (^deltaRGB)(CGFloat *R);
deltaRGB = ^(CGFloat *R) {
*R = (*R > 0.04045) ? pow((*R + 0.055)/1.055, 2.40) : (*R/12.92);
};
deltaRGB(&R);
deltaRGB(&G);
deltaRGB(&B);
CGFloat X = R*41.24 + G*35.76 + B*18.05;
CGFloat Y = R*21.26 + G*71.52 + B*7.22;
CGFloat Z = R*1.93 + G*11.92 + B*95.05;
// Convert XYZ to L*a*b*
X = X/95.047;
Y = Y/100.000;
Z = Z/108.883;
// Create deltaF block
void (^deltaF)(CGFloat *f);
deltaF = ^(CGFloat *f){
*f = (*f > pow((6.0/29.0), 3.0)) ? pow(*f, 1.0/3.0) : (1/3)*pow((29.0/6.0), 2.0) * *f + 4/29.0;
};
deltaF(&X);
deltaF(&Y);
deltaF(&Z);
NSNumber *L = @(116*Y - 16);
NSNumber *a = @(500 * (X - Y));
NSNumber *b = @(200 * (Y - Z));
return @[L,
a,
b,
rgba[3]];
}
- (NSDictionary *)CIE_LabDictionary {
NSArray *colors = [self CIE_LabArray];
return @{kColoursCIE_L:colors[0],
kColoursCIE_A:colors[1],
kColoursCIE_B:colors[2],
kColoursCIE_alpha:colors[3],};
}
#pragma mark - Color from LAB
+ (instancetype)colorFromCIE_LabArray:(NSArray *)colors {
if (!colors || colors.count < 4) {
return [[self class] clearColor];
}
// Convert LAB to XYZ
CGFloat L = [colors[0] floatValue];
CGFloat A = [colors[1] floatValue];
CGFloat B = [colors[2] floatValue];
CGFloat Y = (L + 16.0)/116.0;
CGFloat X = A/500 + Y;
CGFloat Z = Y - B/200;
void (^deltaXYZ)(CGFloat *);
deltaXYZ = ^(CGFloat *k){
*k = (pow(*k, 3.0) > 0.008856) ? pow(*k, 3.0) : (*k - 4/29.0)/7.787;
};
deltaXYZ(&X);
deltaXYZ(&Y);
deltaXYZ(&Z);
X = X*.95047;
Y = Y*1.00000;
Z = Z*1.08883;
// Convert XYZ to RGB
CGFloat R = X*3.2406 + Y*-1.5372 + Z*-0.4986;
CGFloat G = X*-0.9689 + Y*1.8758 + Z*0.0415;
CGFloat _B = X*0.0557 + Y*-0.2040 + Z*1.0570;
void (^deltaRGB)(CGFloat *);
deltaRGB = ^(CGFloat *k){
*k = (*k > 0.0031308) ? 1.055 * (pow(*k, (1/2.4))) - 0.055 : *k * 12.92;
};
deltaRGB(&R);
deltaRGB(&G);
deltaRGB(&_B);
// return Color
return [[self class] colorFromRGBAArray:@[@(R), @(G), @(_B), colors[3]]];
}
+ (instancetype)colorFromCIE_LabDictionary:(NSDictionary *)colors {
if (!colors) {
return [[self class] clearColor];
}
if (colors[kColoursCIE_L] && colors[kColoursCIE_A] && colors[kColoursCIE_B] && colors[kColoursCIE_alpha]) {
return [self colorFromCIE_LabArray:@[colors[kColoursCIE_L],
colors[kColoursCIE_A],
colors[kColoursCIE_B],
colors[kColoursCIE_alpha]]];
}
return [[self class] clearColor];
}
#pragma mark - Color to CMYK
- (NSArray *)cmykArray
{
// Convert RGB to CMY
NSArray *rgb = [self rgbaArray];
CGFloat C = 1 - [rgb[0] floatValue];
CGFloat M = 1 - [rgb[1] floatValue];
CGFloat Y = 1 - [rgb[2] floatValue];
// Find K
CGFloat K = MIN(1, MIN(C, MIN(Y, M)));
if (K == 1) {
C = 0;
M = 0;
Y = 0;
}
else {
void (^newCMYK)(CGFloat *);
newCMYK = ^(CGFloat *x){
*x = (*x - K)/(1 - K);
};
newCMYK(&C);
newCMYK(&M);
newCMYK(&Y);
}
return @[@(C),
@(M),
@(Y),
@(K)];
}
- (NSDictionary *)cmykDictionary
{
NSArray *colors = [self cmykArray];
return @{kColoursCMYK_C:colors[0],
kColoursCMYK_M:colors[1],
kColoursCMYK_Y:colors[2],
kColoursCMYK_K:colors[3]};
}
#pragma mark - CMYK to Color
+ (instancetype)colorFromCMYKArray:(NSArray *)cmyk
{
if (!cmyk || cmyk.count < 4) {
return [[self class] clearColor];
}
// Find CMY values
CGFloat C = [cmyk[0] floatValue];
CGFloat M = [cmyk[1] floatValue];
CGFloat Y = [cmyk[2] floatValue];
CGFloat K = [cmyk[3] floatValue];
void (^cmyTransform)(CGFloat *);
cmyTransform = ^(CGFloat *x){
*x = *x * (1 - K) + K;
};
cmyTransform(&C);
cmyTransform(&M);
cmyTransform(&Y);
// Translate CMY to RGB
CGFloat R = 1 - C;
CGFloat G = 1 - M;
CGFloat B = 1 - Y;
// return the Color
return [[self class] colorFromRGBAArray:@[@(R),
@(G),
@(B),
@(1)]];
}
+ (instancetype)colorFromCMYKDictionary:(NSDictionary *)cmyk
{
if (!cmyk) {
return [[self class] clearColor];
}
if (cmyk[kColoursCMYK_C] && cmyk[kColoursCMYK_M] && cmyk[kColoursCMYK_Y] && cmyk[kColoursCMYK_K]) {
return [[self class] colorFromCMYKArray:@[cmyk[kColoursCMYK_C],
cmyk[kColoursCMYK_M],
cmyk[kColoursCMYK_Y],
cmyk[kColoursCMYK_K]]];
}
return [[self class] clearColor];
}
#pragma mark - Color Components
- (NSDictionary *)colorComponents
{
NSMutableDictionary *components = [[self rgbaDictionary] mutableCopy];
[components addEntriesFromDictionary:[self hsbaDictionary]];
[components addEntriesFromDictionary:[self CIE_LabDictionary]];
return components;
}
- (CGFloat)red
{
return [[self rgbaArray][0] floatValue];
}
- (CGFloat)green
{
return [[self rgbaArray][1] floatValue];
}
- (CGFloat)blue
{
return [[self rgbaArray][2] floatValue];
}
- (CGFloat)hue
{
return [[self hsbaArray][0] floatValue];
}
- (CGFloat)saturation
{
return [[self hsbaArray][1] floatValue];
}
- (CGFloat)brightness
{
return [[self hsbaArray][2] floatValue];
}
- (CGFloat)alpha
{
return [[self rgbaArray][3] floatValue];
}
- (CGFloat)CIE_Lightness
{
return [[self CIE_LabArray][0] floatValue];
}
- (CGFloat)CIE_a
{
return [[self CIE_LabArray][1] floatValue];
}
- (CGFloat)CIE_b
{
return [[self CIE_LabArray][2] floatValue];
}
- (CGFloat)cyan {
return [[self cmykArray][0] floatValue];
}
- (CGFloat)magenta {
return [[self cmykArray][1] floatValue];
}
- (CGFloat)yellow {
return [[self cmykArray][2] floatValue];
}
- (CGFloat)keyBlack {
return [[self cmykArray][3] floatValue];
}
#pragma mark - Generate Color Scheme
- (NSArray *)colorSchemeOfType:(ColorScheme)type
{
NSArray *hsbArray = [self hsbaArray];
float hue = [hsbArray[0] floatValue] * 360;
float sat = [hsbArray[1] floatValue] * 100;
float bright = [hsbArray[2] floatValue] * 100;
float alpha = [hsbArray[3] floatValue];
switch (type) {
case ColorSchemeAnalagous:
return [[self class] analagousColorsFromHue:hue saturation:sat brightness:bright alpha:alpha];
case ColorSchemeMonochromatic:
return [[self class] monochromaticColorsFromHue:hue saturation:sat brightness:bright alpha:alpha];
case ColorSchemeTriad:
return [[self class] triadColorsFromHue:hue saturation:sat brightness:bright alpha:alpha];
case ColorSchemeComplementary:
return [[self class] complementaryColorsFromHue:hue saturation:sat brightness:bright alpha:alpha];
default:
return nil;
}
}
#pragma mark - Color Scheme Generation - Helper methods
+ (NSArray *)analagousColorsFromHue:(float)h saturation:(float)s brightness:(float)b alpha:(float)a
{
return @[[[self class] colorWithHue:[[self class] addDegrees:30 toDegree:h]/360 saturation:(s-5)/100 brightness:(b-10)/100 alpha:a],
[[self class] colorWithHue:[[self class] addDegrees:15 toDegree:h]/360 saturation:(s-5)/100 brightness:(b-5)/100 alpha:a],
[[self class] colorWithHue:[[self class] addDegrees:-15 toDegree:h]/360 saturation:(s-5)/100 brightness:(b-5)/100 alpha:a],
[[self class] colorWithHue:[[self class] addDegrees:-30 toDegree:h]/360 saturation:(s-5)/100 brightness:(b-10)/100 alpha:a]];
}
+ (NSArray *)monochromaticColorsFromHue:(float)h saturation:(float)s brightness:(float)b alpha:(float)a
{
return @[[[self class] colorWithHue:h/360 saturation:(s/2)/100 brightness:(b/3)/100 alpha:a],
[[self class] colorWithHue:h/360 saturation:s/100 brightness:(b/2)/100 alpha:a],
[[self class] colorWithHue:h/360 saturation:(s/3)/100 brightness:(2*b/3)/100 alpha:a],
[[self class] colorWithHue:h/360 saturation:s/100 brightness:(4*b/5)/100 alpha:a]];
}
+ (NSArray *)triadColorsFromHue:(float)h saturation:(float)s brightness:(float)b alpha:(float)a
{
return @[[[self class] colorWithHue:[[self class] addDegrees:120 toDegree:h]/360 saturation:(7*s/6)/100 brightness:(b-5)/100 alpha:a],
[[self class] colorWithHue:[[self class] addDegrees:120 toDegree:h]/360 saturation:s/100 brightness:b/100 alpha:a],
[[self class] colorWithHue:[[self class] addDegrees:240 toDegree:h]/360 saturation:s/100 brightness:b/100 alpha:a],
[[self class] colorWithHue:[[self class] addDegrees:240 toDegree:h]/360 saturation:(7*s/6)/100 brightness:(b-5)/100 alpha:a]];
}
+ (NSArray *)complementaryColorsFromHue:(float)h saturation:(float)s brightness:(float)b alpha:(float)a
{
return @[[[self class] colorWithHue:h/360 saturation:s/100 brightness:(4*b/5)/100 alpha:a],
[[self class] colorWithHue:h/360 saturation:(5*s/7)/100 brightness:b/100 alpha:a],
[[self class] colorWithHue:[[self class] addDegrees:180 toDegree:h]/360 saturation:s/100 brightness:b/100 alpha:a],
[[self class] colorWithHue:[[self class] addDegrees:180 toDegree:h]/360 saturation:(5*s/7)/100 brightness:b/100 alpha:a]];
}
#pragma mark - Contrasting Color
- (instancetype)blackOrWhiteContrastingColor
{
NSArray *rgbaArray = [self rgbaArray];
double a = 1 - ((0.299 * [rgbaArray[0] doubleValue]) + (0.587 * [rgbaArray[1] doubleValue]) + (0.114 * [rgbaArray[2] doubleValue]));
return a < 0.5 ? [[self class] blackColor] : [[self class] whiteColor];
}
#pragma mark - Complementary Color
- (instancetype)complementaryColor
{
NSMutableDictionary *hsba = [[self hsbaDictionary] mutableCopy];
float newH = [[self class] addDegrees:180.0f toDegree:([hsba[kColoursHSBA_H] floatValue]*360)];
[hsba setObject:@(newH) forKey:kColoursHSBA_H];
return [[self class] colorFromHSBADictionary:hsba];
}
#pragma mark - Distance between Colors
- (CGFloat)distanceFromColor:(id)color
{
// Defaults to CIE94
return [self distanceFromColor:color type:ColorDistanceCIE94];
}
- (CGFloat)distanceFromColor:(id)color type:(ColorDistance)distanceType {
/**
*
* Detecting a difference in two colors is not as trivial as it sounds.
* One's first instinct is to go for a difference in RGB values, leaving
* you with a sum of the differences of each point. It looks great! Until
* you actually start comparing colors. Why do these two reds have a different
* distance than these two blues *in real life* vs computationally?
* Human visual perception is next in the line of things between a color
* and your brain. Some colors are just perceived to have larger variants inside
* of their respective areas than others, so we need a way to model this
* human variable to colors. Enter CIELAB. This color formulation is supposed to be
* this model. So now we need to standardize a unit of distance between any two
* colors that works independent of how humans visually perceive that distance.
* Enter CIE76,94,2000. These are methods that use user-tested data and other
* mathematically and statistically significant correlations to output this info.
* You can read the wiki articles below to get a better understanding historically
* of how we moved to newer and better color distance formulas, and what
* their respective pros/cons are.
*
* References:
*
* http://en.wikipedia.org/wiki/Color_difference
* http://en.wikipedia.org/wiki/Just_noticeable_difference
* http://en.wikipedia.org/wiki/CIELAB
*
*/
// Check if it's a color
if (![color isKindOfClass:[self class]]) {
// NSLog(@"Not a %@ object.", NSStringFromClass([self class]));
return MAXFLOAT;
}
// Set Up Common Variables
NSArray *lab1 = [self CIE_LabArray];
NSArray *lab2 = [color CIE_LabArray];
CGFloat L1 = [lab1[0] floatValue];
CGFloat A1 = [lab1[1] floatValue];
CGFloat B1 = [lab1[2] floatValue];
CGFloat L2 = [lab2[0] floatValue];
CGFloat A2 = [lab2[1] floatValue];
CGFloat B2 = [lab2[2] floatValue];
// CIE76 first
if (distanceType == ColorDistanceCIE76) {
CGFloat distance = sqrtf(pow((L1-L2), 2) + pow((A1-A2), 2) + pow((B1-B2), 2));
return distance;
}
// More Common Variables
CGFloat kL = 1;
CGFloat kC = 1;
CGFloat kH = 1;
CGFloat k1 = 0.045;
CGFloat k2 = 0.015;
CGFloat deltaL = L1 - L2;
CGFloat C1 = sqrt((A1*A1) + (B1*B1));
CGFloat C2 = sqrt((A2*A2) + (B2*B2));
CGFloat deltaC = C1 - C2;
CGFloat deltaH = sqrt(pow((A1-A2), 2.0) + pow((B1-B2), 2.0) - pow(deltaC, 2.0));
CGFloat sL = 1;
CGFloat sC = 1 + k1*(sqrt((A1*A1) + (B1*B1)));
CGFloat sH = 1 + k2*(sqrt((A1*A1) + (B1*B1)));
// CIE94
if (distanceType == ColorDistanceCIE94) {
return sqrt(pow((deltaL/(kL*sL)), 2.0) + pow((deltaC/(kC*sC)), 2.0) + pow((deltaH/(kH*sH)), 2.0));
}
// CIE2000
// More variables
CGFloat deltaLPrime = L2 - L1;
CGFloat meanL = (L1 + L2)/2;
CGFloat meanC = (C1 + C2)/2;
CGFloat aPrime1 = A1 + A1/2*(1 - sqrt(pow(meanC, 7.0)/(pow(meanC, 7.0) + pow(25.0, 7.0))));
CGFloat aPrime2 = A2 + A2/2*(1 - sqrt(pow(meanC, 7.0)/(pow(meanC, 7.0) + pow(25.0, 7.0))));
CGFloat cPrime1 = sqrt((aPrime1*aPrime1) + (B1*B1));
CGFloat cPrime2 = sqrt((aPrime2*aPrime2) + (B2*B2));
CGFloat cMeanPrime = (cPrime1 + cPrime2)/2;
CGFloat deltaCPrime = cPrime1 - cPrime2;
CGFloat hPrime1 = atan2(B1, aPrime1);
CGFloat hPrime2 = atan2(B2, aPrime2);
hPrime1 = fmodf(hPrime1, [self radiansFromDegree:360]);
hPrime2 = fmodf(hPrime2, [self radiansFromDegree:360]);
CGFloat deltahPrime = 0;
if (fabsf(hPrime1 - hPrime2) <= [self radiansFromDegree:180]) {
deltahPrime = hPrime2 - hPrime1;
}
else {
deltahPrime = (hPrime2 <= hPrime1) ? hPrime2 - hPrime1 + [self radiansFromDegree:360] : hPrime2 - hPrime1 - [self radiansFromDegree:360];
}
CGFloat deltaHPrime = 2 * sqrt(cPrime1*cPrime2) * sin(deltahPrime/2);
CGFloat meanHPrime = (fabsf(hPrime1 - hPrime2) <= [self radiansFromDegree:180]) ? (hPrime1 + hPrime2)/2 : (hPrime1 + hPrime2 + [self radiansFromDegree:360])/2;
CGFloat T = 1 - 0.17*cos(meanHPrime - [self radiansFromDegree:30]) + 0.24*cos(2*meanHPrime)+0.32*cos(3*meanHPrime + [self radiansFromDegree:6]) - 0.20*cos(4*meanHPrime - [self radiansFromDegree:63]);
sL = 1 + (0.015 * pow((meanL - 50), 2))/sqrt(20 + pow((meanL - 50), 2));
sC = 1 + 0.045*cMeanPrime;
sH = 1 + 0.015*cMeanPrime*T;
CGFloat Rt = -2 * sqrt(pow(cMeanPrime, 7)/(pow(cMeanPrime, 7) + pow(25.0, 7))) * sin([self radiansFromDegree:60]* exp(-1 * pow((meanHPrime - [self radiansFromDegree:275])/[self radiansFromDegree:25], 2)));
// Finally return CIE2000 distance
return sqrt(pow((deltaLPrime/(kL*sL)), 2) + pow((deltaCPrime/(kC*sC)), 2) + pow((deltaHPrime/(kH*sH)), 2) + Rt*(deltaC/(kC*sC))*(deltaHPrime/(kH*sH)));
}
#pragma mark - System Colors
+ (instancetype)infoBlueColor
{
return [[self class] colorWithR:47 G:112 B:225 A:1.0];
}
+ (instancetype)successColor
{
return [[self class] colorWithR:83 G:215 B:106 A:1.0];
}
+ (instancetype)warningColor
{
return [[self class] colorWithR:221 G:170 B:59 A:1.0];
}
+ (instancetype)dangerColor
{
return [[self class] colorWithR:229 G:0 B:15 A:1.0];
}
#pragma mark - Whites
+ (instancetype)antiqueWhiteColor
{
return [[self class] colorWithR:250 G:235 B:215 A:1.0];
}
+ (instancetype)oldLaceColor
{
return [[self class] colorWithR:253 G:245 B:230 A:1.0];
}
+ (instancetype)ivoryColor
{
return [[self class] colorWithR:255 G:255 B:240 A:1.0];
}
+ (instancetype)seashellColor
{
return [[self class] colorWithR:255 G:245 B:238 A:1.0];
}
+ (instancetype)ghostWhiteColor
{
return [[self class] colorWithR:248 G:248 B:255 A:1.0];
}
+ (instancetype)snowColor
{
return [[self class] colorWithR:255 G:250 B:250 A:1.0];
}
+ (instancetype)linenColor
{
return [[self class] colorWithR:250 G:240 B:230 A:1.0];
}
#pragma mark - Grays
+ (instancetype)black25PercentColor
{
return [[self class] colorWithWhite:0.25 alpha:1.0];
}
+ (instancetype)black50PercentColor
{
return [[self class] colorWithWhite:0.5 alpha:1.0];
}
+ (instancetype)black75PercentColor
{
return [[self class] colorWithWhite:0.75 alpha:1.0];
}
+ (instancetype)warmGrayColor
{
return [[self class] colorWithR:133 G:117 B:112 A:1.0];
}
+ (instancetype)coolGrayColor
{
return [[self class] colorWithR:118 G:122 B:133 A:1.0];
}
+ (instancetype)charcoalColor
{
return [[self class] colorWithR:34 G:34 B:34 A:1.0];
}
#pragma mark - Blues
+ (instancetype)tealColor
{
return [[self class] colorWithR:28 G:160 B:170 A:1.0];
}
+ (instancetype)steelBlueColor
{
return [[self class] colorWithR:103 G:153 B:170 A:1.0];
}
+ (instancetype)robinEggColor
{
return [[self class] colorWithR:141 G:218 B:247 A:1.0];
}
+ (instancetype)pastelBlueColor
{
return [[self class] colorWithR:99 G:161 B:247 A:1.0];
}
+ (instancetype)turquoiseColor
{
return [[self class] colorWithR:112 G:219 B:219 A:1.0];
}
+ (instancetype)skyBlueColor
{
return [[self class] colorWithR:0 G:178 B:238 A:1.0];
}
+ (instancetype)indigoColor
{
return [[self class] colorWithR:13 G:79 B:139 A:1.0];
}
+ (instancetype)denimColor
{
return [[self class] colorWithR:67 G:114 B:170 A:1.0];
}
+ (instancetype)blueberryColor
{
return [[self class] colorWithR:89 G:113 B:173 A:1.0];
}
+ (instancetype)cornflowerColor
{
return [[self class] colorWithR:100 G:149 B:237 A:1.0];
}
+ (instancetype)babyBlueColor
{
return [[self class] colorWithR:190 G:220 B:230 A:1.0];
}
+ (instancetype)midnightBlueColor
{
return [[self class] colorWithR:13 G:26 B:35 A:1.0];
}
+ (instancetype)fadedBlueColor
{
return [[self class] colorWithR:23 G:137 B:155 A:1.0];
}
+ (instancetype)icebergColor
{
return [[self class] colorWithR:200 G:213 B:219 A:1.0];
}
+ (instancetype)waveColor
{
return [[self class] colorWithR:102 G:169 B:251 A:1.0];
}
#pragma mark - Greens
+ (instancetype)emeraldColor
{
return [[self class] colorWithR:1 G:152 B:117 A:1.0];
}
+ (instancetype)grassColor
{
return [[self class] colorWithR:99 G:214 B:74 A:1.0];
}
+ (instancetype)pastelGreenColor
{
return [[self class] colorWithR:126 G:242 B:124 A:1.0];
}
+ (instancetype)seafoamColor
{
return [[self class] colorWithR:77 G:226 B:140 A:1.0];
}
+ (instancetype)paleGreenColor
{
return [[self class] colorWithR:176 G:226 B:172 A:1.0];
}
+ (instancetype)cactusGreenColor
{
return [[self class] colorWithR:99 G:111 B:87 A:1.0];
}
+ (instancetype)chartreuseColor
{
return [[self class] colorWithR:69 G:139 B:0 A:1.0];
}
+ (instancetype)hollyGreenColor
{
return [[self class] colorWithR:32 G:87 B:14 A:1.0];
}
+ (instancetype)oliveColor
{
return [[self class] colorWithR:91 G:114 B:34 A:1.0];
}
+ (instancetype)oliveDrabColor
{
return [[self class] colorWithR:107 G:142 B:35 A:1.0];
}
+ (instancetype)moneyGreenColor
{
return [[self class] colorWithR:134 G:198 B:124 A:1.0];
}
+ (instancetype)honeydewColor
{
return [[self class] colorWithR:216 G:255 B:231 A:1.0];
}
+ (instancetype)limeColor
{
return [[self class] colorWithR:56 G:237 B:56 A:1.0];
}
+ (instancetype)cardTableColor
{
return [[self class] colorWithR:87 G:121 B:107 A:1.0];
}
#pragma mark - Reds
+ (instancetype)salmonColor
{
return [[self class] colorWithR:233 G:87 B:95 A:1.0];
}
+ (instancetype)brickRedColor
{
return [[self class] colorWithR:151 G:27 B:16 A:1.0];
}
+ (instancetype)easterPinkColor
{
return [[self class] colorWithR:241 G:167 B:162 A:1.0];
}
+ (instancetype)grapefruitColor
{
return [[self class] colorWithR:228 G:31 B:54 A:1.0];
}
+ (instancetype)pinkColor
{
return [[self class] colorWithR:255 G:95 B:154 A:1.0];
}
+ (instancetype)indianRedColor
{
return [[self class] colorWithR:205 G:92 B:92 A:1.0];
}
+ (instancetype)strawberryColor
{
return [[self class] colorWithR:190 G:38 B:37 A:1.0];
}
+ (instancetype)coralColor
{
return [[self class] colorWithR:240 G:128 B:128 A:1.0];
}
+ (instancetype)maroonColor
{
return [[self class] colorWithR:80 G:4 B:28 A:1.0];
}
+ (instancetype)watermelonColor
{
return [[self class] colorWithR:242 G:71 B:63 A:1.0];
}
+ (instancetype)tomatoColor
{
return [[self class] colorWithR:255 G:99 B:71 A:1.0];
}
+ (instancetype)pinkLipstickColor
{
return [[self class] colorWithR:255 G:105 B:180 A:1.0];
}
+ (instancetype)paleRoseColor
{
return [[self class] colorWithR:255 G:228 B:225 A:1.0];
}
+ (instancetype)crimsonColor
{
return [[self class] colorWithR:187 G:18 B:36 A:1.0];
}
#pragma mark - Purples
+ (instancetype)eggplantColor
{
return [[self class] colorWithR:105 G:5 B:98 A:1.0];
}
+ (instancetype)pastelPurpleColor
{
return [[self class] colorWithR:207 G:100 B:235 A:1.0];
}
+ (instancetype)palePurpleColor
{
return [[self class] colorWithR:229 G:180 B:235 A:1.0];
}
+ (instancetype)coolPurpleColor
{
return [[self class] colorWithR:140 G:93 B:228 A:1.0];
}
+ (instancetype)violetColor
{
return [[self class] colorWithR:191 G:95 B:255 A:1.0];
}
+ (instancetype)plumColor
{
return [[self class] colorWithR:139 G:102 B:139 A:1.0];
}
+ (instancetype)lavenderColor
{
return [[self class] colorWithR:204 G:153 B:204 A:1.0];
}
+ (instancetype)raspberryColor
{
return [[self class] colorWithR:135 G:38 B:87 A:1.0];
}
+ (instancetype)fuschiaColor
{
return [[self class] colorWithR:255 G:20 B:147 A:1.0];
}
+ (instancetype)grapeColor
{
return [[self class] colorWithR:54 G:11 B:88 A:1.0];
}
+ (instancetype)periwinkleColor
{
return [[self class] colorWithR:135 G:159 B:237 A:1.0];
}
+ (instancetype)orchidColor
{
return [[self class] colorWithR:218 G:112 B:214 A:1.0];
}
#pragma mark - Yellows
+ (instancetype)goldenrodColor
{
return [[self class] colorWithR:215 G:170 B:51 A:1.0];
}
+ (instancetype)yellowGreenColor
{
return [[self class] colorWithR:192 G:242 B:39 A:1.0];
}
+ (instancetype)bananaColor
{
return [[self class] colorWithR:229 G:227 B:58 A:1.0];
}
+ (instancetype)mustardColor
{
return [[self class] colorWithR:205 G:171 B:45 A:1.0];
}
+ (instancetype)buttermilkColor
{
return [[self class] colorWithR:254 G:241 B:181 A:1.0];
}
+ (instancetype)goldColor
{
return [[self class] colorWithR:139 G:117 B:18 A:1.0];
}
+ (instancetype)creamColor
{
return [[self class] colorWithR:240 G:226 B:187 A:1.0];
}
+ (instancetype)lightCreamColor
{
return [[self class] colorWithR:240 G:238 B:215 A:1.0];
}
+ (instancetype)wheatColor
{
return [[self class] colorWithR:240 G:238 B:215 A:1.0];
}
+ (instancetype)beigeColor
{
return [[self class] colorWithR:245 G:245 B:220 A:1.0];
}
#pragma mark - Oranges
+ (instancetype)peachColor
{
return [[self class] colorWithR:242 G:187 B:97 A:1.0];
}
+ (instancetype)burntOrangeColor
{
return [[self class] colorWithR:184 G:102 B:37 A:1.0];
}
+ (instancetype)pastelOrangeColor
{
return [[self class] colorWithR:248 G:197 B:143 A:1.0];
}
+ (instancetype)cantaloupeColor
{
return [[self class] colorWithR:250 G:154 B:79 A:1.0];
}
+ (instancetype)carrotColor
{
return [[self class] colorWithR:237 G:145 B:33 A:1.0];
}
+ (instancetype)mandarinColor
{
return [[self class] colorWithR:247 G:145 B:55 A:1.0];
}
#pragma mark - Browns
+ (instancetype)chiliPowderColor
{
return [[self class] colorWithR:199 G:63 B:23 A:1.0];
}
+ (instancetype)burntSiennaColor
{
return [[self class] colorWithR:138 G:54 B:15 A:1.0];
}
+ (instancetype)chocolateColor
{
return [[self class] colorWithR:94 G:38 B:5 A:1.0];
}
+ (instancetype)coffeeColor
{
return [[self class] colorWithR:141 G:60 B:15 A:1.0];
}
+ (instancetype)cinnamonColor
{
return [[self class] colorWithR:123 G:63 B:9 A:1.0];
}
+ (instancetype)almondColor
{
return [[self class] colorWithR:196 G:142 B:72 A:1.0];
}
+ (instancetype)eggshellColor
{
return [[self class] colorWithR:252 G:230 B:201 A:1.0];
}
+ (instancetype)sandColor
{
return [[self class] colorWithR:222 G:182 B:151 A:1.0];
}
+ (instancetype)mudColor
{
return [[self class] colorWithR:70 G:45 B:29 A:1.0];
}
+ (instancetype)siennaColor
{
return [[self class] colorWithR:160 G:82 B:45 A:1.0];
}
+ (instancetype)dustColor
{
return [[self class] colorWithR:236 G:214 B:197 A:1.0];
}
#pragma mark - Private
#pragma mark - RGBA Helper method
+ (instancetype)colorWithR:(CGFloat)red G:(CGFloat)green B:(CGFloat)blue A:(CGFloat)alpha
{
return [[self class] colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha];
}
#pragma mark - Degrees Helper method for Color Schemes
+ (float)addDegrees:(float)addDeg toDegree:(float)staticDeg
{
staticDeg += addDeg;
if (staticDeg > 360) {
float offset = staticDeg - 360;
return offset;
}
else if (staticDeg < 0) {
return -1 * staticDeg;
}
else {
return staticDeg;
}
}
- (CGFloat)radiansFromDegree:(CGFloat)degree {
return degree * M_PI/180;
}
#pragma mark - Swizzle
#pragma mark - On Load - Flip methods
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL rgbaSelector = @selector(getRed:green:blue:alpha:);
SEL swizzledRGBASelector = @selector(colours_getRed:green:blue:alpha:);
SEL hsbaSelector = @selector(getHue:saturation:brightness:alpha:);
SEL swizzledHSBASelector = @selector(colours_getHue:saturation:brightness:alpha:);
Method rgbaMethod = class_getInstanceMethod(class, rgbaSelector);
Method swizzledRGBAMethod = class_getInstanceMethod(class, swizzledRGBASelector);
Method hsbaMethod = class_getInstanceMethod(class, hsbaSelector);
Method swizzledHSBAMethod = class_getInstanceMethod(class, swizzledHSBASelector);
// Attempt adding the methods
BOOL didAddRGBAMethod =
class_addMethod(class,
rgbaSelector,
method_getImplementation(swizzledRGBAMethod),
method_getTypeEncoding(swizzledRGBAMethod));
BOOL didAddHSBAMethod =
class_addMethod(class,
hsbaSelector,
method_getImplementation(swizzledHSBAMethod),
method_getTypeEncoding(swizzledHSBAMethod));
// Replace methods
if (didAddRGBAMethod) {
class_replaceMethod(class,
swizzledRGBASelector,
method_getImplementation(swizzledRGBAMethod),
method_getTypeEncoding(swizzledRGBAMethod));
} else {
method_exchangeImplementations(rgbaMethod, swizzledRGBAMethod);
}
if (didAddHSBAMethod) {
class_replaceMethod(class,
swizzledHSBASelector,
method_getImplementation(swizzledHSBAMethod),
method_getTypeEncoding(swizzledHSBAMethod));
} else {
method_exchangeImplementations(hsbaMethod, swizzledHSBAMethod);
}
});
}
#pragma mark - Swizzled Methods
- (BOOL)colours_getRed:(CGFloat *)red green:(CGFloat *)green blue:(CGFloat *)blue alpha:(CGFloat *)alpha
{
if (CGColorGetNumberOfComponents(self.CGColor) == 4) {
return [self colours_getRed:red green:green blue:blue alpha:alpha];
}
else if (CGColorGetNumberOfComponents(self.CGColor) == 2) {
CGFloat white;
CGFloat m_alpha;
if ([self getWhite:&white alpha:&m_alpha]) {
*red = white * 1.0;
*green = white * 1.0;
*blue = white * 1.0;
*alpha = m_alpha;
return YES;
}
}
return NO;
}
- (BOOL)colours_getHue:(CGFloat *)hue saturation:(CGFloat *)saturation brightness:(CGFloat *)brightness alpha:(CGFloat *)alpha
{
if (CGColorGetNumberOfComponents(self.CGColor) == 4) {
return [self colours_getHue:hue saturation:saturation brightness:brightness alpha:alpha];
}
else if (CGColorGetNumberOfComponents(self.CGColor) == 2) {
CGFloat white = 0;
CGFloat a = 0;
if ([self getWhite:&white alpha:&a]) {
*hue = 0;
*saturation = 0;
*brightness = white * 1.0;
*alpha = a * 1.0;
return YES;
}
}
return NO;
}
@end