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