Advertisement
Guest User

UIColor-Expanded.m

a guest
Dec 25th, 2012
364
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #import "UIColor-Expanded.h"
  2.  
  3. /*
  4.  
  5.  Thanks to Poltras, Millenomi, Eridius, Nownot, WhatAHam, jberry,
  6.  and everyone else who helped out but whose name is inadvertantly omitted
  7.  
  8. */
  9.  
  10. /*
  11.  Current outstanding request list:
  12.  
  13.  - PolarBearFarm - color descriptions ([UIColor warmGrayWithHintOfBlueTouchOfRedAndSplashOfYellowColor])
  14.  - Eridius - UIColor needs a method that takes 2 colors and gives a third complementary one
  15.  - Consider UIMutableColor that can be adjusted (brighter, cooler, warmer, thicker-alpha, etc)
  16.  */
  17.  
  18. /*
  19.  FOR REFERENCE: Color Space Models: enum CGColorSpaceModel {
  20.     kCGColorSpaceModelUnknown = -1,
  21.     kCGColorSpaceModelMonochrome,
  22.     kCGColorSpaceModelRGB,
  23.     kCGColorSpaceModelCMYK,
  24.     kCGColorSpaceModelLab,
  25.     kCGColorSpaceModelDeviceN,
  26.     kCGColorSpaceModelIndexed,
  27.     kCGColorSpaceModelPattern
  28. };
  29. */
  30.  
  31. static const char *colorNameDB;
  32. static const char *crayolaNameDB;
  33.  
  34. // Complete dictionary of color name -> color mappings, generated
  35. // by a call to +namedColors or +colorWithName:
  36. static NSDictionary *colorNameCache = nil;
  37. static NSDictionary *crayolaNameCache = nil;
  38. static NSLock *colorNameCacheLock;
  39. static NSLock *crayolaNameCacheLock;
  40.  
  41.  
  42. @implementation UIColor (BGSCMYKAdditions)
  43.  
  44. - (NSDictionary *)cmykValues
  45. {
  46.  NSMutableDictionary *values = [[NSMutableDictionary alloc] init];
  47.  CGColorRef ref = [self CGColor];
  48.  if (CGColorGetNumberOfComponents(ref) == 4)
  49.  {
  50.   const CGFloat *parts = CGColorGetComponents(ref);
  51.   CGFloat r = parts[0];
  52.   CGFloat g = parts[1];
  53.   CGFloat b = parts[2];
  54.  
  55.   /*
  56.    Black   = minimum(1-Red,1-Green,1-Blue)
  57.    Cyan    = (1-Red-Black)/(1-Black)
  58.    Magenta = (1-Green-Black)/(1-Black)
  59.    Yellow  = (1-Blue-Black)/(1-Black)
  60.    */
  61.   CGFloat k = MIN(1-r,MIN(1-g,1-b));
  62.   CGFloat c = (1.0f-r-k)/(1.0f-k);
  63.   CGFloat m = (1.0f-g-k)/(1.0f-k);
  64.   CGFloat y = (1.0f-b-k)/(1.0f-k);
  65.  
  66.   [values setObject:[NSNumber numberWithFloat:c] forKey:@"c"];
  67.   [values setObject:[NSNumber numberWithFloat:m] forKey:@"m"];
  68.   [values setObject:[NSNumber numberWithFloat:y] forKey:@"y"];
  69.   [values setObject:[NSNumber numberWithFloat:k] forKey:@"k"];
  70.  }
  71.  return values;
  72. }
  73.  
  74.  
  75. @end
  76.  
  77.  
  78. #if SUPPORTS_UNDOCUMENTED_API
  79. // UIColor_Undocumented
  80. // Undocumented methods of UIColor
  81. @interface UIColor (UIColor_Undocumented)
  82. - (NSString *)styleString;
  83. @end
  84. #endif // SUPPORTS_UNDOCUMENTED_API
  85.  
  86. @interface UIColor (UIColor_Expanded_Support)
  87. + (void)populateColorNameCache;
  88. + (void)populateCrayolaNameCache;
  89. @end
  90.  
  91. #pragma mark -
  92.  
  93. @implementation UIColor (UIColor_Expanded)
  94.  
  95. - (CGColorSpaceModel)colorSpaceModel {
  96.     return CGColorSpaceGetModel(CGColorGetColorSpace(self.CGColor));
  97. }
  98.  
  99. - (NSString *)colorSpaceString {
  100.     switch (self.colorSpaceModel) {
  101.         case kCGColorSpaceModelUnknown:
  102.             return @"kCGColorSpaceModelUnknown";
  103.         case kCGColorSpaceModelMonochrome:
  104.             return @"kCGColorSpaceModelMonochrome";
  105.         case kCGColorSpaceModelRGB:
  106.             return @"kCGColorSpaceModelRGB";
  107.         case kCGColorSpaceModelCMYK:
  108.             return @"kCGColorSpaceModelCMYK";
  109.         case kCGColorSpaceModelLab:
  110.             return @"kCGColorSpaceModelLab";
  111.         case kCGColorSpaceModelDeviceN:
  112.             return @"kCGColorSpaceModelDeviceN";
  113.         case kCGColorSpaceModelIndexed:
  114.             return @"kCGColorSpaceModelIndexed";
  115.         case kCGColorSpaceModelPattern:
  116.             return @"kCGColorSpaceModelPattern";
  117.         default:
  118.             return @"Not a valid color space";
  119.     }
  120. }
  121.  
  122. - (BOOL)canProvideRGBComponents {
  123.     switch (self.colorSpaceModel) {
  124.         case kCGColorSpaceModelRGB:
  125.         case kCGColorSpaceModelMonochrome:
  126.             return YES;
  127.         default:
  128.             return NO;
  129.     }
  130. }
  131.  
  132. - (NSArray *)arrayFromRGBAComponents {
  133.     NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -arrayFromRGBAComponents");
  134.  
  135.     CGFloat r,g,b,a;
  136.     if (![self red:&r green:&g blue:&b alpha:&a]) return nil;
  137.    
  138.     return [NSArray arrayWithObjects:
  139.             [NSNumber numberWithFloat:r],
  140.             [NSNumber numberWithFloat:g],
  141.             [NSNumber numberWithFloat:b],
  142.             [NSNumber numberWithFloat:a],
  143.             nil];
  144. }
  145.  
  146. - (BOOL)red:(CGFloat *)red green:(CGFloat *)green blue:(CGFloat *)blue alpha:(CGFloat *)alpha {
  147.     const CGFloat *components = CGColorGetComponents(self.CGColor);
  148.    
  149.     CGFloat r,g,b,a;
  150.    
  151.     switch (self.colorSpaceModel) {
  152.         case kCGColorSpaceModelMonochrome:
  153.             r = g = b = components[0];
  154.             a = components[1];
  155.             break;
  156.         case kCGColorSpaceModelRGB:
  157.             r = components[0];
  158.             g = components[1];
  159.             b = components[2];
  160.             a = components[3];
  161.             break;
  162.         default:    // We don't know how to handle this model
  163.             return NO;
  164.     }
  165.    
  166.     if (red) *red = r;
  167.     if (green) *green = g;
  168.     if (blue) *blue = b;
  169.     if (alpha) *alpha = a;
  170.    
  171.     return YES;
  172. }
  173.  
  174. - (BOOL)hue:(CGFloat *)hue saturation:(CGFloat *)saturation brightness:(CGFloat *)brightness alpha:(CGFloat *)alpha {
  175.    
  176.     CGFloat r,g,b,a;
  177.     if (![self red:&r green:&g blue:&b alpha:&a]) return NO;
  178.    
  179.     [UIColor red:r green:g blue:b toHue:hue saturation:saturation brightness:brightness];
  180.    
  181.     if (alpha) *alpha = a;
  182.     return YES;
  183. }
  184.  
  185. - (CGFloat)red {
  186.     NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -red");
  187.     const CGFloat *c = CGColorGetComponents(self.CGColor);
  188.     return c[0];
  189. }
  190.  
  191. - (CGFloat)green {
  192.     NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -green");
  193.     const CGFloat *c = CGColorGetComponents(self.CGColor);
  194.     if (self.colorSpaceModel == kCGColorSpaceModelMonochrome) return c[0];
  195.     return c[1];
  196. }
  197.  
  198. - (CGFloat)blue {
  199.     NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -blue");
  200.     const CGFloat *c = CGColorGetComponents(self.CGColor);
  201.     if (self.colorSpaceModel == kCGColorSpaceModelMonochrome) return c[0];
  202.     return c[2];
  203. }
  204.  
  205. - (CGFloat)white {
  206.     NSAssert(self.colorSpaceModel == kCGColorSpaceModelMonochrome, @"Must be a Monochrome color to use -white");
  207.     const CGFloat *c = CGColorGetComponents(self.CGColor);
  208.     return c[0];
  209. }
  210.  
  211. - (CGFloat)hue {
  212.     NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -hue");
  213.     CGFloat h = 0.0f;
  214.     [self hue:&h saturation:nil brightness:nil alpha:nil];
  215.     return h;
  216. }
  217.  
  218. - (CGFloat)saturation {
  219.     NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -saturation");
  220.     CGFloat s = 0.0f;
  221.     [self hue:nil saturation:&s brightness:nil alpha:nil];
  222.     return s;
  223. }
  224.  
  225. - (CGFloat)brightness {
  226.     NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -brightness");
  227.     CGFloat v = 0.0f;
  228.     [self hue:nil saturation:nil brightness:&v alpha:nil];
  229.     return v;
  230. }
  231.  
  232. - (CGFloat)alpha {
  233.     return CGColorGetAlpha(self.CGColor);
  234. }
  235.  
  236. - (CGFloat)luminance {
  237.     NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use luminance");
  238.  
  239.     CGFloat r,g,b;
  240.     if (![self red:&r green:&g blue:&b alpha:nil]) return 0.0f;
  241.    
  242.     // http://en.wikipedia.org/wiki/Luma_(video)
  243.     // Y = 0.2126 R + 0.7152 G + 0.0722 B
  244.    
  245.     return r*0.2126f + g*0.7152f + b*0.0722f;
  246. }
  247.  
  248. - (UInt32)rgbHex {
  249.     NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use rgbHex");
  250.    
  251.     CGFloat r,g,b,a;
  252.     if (![self red:&r green:&g blue:&b alpha:&a]) return 0;
  253.    
  254.     r = MIN(MAX(r, 0.0f), 1.0f);
  255.     g = MIN(MAX(g, 0.0f), 1.0f);
  256.     b = MIN(MAX(b, 0.0f), 1.0f);
  257.    
  258.     return (((int)roundf(r * 255)) << 16)
  259.          | (((int)roundf(g * 255)) << 8)
  260.          | (((int)roundf(b * 255)));
  261. }
  262.  
  263. #pragma mark Arithmetic operations
  264.  
  265. - (UIColor *)colorByLuminanceMapping {
  266.     return [UIColor colorWithWhite:self.luminance alpha:1.0f];
  267. }
  268.  
  269. - (UIColor *)colorByMultiplyingByRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha {
  270.     NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations");
  271.  
  272.     CGFloat r,g,b,a;
  273.     if (![self red:&r green:&g blue:&b alpha:&a]) return nil;
  274.        
  275.     return [UIColor colorWithRed:MAX(0.0, MIN(1.0, r * red))
  276.                            green:MAX(0.0, MIN(1.0, g * green))
  277.                             blue:MAX(0.0, MIN(1.0, b * blue))
  278.                            alpha:MAX(0.0, MIN(1.0, a * alpha))];
  279. }
  280.  
  281. - (UIColor *)colorByAddingRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha {
  282.     NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations");
  283.    
  284.     CGFloat r,g,b,a;
  285.     if (![self red:&r green:&g blue:&b alpha:&a]) return nil;
  286.    
  287.     return [UIColor colorWithRed:MAX(0.0, MIN(1.0, r + red))
  288.                            green:MAX(0.0, MIN(1.0, g + green))
  289.                             blue:MAX(0.0, MIN(1.0, b + blue))
  290.                            alpha:MAX(0.0, MIN(1.0, a + alpha))];
  291. }
  292.  
  293. - (UIColor *)colorByLighteningToRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha {
  294.     NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations");
  295.    
  296.     CGFloat r,g,b,a;
  297.     if (![self red:&r green:&g blue:&b alpha:&a]) return nil;
  298.        
  299.     return [UIColor colorWithRed:MAX(r, red)
  300.                            green:MAX(g, green)
  301.                             blue:MAX(b, blue)
  302.                            alpha:MAX(a, alpha)];
  303. }
  304.  
  305. - (UIColor *)colorByDarkeningToRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha {
  306.     NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations");
  307.    
  308.     CGFloat r,g,b,a;
  309.     if (![self red:&r green:&g blue:&b alpha:&a]) return nil;
  310.    
  311.     return [UIColor colorWithRed:MIN(r, red)
  312.                            green:MIN(g, green)
  313.                             blue:MIN(b, blue)
  314.                            alpha:MIN(a, alpha)];
  315. }
  316.  
  317. - (UIColor *)colorByMultiplyingBy:(CGFloat)f {
  318.     return [self colorByMultiplyingByRed:f green:f blue:f alpha:1.0f];
  319. }
  320.  
  321. - (UIColor *)colorByAdding:(CGFloat)f {
  322.     return [self colorByMultiplyingByRed:f green:f blue:f alpha:0.0f];
  323. }
  324.  
  325. - (UIColor *)colorByLighteningTo:(CGFloat)f {
  326.     return [self colorByLighteningToRed:f green:f blue:f alpha:0.0f];
  327. }
  328.  
  329. - (UIColor *)colorByDarkeningTo:(CGFloat)f {
  330.     return [self colorByDarkeningToRed:f green:f blue:f alpha:1.0f];
  331. }
  332.  
  333. - (UIColor *)colorByMultiplyingByColor:(UIColor *)color {
  334.     NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations");
  335.    
  336.     CGFloat r,g,b,a;
  337.     if (![self red:&r green:&g blue:&b alpha:&a]) return nil;
  338.    
  339.     return [self colorByMultiplyingByRed:r green:g blue:b alpha:1.0f];
  340. }
  341.  
  342. - (UIColor *)colorByAddingColor:(UIColor *)color {
  343.     NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations");
  344.    
  345.     CGFloat r,g,b,a;
  346.     if (![self red:&r green:&g blue:&b alpha:&a]) return nil;
  347.    
  348.     return [self colorByAddingRed:r green:g blue:b alpha:0.0f];
  349. }
  350.  
  351. - (UIColor *)colorByLighteningToColor:(UIColor *)color {
  352.     NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations");
  353.    
  354.     CGFloat r,g,b,a;
  355.     if (![self red:&r green:&g blue:&b alpha:&a]) return nil;
  356.        
  357.     return [self colorByLighteningToRed:r green:g blue:b alpha:0.0f];
  358. }
  359.  
  360. - (UIColor *)colorByDarkeningToColor:(UIColor *)color {
  361.     NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations");
  362.    
  363.     CGFloat r,g,b,a;
  364.     if (![self red:&r green:&g blue:&b alpha:&a]) return nil;
  365.    
  366.     return [self colorByDarkeningToRed:r green:g blue:b alpha:1.0f];
  367. }
  368.  
  369. #pragma mark Complementary Colors, etc
  370.  
  371. // Pick a color that is likely to contrast well with this color
  372. - (UIColor *)contrastingColor {
  373.     return (self.luminance > 0.5f) ? [UIColor blackColor] : [UIColor whiteColor];
  374. }
  375.  
  376. // Pick the color that is 180 degrees away in hue
  377. - (UIColor *)complementaryColor {
  378.    
  379.     // Convert to HSB
  380.     CGFloat h,s,v,a;
  381.     if (![self hue:&h saturation:&s brightness:&v alpha:&a]) return nil;
  382.        
  383.     // Pick color 180 degrees away
  384.     h += 180.0f;
  385.     if (h > 360.f) h -= 360.0f;
  386.    
  387.     // Create a color in RGB
  388.     return [UIColor colorWithHue:h saturation:s brightness:v alpha:a];
  389. }
  390.  
  391. // Pick two colors more colors such that all three are equidistant on the color wheel
  392. // (120 degrees and 240 degress difference in hue from self)
  393. - (NSArray*)triadicColors {
  394.     return [self analogousColorsWithStepAngle:120.0f pairCount:1];
  395. }
  396.  
  397. // Pick n pairs of colors, stepping in increasing steps away from this color around the wheel
  398. - (NSArray*)analogousColorsWithStepAngle:(CGFloat)stepAngle pairCount:(int)pairs {
  399.     // Convert to HSB
  400.     CGFloat h,s,v,a;
  401.     if (![self hue:&h saturation:&s brightness:&v alpha:&a]) return nil;
  402.    
  403.     NSMutableArray* colors = [NSMutableArray arrayWithCapacity:pairs * 2];
  404.    
  405.     if (stepAngle < 0.0f)
  406.         stepAngle *= -1.0f;
  407.    
  408.     for (int i = 1; i <= pairs; ++i) {
  409.         CGFloat a = fmodf(stepAngle * i, 360.0f);
  410.        
  411.         CGFloat h1 = fmodf(h + a, 360.0f);
  412.         CGFloat h2 = fmodf(h + 360.0f - a, 360.0f);
  413.        
  414.         [colors addObject:[UIColor colorWithHue:h1 saturation:s brightness:v alpha:a]];
  415.         [colors addObject:[UIColor colorWithHue:h2 saturation:s brightness:v alpha:a]];
  416.     }
  417.    
  418.     return [colors copy];
  419. }
  420.  
  421. #pragma mark String utilities
  422.  
  423. - (NSString *)stringFromColor {
  424.     NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -stringFromColor");
  425.     NSString *result;
  426.     switch (self.colorSpaceModel) {
  427.         case kCGColorSpaceModelRGB:
  428.             result = [NSString stringWithFormat:@"{%0.3f, %0.3f, %0.3f, %0.3f}", self.red, self.green, self.blue, self.alpha];
  429.             break;
  430.         case kCGColorSpaceModelMonochrome:
  431.             result = [NSString stringWithFormat:@"{%0.3f, %0.3f}", self.white, self.alpha];
  432.             break;
  433.         default:
  434.             result = nil;
  435.     }
  436.     return result;
  437. }
  438.  
  439. - (NSString *)hexStringFromColor {
  440.     return [NSString stringWithFormat:@"%0.6lX", self.rgbHex];
  441. }
  442.  
  443. - (NSString *)closestColorNameFor: (const char *) aColorDatabase {
  444.     NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use closestColorName");
  445.    
  446.     int targetHex = self.rgbHex;
  447.     int rInt = (targetHex >> 16) & 0x0ff;
  448.     int gInt = (targetHex >> 8) & 0x0ff;
  449.     int bInt = (targetHex >> 0) & 0x0ff;
  450.    
  451.     float bestScore = MAXFLOAT;
  452.     const char* bestPos = nil;
  453.    
  454.     // Walk the name db string looking for the name with closest match
  455.     for (const char* p = aColorDatabase; (p = strchr(p, '#')); ++p) {
  456.         int r,g,b;
  457.         if (sscanf(p+1, "%2x%2x%2x", &r, &g, &b) == 3) {
  458.             // Calculate difference between this color and the target color
  459.             int rDiff = abs(rInt - r);
  460.             int gDiff = abs(gInt - g);
  461.             int bDiff = abs(bInt - b);
  462.             float score = logf(rDiff+1) + logf(gDiff+1) + logf(bDiff+1);
  463.            
  464.             // Track the best score/name seen
  465.             if (score < bestScore) {
  466.                 bestScore = score;
  467.                 bestPos = p;
  468.             }
  469.         }
  470.     }
  471.    
  472.     // bestPos now points to the # following the best name seen
  473.     // Backup to the start of the name and return it
  474.     const char* name;
  475.     for (name = bestPos-1; *name != ','; --name)
  476.         ;
  477.     ++name;
  478.     NSString *result = [[NSString alloc] initWithBytes:name length:bestPos - name encoding:NSUTF8StringEncoding];
  479.    
  480.     return result;
  481. }
  482.  
  483.  
  484. - (NSString *)closestColorName {
  485.     return [self closestColorNameFor:colorNameDB];
  486. }
  487.  
  488. - (NSString *)closestCrayonName {
  489.     return [self closestColorNameFor:crayolaNameDB];
  490. }
  491.  
  492. + (UIColor *)colorWithString:(NSString *)stringToConvert {
  493.     NSScanner *scanner = [NSScanner scannerWithString:stringToConvert];
  494.     if (![scanner scanString:@"{" intoString:NULL]) return nil;
  495.     const NSUInteger kMaxComponents = 4;
  496.     CGFloat c[kMaxComponents];
  497.     NSUInteger i = 0;
  498.     if (![scanner scanFloat:&c[i++]]) return nil;
  499.     while (1) {
  500.         if ([scanner scanString:@"}" intoString:NULL]) break;
  501.         if (i >= kMaxComponents) return nil;
  502.         if ([scanner scanString:@"," intoString:NULL]) {
  503.             if (![scanner scanFloat:&c[i++]]) return nil;
  504.         } else {
  505.             // either we're at the end of there's an unexpected character here
  506.             // both cases are error conditions
  507.             return nil;
  508.         }
  509.     }
  510.     if (![scanner isAtEnd]) return nil;
  511.     UIColor *color;
  512.     switch (i) {
  513.         case 2: // monochrome
  514.             color = [UIColor colorWithWhite:c[0] alpha:c[1]];
  515.             break;
  516.         case 4: // RGB
  517.             color = [UIColor colorWithRed:c[0] green:c[1] blue:c[2] alpha:c[3]];
  518.             break;
  519.         default:
  520.             color = nil;
  521.     }
  522.     return color;
  523. }
  524.  
  525. #pragma mark Class methods
  526.  
  527. + (UIColor *)randomColor {
  528.     return [UIColor colorWithRed:random() / (CGFloat)RAND_MAX
  529.                            green:random() / (CGFloat)RAND_MAX
  530.                             blue:random() / (CGFloat)RAND_MAX
  531.                            alpha:1.0f];
  532. }
  533.  
  534. + (UIColor *)colorWithRGBHex:(UInt32)hex {
  535.     int r = (hex >> 16) & 0xFF;
  536.     int g = (hex >> 8) & 0xFF;
  537.     int b = (hex) & 0xFF;
  538.    
  539.     return [UIColor colorWithRed:r / 255.0f
  540.                            green:g / 255.0f
  541.                             blue:b / 255.0f
  542.                            alpha:1.0f];
  543. }
  544.  
  545. // Returns a UIColor by scanning the string for a hex number and passing that to +[UIColor colorWithRGBHex:]
  546. // Skips any leading whitespace and ignores any trailing characters
  547. + (UIColor *)colorWithHexString:(NSString *)stringToConvert {
  548.     NSScanner *scanner = [NSScanner scannerWithString:stringToConvert];
  549.     unsigned hexNum;
  550.     if (![scanner scanHexInt:&hexNum]) return nil;
  551.     return [UIColor colorWithRGBHex:hexNum];
  552. }
  553.  
  554. // Lookup a color using css 3/svg color name
  555. + (UIColor *)colorWithName:(NSString *)cssColorName {
  556.     return [[UIColor namedColors] objectForKey:cssColorName];
  557. }
  558.  
  559. // Lookup a color using a crayola name
  560. + (UIColor *)crayonWithName:(NSString *)crayolaColorName {
  561.     return [[UIColor namedCrayons] objectForKey:crayolaColorName];
  562. }
  563.  
  564. // Return complete mapping of css3/svg color names --> colors
  565. + (NSDictionary *)namedColors {
  566.     [colorNameCacheLock lock];
  567.     if (colorNameCache == nil) [UIColor populateColorNameCache];
  568.     [colorNameCacheLock unlock];
  569.     return colorNameCache;
  570. }
  571.  
  572. + (NSDictionary *)namedCrayons {
  573.     [crayolaNameCacheLock lock];
  574.     if (crayolaNameCache == nil) [UIColor populateCrayolaNameCache];
  575.     [crayolaNameCacheLock unlock];
  576.     return crayolaNameCache;
  577. }
  578.  
  579. + (UIColor *)colorWithHue:(CGFloat)hue saturation:(CGFloat)saturation brightness:(CGFloat)brightness alpha:(CGFloat)alpha {
  580.     // Convert hsb to rgb
  581.     CGFloat r,g,b;
  582.     [self hue:hue saturation:saturation brightness:brightness toRed:&r green:&g blue:&b];
  583.    
  584.     // Create a color with rgb
  585.     return [self colorWithRed:r green:g blue:b alpha:alpha];
  586. }
  587.  
  588.  
  589. #pragma mark Color Space Conversions
  590.  
  591. + (void)hue:(CGFloat)h saturation:(CGFloat)s brightness:(CGFloat)v toRed:(CGFloat *)pR green:(CGFloat *)pG blue:(CGFloat *)pB {
  592.     CGFloat r,g,b;
  593.    
  594.     // From Foley and Van Dam
  595.    
  596.     if (s == 0.0f) {
  597.         // Achromatic color: there is no hue
  598.         r = g = b = v;
  599.     } else {
  600.         // Chromatic color: there is a hue
  601.         if (h == 360.0f) h = 0.0f;
  602.         h /= 60.0f;                                     // h is now in [0, 6)
  603.        
  604.         int i = floorf(h);                              // largest integer <= h
  605.         CGFloat f = h - i;                              // fractional part of h
  606.         CGFloat p = v * (1 - s);
  607.         CGFloat q = v * (1 - (s * f));
  608.         CGFloat t = v * (1 - (s * (1 - f)));
  609.        
  610.         switch (i) {
  611.             case 0: r = v; g = t; b = p;    break;
  612.             case 1: r = q; g = v; b = p;    break;
  613.             case 2: r = p; g = v; b = t;    break;
  614.             case 3: r = p; g = q; b = v;    break;
  615.             case 4: r = t; g = p; b = v;    break;
  616.             case 5: r = v; g = p; b = q;    break;
  617.         }
  618.     }
  619.    
  620.     if (pR) *pR = r;
  621.     if (pG) *pG = g;
  622.     if (pB) *pB = b;
  623. }
  624.  
  625.  
  626. + (void)red:(CGFloat)r green:(CGFloat)g blue:(CGFloat)b toHue:(CGFloat *)pH saturation:(CGFloat *)pS brightness:(CGFloat *)pV {
  627.     CGFloat h,s,v;
  628.    
  629.     // From Foley and Van Dam
  630.    
  631.     CGFloat max = MAX(r, MAX(g, b));
  632.     CGFloat min = MIN(r, MIN(g, b));
  633.    
  634.     // Brightness
  635.     v = max;
  636.    
  637.     // Saturation
  638.     s = (max != 0.0f) ? ((max - min) / max) : 0.0f;
  639.    
  640.     if (s == 0.0f) {
  641.         // No saturation, so undefined hue
  642.         h = 0.0f;
  643.     } else {
  644.         // Determine hue
  645.         CGFloat rc = (max - r) / (max - min);       // Distance of color from red
  646.         CGFloat gc = (max - g) / (max - min);       // Distance of color from green
  647.         CGFloat bc = (max - b) / (max - min);       // Distance of color from blue
  648.        
  649.         if (r == max) h = bc - gc;                  // resulting color between yellow and magenta
  650.         else if (g == max) h = 2 + rc - bc;         // resulting color between cyan and yellow
  651.         else /* if (b == max) */ h = 4 + gc - rc;   // resulting color between magenta and cyan
  652.        
  653.         h *= 60.0f;                                 // Convert to degrees
  654.         if (h < 0.0f) h += 360.0f;                  // Make non-negative
  655.     }
  656.    
  657.     if (pH) *pH = h;
  658.     if (pS) *pS = s;
  659.     if (pV) *pV = v;
  660. }
  661.  
  662.  
  663. #pragma mark UIColor_Expanded initialization
  664.  
  665. + (void)load {
  666.     colorNameCacheLock = [[NSLock alloc] init];
  667.     crayolaNameCacheLock = [[NSLock alloc] init];
  668. }
  669.  
  670. @end
  671.  
  672. #pragma mark -
  673.  
  674. #if SUPPORTS_UNDOCUMENTED_API
  675. @implementation UIColor (UIColor_Undocumented_Expanded)
  676. - (NSString *)fetchStyleString {
  677.     return [self styleString];
  678. }
  679.  
  680. // Convert a color into RGB Color space, courtesy of Poltras
  681. // via http://ofcodeandmen.poltras.com/2009/01/22/convert-a-cgcolorref-to-another-cgcolorspaceref/
  682. //
  683. - (UIColor *)rgbColor {
  684.     // Call to undocumented method "styleString".
  685.     NSString *style = [self styleString];
  686.     NSScanner *scanner = [NSScanner scannerWithString:style];
  687.     CGFloat red, green, blue;
  688.     if (![scanner scanString:@"rgb(" intoString:NULL]) return nil;
  689.     if (![scanner scanFloat:&red]) return nil;
  690.     if (![scanner scanString:@"," intoString:NULL]) return nil;
  691.     if (![scanner scanFloat:&green]) return nil;
  692.     if (![scanner scanString:@"," intoString:NULL]) return nil;
  693.     if (![scanner scanFloat:&blue]) return nil;
  694.     if (![scanner scanString:@")" intoString:NULL]) return nil;
  695.     if (![scanner isAtEnd]) return nil;
  696.    
  697.     return [UIColor colorWithRed:red green:green blue:blue alpha:self.alpha];
  698. }
  699. @end
  700. #endif // SUPPORTS_UNDOCUMENTED_API
  701.  
  702. @implementation UIColor (UIColor_Expanded_Support)
  703. /*
  704.  * Database of color names and hex rgb values, derived
  705.  * from the css 3 color spec:
  706.  *  http://www.w3.org/TR/css3-color/
  707.  *
  708.  * We think this is a very compact way of storing
  709.  * this information, and relatively cheap to lookup.
  710.  *
  711.  * Note that we search for color names starting with ','
  712.  * and terminated by '#', so that we don't get false matches.
  713.  * For this reason, the database begins with ','.
  714.  */
  715. static const char *colorNameDB = ","
  716.     "aliceblue#f0f8ff,antiquewhite#faebd7,aqua#00ffff,aquamarine#7fffd4,azure#f0ffff,"
  717.     "beige#f5f5dc,bisque#ffe4c4,black#000000,blanchedalmond#ffebcd,blue#0000ff,"
  718.     "blueviolet#8a2be2,brown#a52a2a,burlywood#deb887,cadetblue#5f9ea0,chartreuse#7fff00,"
  719.     "chocolate#d2691e,coral#ff7f50,cornflowerblue#6495ed,cornsilk#fff8dc,crimson#dc143c,"
  720.     "cyan#00ffff,darkblue#00008b,darkcyan#008b8b,darkgoldenrod#b8860b,darkgray#a9a9a9,"
  721.     "darkgreen#006400,darkgrey#a9a9a9,darkkhaki#bdb76b,darkmagenta#8b008b,"
  722.     "darkolivegreen#556b2f,darkorange#ff8c00,darkorchid#9932cc,darkred#8b0000,"
  723.     "darksalmon#e9967a,darkseagreen#8fbc8f,darkslateblue#483d8b,darkslategray#2f4f4f,"
  724.     "darkslategrey#2f4f4f,darkturquoise#00ced1,darkviolet#9400d3,deeppink#ff1493,"
  725.     "deepskyblue#00bfff,dimgray#696969,dimgrey#696969,dodgerblue#1e90ff,"
  726.     "firebrick#b22222,floralwhite#fffaf0,forestgreen#228b22,fuchsia#ff00ff,"
  727.     "gainsboro#dcdcdc,ghostwhite#f8f8ff,gold#ffd700,goldenrod#daa520,gray#808080,"
  728.     "green#008000,greenyellow#adff2f,grey#808080,honeydew#f0fff0,hotpink#ff69b4,"
  729.     "indianred#cd5c5c,indigo#4b0082,ivory#fffff0,khaki#f0e68c,lavender#e6e6fa,"
  730.     "lavenderblush#fff0f5,lawngreen#7cfc00,lemonchiffon#fffacd,lightblue#add8e6,"
  731.     "lightcoral#f08080,lightcyan#e0ffff,lightgoldenrodyellow#fafad2,lightgray#d3d3d3,"
  732.     "lightgreen#90ee90,lightgrey#d3d3d3,lightpink#ffb6c1,lightsalmon#ffa07a,"
  733.     "lightseagreen#20b2aa,lightskyblue#87cefa,lightslategray#778899,"
  734.     "lightslategrey#778899,lightsteelblue#b0c4de,lightyellow#ffffe0,lime#00ff00,"
  735.     "limegreen#32cd32,linen#faf0e6,magenta#ff00ff,maroon#800000,mediumaquamarine#66cdaa,"
  736.     "mediumblue#0000cd,mediumorchid#ba55d3,mediumpurple#9370db,mediumseagreen#3cb371,"
  737.     "mediumslateblue#7b68ee,mediumspringgreen#00fa9a,mediumturquoise#48d1cc,"
  738.     "mediumvioletred#c71585,midnightblue#191970,mintcream#f5fffa,mistyrose#ffe4e1,"
  739.     "moccasin#ffe4b5,navajowhite#ffdead,navy#000080,oldlace#fdf5e6,olive#808000,"
  740.     "olivedrab#6b8e23,orange#ffa500,orangered#ff4500,orchid#da70d6,palegoldenrod#eee8aa,"
  741.     "palegreen#98fb98,paleturquoise#afeeee,palevioletred#db7093,papayawhip#ffefd5,"
  742.     "peachpuff#ffdab9,peru#cd853f,pink#ffc0cb,plum#dda0dd,powderblue#b0e0e6,"
  743.     "purple#800080,red#ff0000,rosybrown#bc8f8f,royalblue#4169e1,saddlebrown#8b4513,"
  744.     "salmon#fa8072,sandybrown#f4a460,seagreen#2e8b57,seashell#fff5ee,sienna#a0522d,"
  745.     "silver#c0c0c0,skyblue#87ceeb,slateblue#6a5acd,slategray#708090,slategrey#708090,"
  746.     "snow#fffafa,springgreen#00ff7f,steelblue#4682b4,tan#d2b48c,teal#008080,"
  747.     "thistle#d8bfd8,tomato#ff6347,turquoise#40e0d0,violet#ee82ee,wheat#f5deb3,"
  748.     "white#ffffff,whitesmoke#f5f5f5,yellow#ffff00,yellowgreen#9acd32";
  749.  
  750. static const char *crayolaNameDB = ","
  751.     "Almond#EED9C4,Antique Brass#C88A65,Apricot#FDD5B1,Aquamarine#71D9E2,Asparagus#7BA05B,"
  752.     "Atomic Tangerine#FF9966,Banana Mania#FBE7B2,Beaver#926F5B,Bittersweet#FE6F5E,Black#000000,"
  753.     "Blizzard Blue#A3E3ED,Blue#0066FF,Blue Bell#9999CC,Blue Green#0095B6,Blue Violet#6456B7,"
  754.     "Brick Red#C62D42,Brink Pink#FB607F,Brown#AF593E,Burnt Orange#FF7034,Burnt Sienna#E97451,"
  755.     "Cadet Blue#A9B2C3,Canary#FFFF99,Caribbean Green#00CC99,Carnation Pink#FFA6C9,Cerise#DA3287,"
  756.     "Cerulean#02A4D3,Chartreuse#FF9966,Chestnut#B94E48,Copper#DA8A67,Cornflower#93CCEA,Cotton Candy#FFB7D5,"
  757.     "Cranberry#DB5079,Dandelion#FED85D,Denim#1560BD,Desert Sand#EDC9AF,Eggplant#614051,Electric Lime#CCFF00,"
  758.     "Fern#63B76C,Flesh#FFCBA4,Forest Green#5FA777,Fuchsia#C154C1,Fuzzy Wuzzy Brown#C45655,Gold#E6BE8A,Goldenrod#FCD667,"
  759.     "Granny Smith Apple#9DE093,Gray#8B8680,Green#01A368,Green Yellow#F1E788,Happy Ever After#6CDA37,Hot Magenta#FF00CC,"
  760.     "Inch Worm#B0E313,Indian Red#B94E48,Indigo#4F69C6,Jazzberry Jam#A50B5E,Jungle Green#29AB87,Laser Lemon#FFFF66,"
  761.     "Lavender#FBAED2,Macaroni And Cheese#FFB97B,Magenta#F653A6,Magic Mint#AAF0D1,Mahogany#CA3435,Manatee#8D90A1,"
  762.     "Mango Tango#E77200,Maroon#C32148,Mauvelous#F091A9,Melon#FEBAAD,Midnight Blue#003366,Mountain Meadow#1AB385,"
  763.     "Mulberry#C54B8C,Navy Blue#0066CC,Neon Carrot#FF9933,Olive Green#B5B35C,Orange#FF681F,Orchid#E29CD2,Outer Space#2D383A,"
  764.     "Outrageous Orange#FF6037,Pacific Blue#009DC4,Peach#FFCBA4,Periwinkle#C3CDE6,Pig Pink#FDD7E4,Pine Green#01796F,"
  765.     "Pink Flamingo#FF66FF,Plum#843179,Prussian Blue#003366,Purple Heart#652DC1,Purple Mountain's Majesty#9678B6,"
  766.     "Purple Pizzazz#FF00CC,Radical Red#FF355E,Raw Sienna#D27D46,Razzle Dazzle Rose#FF33CC,Razzmatazz#E30B5C,Red#ED0A3F,"
  767.     "Red Orange#FF3F34,Red Violet#BB3385,Robin's Egg Blue#00CCCC,Royal Purple#6B3FA0,Salmon#FF91A4,Scarlet#FD0E35,"
  768.     "Screamin' Green#66FF66,Sea Green#93DFB8,Sepia#9E5B40,Shadow#837050,Shamrock#33CC99,Shocking Pink#FF6FFF,Silver#C9C0BB,"
  769.     "Sky Blue#76D7EA,Spring Green#ECEBBD,Sunglow#FFCC33,Sunset Orange#FE4C40,Tan#FA9D5A,Tickle Me Pink#FC80A5,Timberwolf#D9D6CF,"
  770.     "Torch Red#FD0E35,Tropical Rain Forest#00755E,Tumbleweed#DEA681,Turquoise Blue#6CDAE7,Ultra Green#66FF66,Ultra Orange#FF6037,"
  771.     "Ultra Pink#FF6FFF,Ultra Red#FD5B78,Ultra Yellow#FFFF66,Unmellow Yellow#FFFF66,Violet (purple)#8359A3,Violet Red#F7468A,"
  772.     "Vivid Tangerine#FF9980,Vivid Violet#803790,White#FFFFFF,Wild Blue Yonder#7A89B8,Wild Strawberry#FF3399,Wild Watermelon#FD5B78,"
  773.     "Wisteria#C9A0DC,Yellow#FBE870,Yellow Green#C5E17A,Yellow Orange#FFAE42";
  774.  
  775.  
  776. + (void)populateColorNameCache {
  777.     NSAssert(colorNameCache == nil, @"+pouplateColorNameCache was called when colorNameCache was not nil");
  778.     NSMutableDictionary *cache = [NSMutableDictionary dictionary];
  779.     for (const char* entry = colorNameDB; (entry = strchr(entry, ',')); ) {
  780.        
  781.         // Step forward to the start of the name
  782.         ++entry;
  783.        
  784.         // Find the following hash
  785.         const char* h = strchr(entry, '#');
  786.         NSAssert(h, @"Malformed colorNameDB");
  787.        
  788.         // Get the name
  789.         NSString* name = [[NSString alloc] initWithBytes:entry length:h - entry encoding:NSUTF8StringEncoding];
  790.        
  791.         // Get the color, and add to the dictionary
  792.         int hex, increment;
  793.         if (sscanf(++h, "%x%n", &hex, &increment) != 1) { break;} // thanks Curtis Duhn
  794.         [cache setObject:[self colorWithRGBHex:hex] forKey:name];
  795.        
  796.         // Cleanup and move to the next item
  797.         entry = h + increment;
  798.     }
  799.     colorNameCache = [cache copy];
  800. }
  801.  
  802. + (void)populateCrayolaNameCache {
  803.     NSAssert(crayolaNameCache == nil, @"+pouplateCrayolaNameCache was called when crayolaNameCache was not nil");
  804.     NSMutableDictionary *cache = [NSMutableDictionary dictionary];
  805.     for (const char* entry = crayolaNameDB; (entry = strchr(entry, ',')); ) {
  806.        
  807.         // Step forward to the start of the name
  808.         ++entry;
  809.        
  810.         // Find the following hash
  811.         const char* h = strchr(entry, '#');
  812.         NSAssert(h, @"Malformed crayolaNameDB");
  813.        
  814.         // Get the name
  815.         NSString* name = [[NSString alloc] initWithBytes:entry length:h - entry encoding:NSUTF8StringEncoding];
  816.        
  817.         // Get the color, and add to the dictionary
  818.         int hex, increment;
  819.         if (sscanf(++h, "%x%n", &hex, &increment) != 1) { break;} // thanks Curtis Duhn
  820.         [cache setObject:[self colorWithRGBHex:hex] forKey:name];
  821.        
  822.         // Cleanup and move to the next item
  823.         entry = h + increment;
  824.     }
  825.     crayolaNameCache = [cache copy];
  826. }
  827. @end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement