Oolite 1.91.0.7604-240417-a536cbe
Loading...
Searching...
No Matches
OOColor.m
Go to the documentation of this file.
1/*
2
3OOColor.m
4
5Oolite
6Copyright (C) 2004-2013 Giles C Williams and contributors
7
8This program is free software; you can redistribute it and/or
9modify it under the terms of the GNU General Public License
10as published by the Free Software Foundation; either version 2
11of the License, or (at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21MA 02110-1301, USA.
22
23*/
24
25#import "OOColor.h"
27#import "OOMaths.h"
28
29
30@implementation OOColor
31
32// Set methods are internal, because OOColor is immutable (as seen from outside).
33- (void) setRed:(float)r green:(float)g blue:(float)b alpha:(float)a
34{
35 rgba[0] = r;
36 rgba[1] = g;
37 rgba[2] = b;
38 rgba[3] = a;
39}
40
41
42- (void) setHue:(float)h saturation:(float)s brightness:(float)b alpha:(float)a
43{
44 rgba[3] = a;
45 if (s == 0.0f)
46 {
47 rgba[0] = rgba[1] = rgba[2] = b;
48 return;
49 }
50 float f, p, q, t;
51 int i;
52 h = fmod(h, 360.0f);
53 if (h < 0.0) h += 360.0f;
54 h /= 60.0f;
55
56 i = floor(h);
57 f = h - i;
58 p = b * (1.0f - s);
59 q = b * (1.0f - (s * f));
60 t = b * (1.0f - (s * (1.0f - f)));
61
62 switch (i)
63 {
64 case 0:
65 rgba[0] = b; rgba[1] = t; rgba[2] = p; break;
66 case 1:
67 rgba[0] = q; rgba[1] = b; rgba[2] = p; break;
68 case 2:
69 rgba[0] = p; rgba[1] = b; rgba[2] = t; break;
70 case 3:
71 rgba[0] = p; rgba[1] = q; rgba[2] = b; break;
72 case 4:
73 rgba[0] = t; rgba[1] = p; rgba[2] = b; break;
74 case 5:
75 rgba[0] = b; rgba[1] = p; rgba[2] = q; break;
76 }
77}
78
79
80- (id) copyWithZone:(NSZone *)zone
81{
82 // Copy is implemented as retain since OOColor is immutable.
83 return [self retain];
84}
85
86
87+ (OOColor *) colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness alpha:(float)alpha
88{
89 OOColor* result = [[OOColor alloc] init];
90 [result setHue:360.0f * hue saturation:saturation brightness:brightness alpha:alpha];
91 return [result autorelease];
92}
93
94
95+ (OOColor *) colorWithRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
96{
97 OOColor* result = [[OOColor alloc] init];
98 [result setRed:red green:green blue:blue alpha:alpha];
99 return [result autorelease];
100}
101
102
103+ (OOColor *) colorWithWhite:(float)white alpha:(float)alpha
104{
105 return [OOColor colorWithRed:white green:white blue:white alpha:alpha];
106}
107
108
109+ (OOColor *) colorWithRGBAComponents:(OORGBAComponents)components
110{
111 return [self colorWithRed:components.r
112 green:components.g
113 blue:components.b
114 alpha:components.a];
115}
116
117
118+ (OOColor *) colorWithHSBAComponents:(OOHSBAComponents)components
119{
120 return [self colorWithHue:components.h / 360.0f
121 saturation:components.s
122 brightness:components.b
123 alpha:components.a];
124}
125
126
127+ (OOColor *) colorWithDescription:(id)description
128{
129 return [self colorWithDescription:description saturationFactor:1.0f];
130}
131
132
133+ (OOColor *) colorWithDescription:(id)description saturationFactor:(float)factor
134{
135 NSDictionary *dict = nil;
136 OOColor *result = nil;
137
138 if (description == nil) return nil;
139
140 if ([description isKindOfClass:[OOColor class]])
141 {
142 result = [[description copy] autorelease];
143 }
144 else if ([description isKindOfClass:[NSString class]])
145 {
146 if ([description hasSuffix:@"Color"])
147 {
148 // +fooColor selector
149 SEL selector = NSSelectorFromString(description);
150 if ([self respondsToSelector:selector]) result = [self performSelector:selector];
151 }
152 else
153 {
154 // Some other string
155 result = [self colorFromString:description];
156 }
157 }
158 else if ([description isKindOfClass:[NSArray class]])
159 {
160 result = [self colorFromString:[description componentsJoinedByString:@" "]];
161 }
162 else if ([description isKindOfClass:[NSDictionary class]])
163 {
164 dict = description; // Workaround for gnu-gcc's more agressive "multiple methods named..." warnings.
165
166 if ([dict objectForKey:@"hue"] != nil)
167 {
168 // Treat as HSB(A) dictionary
169 float h = [dict oo_floatForKey:@"hue"];
170 float s = [dict oo_floatForKey:@"saturation" defaultValue:1.0f];
171 float b = [dict oo_floatForKey:@"brightness" defaultValue:-1.0f];
172 if (b < 0.0f) b = [dict oo_floatForKey:@"value" defaultValue:1.0f];
173 float a = [dict oo_floatForKey:@"alpha" defaultValue:-1.0f];
174 if (a < 0.0f) a = [dict oo_floatForKey:@"opacity" defaultValue:1.0f];
175
176 // Not "result =", because we handle the saturation scaling here to allow oversaturation.
177 return [OOColor colorWithHue:h / 360.0f saturation:s * factor brightness:b alpha:a];
178 }
179 else
180 {
181 // Treat as RGB(A) dictionary
182 float r = [dict oo_floatForKey:@"red"];
183 float g = [dict oo_floatForKey:@"green"];
184 float b = [dict oo_floatForKey:@"blue"];
185 float a = [dict oo_floatForKey:@"alpha" defaultValue:-1.0f];
186 if (a < 0.0f) a = [dict oo_floatForKey:@"opacity" defaultValue:1.0f];
187
188 result = [OOColor colorWithRed:r green:g blue:b alpha:a];
189 }
190 }
191
192 if (factor != 1.0f && result != nil)
193 {
194 float h, s, b, a;
195 [result getHue:&h saturation:&s brightness:&b alpha:&a];
196 h *= 1.0 / 360.0f; // See note in header.
197 s *= factor;
198 result = [self colorWithHue:h saturation:s brightness:b alpha:a];
199 }
200
201 return result;
202}
203
204
205+ (OOColor *) brightColorWithDescription:(id)description
206{
207 OOColor *color = [OOColor colorWithDescription:description];
208 if (color == nil || 0.5f <= [color brightnessComponent]) return color;
209
210 return [OOColor colorWithHue:[color hueComponent] / 360.0f saturation:[color saturationComponent] brightness:0.5f alpha:1.0f];
211}
212
213
214+ (OOColor *) colorFromString:(NSString*) colorFloatString
215{
216 float rgbaValue[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
217 NSScanner *scanner = [NSScanner scannerWithString:colorFloatString];
218 float factor = 1.0f;
219 int i;
220
221 for (i = 0; i != 4; ++i)
222 {
223 if (![scanner scanFloat:&rgbaValue[i]])
224 {
225 // Less than three floats or non-float, can't parse -> quit
226 if (i < 3) return nil;
227
228 // If we get here, we only got three components. Make sure alpha is at correct scale:
229 rgbaValue[3] /= factor;
230 }
231 if (1.0f < rgbaValue[i]) factor = 1.0f / 255.0f;
232 }
233
234 return [OOColor colorWithRed:rgbaValue[0] * factor green:rgbaValue[1] * factor blue:rgbaValue[2] * factor alpha:rgbaValue[3] * factor];
235}
236
237
238+ (OOColor *) blackColor // 0.0 white
239{
240 return [OOColor colorWithWhite:0.0f alpha:1.0f];
241}
242
243
244+ (OOColor *) darkGrayColor // 0.333 white
245{
246 return [OOColor colorWithWhite:1.0f/3.0f alpha:1.0f];
247}
248
249
250+ (OOColor *) lightGrayColor // 0.667 white
251{
252 return [OOColor colorWithWhite:2.0f/3.0f alpha:1.0f];
253}
254
255
256+ (OOColor *) whiteColor // 1.0 white
257{
258 return [OOColor colorWithWhite:1.0f alpha:1.0f];
259}
260
261
262+ (OOColor *) grayColor // 0.5 white
263{
264 return [OOColor colorWithWhite:0.5f alpha:1.0f];
265}
266
267
268+ (OOColor *) redColor // 1.0, 0.0, 0.0 RGB
269{
270 return [OOColor colorWithRed:1.0f green:0.0f blue:0.0f alpha:1.0f];
271}
272
273
274+ (OOColor *) greenColor // 0.0, 1.0, 0.0 RGB
275{
276 return [OOColor colorWithRed:0.0f green:1.0f blue:0.0f alpha:1.0f];
277}
278
279
280+ (OOColor *) blueColor // 0.0, 0.0, 1.0 RGB
281{
282 return [OOColor colorWithRed:0.0f green:0.0f blue:1.0f alpha:1.0f];
283}
284
285
286+ (OOColor *) cyanColor // 0.0, 1.0, 1.0 RGB
287{
288 return [OOColor colorWithRed:0.0f green:1.0f blue:1.0f alpha:1.0f];
289}
290
291
292+ (OOColor *) yellowColor // 1.0, 1.0, 0.0 RGB
293{
294 return [OOColor colorWithRed:1.0f green:1.0f blue:0.0f alpha:1.0f];
295}
296
297
298+ (OOColor *) magentaColor // 1.0, 0.0, 1.0 RGB
299{
300 return [OOColor colorWithRed:1.0f green:0.0f blue:1.0f alpha:1.0f];
301}
302
303
304+ (OOColor *) orangeColor // 1.0, 0.5, 0.0 RGB
305{
306 return [OOColor colorWithRed:1.0f green:0.5f blue:0.0f alpha:1.0f];
307}
308
309
310+ (OOColor *) purpleColor // 0.5, 0.0, 0.5 RGB
311{
312 return [OOColor colorWithRed:0.5f green:0.0f blue:0.5f alpha:1.0f];
313}
314
315
316+ (OOColor *)brownColor // 0.6, 0.4, 0.2 RGB
317{
318 return [OOColor colorWithRed:0.6f green:0.4f blue:0.2f alpha:1.0f];
319}
320
321
322+ (OOColor *) clearColor // 0.0 white, 0.0 alpha
323{
324 return [OOColor colorWithWhite:0.0f alpha:0.0f];
325}
326
327
328- (OOColor *) blendedColorWithFraction:(float)fraction ofColor:(OOColor *)color
329{
330 float rgba1[4];
331 [color getRed:&rgba1[0] green:&rgba1[1] blue:&rgba1[2] alpha:&rgba1[3]];
332
333 OOColor *result = [[OOColor alloc] init];
334 [result setRed:OOLerp(rgba[0], rgba1[0], fraction)
335 green:OOLerp(rgba[1], rgba1[1], fraction)
336 blue:OOLerp(rgba[2], rgba1[2], fraction)
337 alpha:OOLerp(rgba[3], rgba1[3], fraction)];
338
339 return [result autorelease];
340}
341
342
343- (NSString *) descriptionComponents
344{
345 return [NSString stringWithFormat:@"%g, %g, %g, %g", rgba[0], rgba[1], rgba[2], rgba[3]];
346}
347
348
349// Get the red, green, or blue components.
350- (float) redComponent
351{
352 return rgba[0];
353}
354
355
356- (float) greenComponent
357{
358 return rgba[1];
359}
360
361
362- (float) blueComponent
363{
364 return rgba[2];
365}
366
367
368- (void) getRed:(float *)red green:(float *)green blue:(float *)blue alpha:(float *)alpha
369{
370 NSParameterAssert(red != NULL && green != NULL && blue != NULL && alpha != NULL);
371
372 *red = rgba[0];
373 *green = rgba[1];
374 *blue = rgba[2];
375 *alpha = rgba[3];
376}
377
378
380{
381 OORGBAComponents c = { rgba[0], rgba[1], rgba[2], rgba[3] };
382 return c;
383}
384
385
386- (BOOL) isBlack
387{
388 return rgba[0] == 0.0f && rgba[1] == 0.0f && rgba[2] == 0.0f;
389}
390
391
392- (BOOL) isWhite
393{
394 return rgba[0] == 1.0f && rgba[1] == 1.0f && rgba[2] == 1.0f && rgba[3] == 1.0f;
395}
396
397
398// Get the components as hue, saturation, or brightness.
399- (float) hueComponent
400{
401 float maxrgb = (rgba[0] > rgba[1])? ((rgba[0] > rgba[2])? rgba[0]:rgba[2]):((rgba[1] > rgba[2])? rgba[1]:rgba[2]);
402 float minrgb = (rgba[0] < rgba[1])? ((rgba[0] < rgba[2])? rgba[0]:rgba[2]):((rgba[1] < rgba[2])? rgba[1]:rgba[2]);
403 float delta = maxrgb - minrgb + 0.0001f;
404 float fRed = rgba[0], fGreen = rgba[1], fBlue = rgba[2];
405 float hue = 0.0f;
406 if (maxrgb == fRed && fGreen >= fBlue)
407 {
408 hue = 60.0f * (fGreen - fBlue) / delta;
409 }
410 else if (maxrgb == fRed && fGreen < fBlue)
411 {
412 hue = 60.0f * (fGreen - fBlue) / delta + 360.0f;
413 }
414 else if (maxrgb == fGreen)
415 {
416 hue = 60.0f * (fBlue - fRed) / delta + 120.0f;
417 }
418 else if (maxrgb == fBlue)
419 {
420 hue = 60.0f * (fRed - fGreen) / delta + 240.0f;
421 }
422 return hue;
423}
424
425- (float) saturationComponent
426{
427 float maxrgb = (rgba[0] > rgba[1])? ((rgba[0] > rgba[2])? rgba[0]:rgba[2]):((rgba[1] > rgba[2])? rgba[1]:rgba[2]);
428 float minrgb = (rgba[0] < rgba[1])? ((rgba[0] < rgba[2])? rgba[0]:rgba[2]):((rgba[1] < rgba[2])? rgba[1]:rgba[2]);
429 return maxrgb == 0.0f ? 0.0f : (1.0f - (minrgb / maxrgb));
430}
431
432- (float) brightnessComponent
433{
434 float maxrgb = (rgba[0] > rgba[1])? ((rgba[0] > rgba[2])? rgba[0]:rgba[2]):((rgba[1] > rgba[2])? rgba[1]:rgba[2]);
435 return maxrgb;
436}
437
438- (void) getHue:(float *)hue saturation:(float *)saturation brightness:(float *)brightness alpha:(float *)alpha
439{
440 NSParameterAssert(hue != NULL && saturation != NULL && brightness != NULL && alpha != NULL);
441
442 *alpha = rgba[3];
443
444 float fRed = rgba[0], fGreen = rgba[1], fBlue = rgba[2];
445 float maxrgb = fmax(fRed, fmax(fGreen, fBlue));
446 float minrgb = fmin(fRed, fmin(fGreen, fBlue));
447 float delta = maxrgb - minrgb + 0.0001f;
448 float h = 0.0f;
449 if (maxrgb == fRed && fGreen >= fBlue)
450 {
451 h = 60.0f * (fGreen - fBlue) / delta;
452 }
453 else if (maxrgb == fRed && fGreen < fBlue)
454 {
455 h = 60.0f * (fGreen - fBlue) / delta + 360.0f;
456 }
457 else if (maxrgb == fGreen)
458 {
459 h = 60.0f * (fBlue - fRed) / delta + 120.0f;
460 }
461 else if (maxrgb == fBlue)
462 {
463 h = 60.0f * (fRed - fGreen) / delta + 240.0f;
464 }
465
466 float s = (maxrgb == 0.0f) ? 0.0f : (1.0f - (minrgb / maxrgb));
467
468 *hue = h;
469 *saturation = s;
470 *brightness = maxrgb;
471}
472
473
475{
477 [self getHue:&c.h
478 saturation:&c.s
479 brightness:&c.b
480 alpha:&c.a];
481 return c;
482}
483
484
485// Get the alpha component.
486- (float) alphaComponent
487{
488 return rgba[3];
489}
490
491
493{
494 if (rgba[3] == 1.0f) return [[self retain] autorelease];
495 return [OOColor colorWithRed:rgba[0] * rgba[3]
496 green:rgba[1] * rgba[3]
497 blue:rgba[2] * rgba[3]
498 alpha:1.0f];
499}
500
501
502- (OOColor *) colorWithBrightnessFactor:(float)factor
503{
504 return [OOColor colorWithRed:OOClamp_0_1_f(rgba[0] * factor)
505 green:OOClamp_0_1_f(rgba[1] * factor)
506 blue:OOClamp_0_1_f(rgba[2] * factor)
507 alpha:rgba[3]];
508}
509
510
511- (NSArray *) normalizedArray
512{
513 float r, g, b, a;
514 [self getRed:&r green:&g blue:&b alpha:&a];
515 return [NSArray arrayWithObjects:
516 [NSNumber numberWithFloat:r],
517 [NSNumber numberWithFloat:g],
518 [NSNumber numberWithFloat:b],
519 [NSNumber numberWithFloat:a],
520 nil];
521}
522
523
524- (NSString *) rgbaDescription
525{
527}
528
529
530- (NSString *) hsbaDescription
531{
533}
534
535@end
536
537
539{
540 return [NSString stringWithFormat:@"{%.3g, %.3g, %.3g, %.3g}", components.r, components.g, components.b, components.a];
541}
542
543
545{
546 return [NSString stringWithFormat:@"{%i, %.3g, %.3g, %.3g}", (int)components.h, components.s, components.b, components.a];
547}
NSString * OORGBAComponentsDescription(OORGBAComponents components)
Definition OOColor.m:538
NSString * OOHSBAComponentsDescription(OOHSBAComponents components)
Definition OOColor.m:544
NSString * OORGBAComponentsDescription(OORGBAComponents components)
Definition OOColor.m:538
NSString * OOHSBAComponentsDescription(OOHSBAComponents components)
Definition OOColor.m:544
return self
return nil
void getHue:saturation:brightness:alpha:(float *hue,[saturation] float *saturation,[brightness] float *brightness,[alpha] float *alpha)
Definition OOColor.m:438
BOOL isBlack()
Definition OOColor.m:386
OOColor * cyanColor()
Definition OOColor.m:286
NSString * descriptionComponents()
Definition OOColor.m:343
OOColor * colorWithRed:green:blue:alpha:(float red,[green] float green,[blue] float blue,[alpha] float alpha)
Definition OOColor.m:95
OOColor * colorWithWhite:alpha:(float white,[alpha] float alpha)
Definition OOColor.m:103
void setHue:saturation:brightness:alpha:(float h, [saturation] float s, [brightness] float b, [alpha] float a)
Definition OOColor.m:42
float alphaComponent()
Definition OOColor.m:486
OOColor * darkGrayColor()
Definition OOColor.m:244
NSString * rgbaDescription()
Definition OOColor.m:524
float saturationComponent()
Definition OOColor.m:425
float blueComponent()
Definition OOColor.m:362
OOColor * orangeColor()
Definition OOColor.m:304
BOOL isWhite()
Definition OOColor.m:392
OOColor * colorWithDescription:(id description)
Definition OOColor.m:127
OOColor * redColor()
Definition OOColor.m:268
OOColor * greenColor()
Definition OOColor.m:274
void getRed:green:blue:alpha:(float *red,[green] float *green,[blue] float *blue,[alpha] float *alpha)
Definition OOColor.m:368
OOColor * blueColor()
Definition OOColor.m:280
OOColor * clearColor()
Definition OOColor.m:322
NSArray * normalizedArray()
Definition OOColor.m:511
float hueComponent()
Definition OOColor.m:399
OOColor * grayColor()
Definition OOColor.m:262
OOColor * whiteColor()
Definition OOColor.m:256
OOColor * colorWithHue:saturation:brightness:alpha:(float hue,[saturation] float saturation,[brightness] float brightness,[alpha] float alpha)
Definition OOColor.m:87
void setRed:green:blue:alpha:(float r, [green] float g, [blue] float b, [alpha] float a)
Definition OOColor.m:33
OOColor * brownColor()
Definition OOColor.m:316
float redComponent()
Definition OOColor.m:350
OOColor * premultipliedColor()
Definition OOColor.m:492
NSString * hsbaDescription()
Definition OOColor.m:530
OOHSBAComponents hsbaComponents()
Definition OOColor.m:474
OOColor * yellowColor()
Definition OOColor.m:292
OOColor * lightGrayColor()
Definition OOColor.m:250
OOColor * magentaColor()
Definition OOColor.m:298
OOColor * blackColor()
Definition OOColor.m:238
OORGBAComponents rgbaComponents()
Definition OOColor.m:379
float rgba[4]
Definition OOColor.h:48
float greenComponent()
Definition OOColor.m:356
OOColor * purpleColor()
Definition OOColor.m:310
float brightnessComponent()
Definition OOColor.m:432