LCOV - code coverage report
Current view: top level - Core - OOColor.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 0 7 0.0 %
Date: 2025-05-28 07:50:54 Functions: 0 0 -

          Line data    Source code
       1           0 : /*
       2             : 
       3             : OOColor.m
       4             : 
       5             : Oolite
       6             : Copyright (C) 2004-2013 Giles C Williams and contributors
       7             : 
       8             : This program is free software; you can redistribute it and/or
       9             : modify it under the terms of the GNU General Public License
      10             : as published by the Free Software Foundation; either version 2
      11             : of the License, or (at your option) any later version.
      12             : 
      13             : This program is distributed in the hope that it will be useful,
      14             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             : GNU General Public License for more details.
      17             : 
      18             : You should have received a copy of the GNU General Public License
      19             : along with this program; if not, write to the Free Software
      20             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
      21             : MA 02110-1301, USA.
      22             : 
      23             : */
      24             : 
      25             : #import "OOColor.h"
      26             : #import "OOCollectionExtractors.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           0 : - (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           0 : - (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           0 : - (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           0 : - (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             : 
     379             : - (OORGBAComponents) rgbaComponents
     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             : 
     474             : - (OOHSBAComponents) hsbaComponents
     475             : {
     476             :         OOHSBAComponents c;
     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             : 
     492             : - (OOColor *) premultipliedColor
     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             : {
     526             :         return OORGBAComponentsDescription([self rgbaComponents]);
     527             : }
     528             : 
     529             : 
     530             : - (NSString *) hsbaDescription
     531             : {
     532             :         return OOHSBAComponentsDescription([self hsbaComponents]);
     533             : }
     534             : 
     535             : @end
     536             : 
     537             : 
     538           0 : NSString *OORGBAComponentsDescription(OORGBAComponents components)
     539             : {
     540             :         return [NSString stringWithFormat:@"{%.3g, %.3g, %.3g, %.3g}", components.r, components.g, components.b, components.a];
     541             : }
     542             : 
     543             : 
     544           0 : NSString *OOHSBAComponentsDescription(OOHSBAComponents components)
     545             : {
     546             :         return [NSString stringWithFormat:@"{%i, %.3g, %.3g, %.3g}", (int)components.h, components.s, components.b, components.a];
     547             : }

Generated by: LCOV version 1.14