Get RGB value from UIColor presets
Asked Answered
S

5

19

I my application I pass RGB color value to server. My app uses UIColor predefined values, like [UIColor grayColor], [UIColor redColor]. I know that I can use following code:

const CGFloat *c = CGColorGetComponents(color.CGColor)

but only for colors that are in RBG color space, however, [UIColor grayColor] is not.

Is there any way to get RGB values for non-RBG colors?

Thanks!

Slat answered 15/1, 2011 at 14:44 Comment(0)
H
44

UIColor has a method which gives you the RGB components (-getRed:green:blue:alpha:) which works great on iOS 7 or higher. On iOS 6 and earlier, this method will fail and return NO if the color is not in an RGB color space (as it will for [UIColor grayColor].)

For iOS 6 and earlier, the only way I know of for doing this that works in all color spaces is to create a Core Graphics bitmap context in an RGB color space and draw into it with your color. You can then read out the RGB values from the resulting bitmap. Note that this won't work for certain colors, like pattern colors (eg. [UIColor groupTableViewBackgroundColor]), which don't have reasonable RGB values.

- (void)getRGBComponents:(CGFloat [3])components forColor:(UIColor *)color {
    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char resultingPixel[4];
    CGContextRef context = CGBitmapContextCreate(&resultingPixel,
                                                 1,
                                                 1,
                                                 8,
                                                 4,
                                                 rgbColorSpace,
                                                 kCGImageAlphaNoneSkipLast);
    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, CGRectMake(0, 0, 1, 1));
    CGContextRelease(context);
    CGColorSpaceRelease(rgbColorSpace);

    for (int component = 0; component < 3; component++) {
        components[component] = resultingPixel[component] / 255.0f;
    }
}

You can use it something like this:

    CGFloat components[3];
    [self getRGBComponents:components forColor:[UIColor grayColor]];
    NSLog(@"%f %f %f", components[0], components[1], components[2]);
Hypostasis answered 15/1, 2011 at 15:6 Comment(8)
Thanks. I decided that my problem doesn't worth this solution, and I switched to [UIColor colotWithRGB] stuff, I got RBG values from foobarpig.com/iphone/….Slat
Note: In iOS 5, UIColor's -getRed:green:blue:alpha only works for colors using RGB colorspace. It is broken for grayscale colors. Have they fixed this in iOS 6?Uremia
@damian — I agree, and I am dumbfounded as to why the answer keeps getting marked higher when it is wrong in general and only correct for RGB colors. If you invoke it a color object that had been obtained by a call to UIColor.blackColor or UIColor.whiteColor (which in many cases you will have no way of knowing), it fails miserably and is very very hard to diagnose. The code is wrong, as you point out. (No offense intended to Jesse.)Uremia
Oh, I didn't realise it also actually didn't work. I just thought it was a horrendously ridiculous way to convert colour values!Donnadonnamarie
@ToddLehman No offense taken; which part of the answer doesn't work? I believe the getRGBComponents:forColor: hack or the Apple-provided UIColor method?Hypostasis
This is from memory (maybe I can run a test later to confirm again), but if you call -getRed:getGreen:getBlue:getAlpha: on UIColor.whiteColor, then on iOS 5 (and later as well, unless they've fixed it?), it will return RGBA=(1,1,0,0) instead of RGBA=(1,1,1,1), because the implementation doesn't check that there are actually 4 color planes, so it accidentally puts that alpha value in the green slot instead of the alpha slot. Similar for black and for any pure gray value created from a grayscale color space.Uremia
@ToddLehman I've updated my answer based on your feedback; sorry for the delay, and thanks for your help.Hypostasis
It appears that starting with iOS7 (I've tested it on iOS7 and iOS8), the getRed:green:blue:alpha: method will work with UIDeviceWhiteColorSpace. But not so with iOS6 and earlier.Zymometer
U
7

I only know of one correct way to do this — and that is to call CGGetColorComponents(). I have tried using UIColor's -getRed:green:blue:alpha, but it does not work correctly on grayscale colors. So here is what I do:

First, I have a simple struct that lets me manipulate RGBA colors easily. This structure is useful elsewhere for doing all sorts of color manipulation stuff like gamma correction and color averaging.

typedef struct
{
  CGFloat r;  // Red component (0 <= r <= 1)
  CGFloat g;  // Green component (0 <= g <= 1)
  CGFloat b;  // Blue component (0 <= b <= 1)
  CGFloat a;  // Alpha/opacity component (0 <= a <= 1)
}
RGBA;

Then, I have a function that returns an RGBA structure given a UIColor.

RGBA RGBAFromUIColor(UIColor *color)
{
  return RGBAFromCGColor(color.CGColor);
}

It passes the UIColor's CGColor to a lower-level function to do the heavy lifting.

RGBA RGBAFromCGColor(CGColorRef color)
{
  RGBA rgba;

  CGColorSpaceRef color_space = CGColorGetColorSpace(color);
  CGColorSpaceModel color_space_model = CGColorSpaceGetModel(color_space);
  const CGFloat *color_components = CGColorGetComponents(color);
  int color_component_count = CGColorGetNumberOfComponents(color);

  switch (color_space_model)
  {
    case kCGColorSpaceModelMonochrome:
    {
      assert(color_component_count == 2);
      rgba = (RGBA)
      {
        .r = color_components[0],
        .g = color_components[0],
        .b = color_components[0],
        .a = color_components[1]
      };
      break;
    }

    case kCGColorSpaceModelRGB:
    {
      assert(color_component_count == 4);
      rgba = (RGBA)
      {
        .r = color_components[0],
        .g = color_components[1],
        .b = color_components[2],
        .a = color_components[3]
      };
      break;
    }

    default:
    {
      NSLog(@"Unsupported color space model %i", color_space_model);
      rgba = (RGBA) { 0, 0, 0, 0 };
      break;
    }
  }

  return rgba;
}

So, this works great for grayscale colors (like UIColor.whiteColor, UIColor.blackColor, UIColor.grayColor, etc.) as well as any red/green/blue/alpha color. It won't work with HSB colors, however.

Uremia answered 19/5, 2013 at 9:14 Comment(6)
The original poster explicitly asked for non-RGB colors, so excluding them is clearly not a solution.Quinquennial
@Quinquennial — Why did you mark this down?? I answered the question. He wants RGB values from non-RGB colors (specifically monochrome), which is precisely what I gave. If you pass the function an RGB color, you get RGB values. If you pass it a gray value (e.g., monochrome), you still get RGB values. Read the answer closer and you'll see that both are handled properly. This is a snippet of production code that I use, so I know it works. Also, as I pointed out in a comment, your answer gives incorrect results. I didn't mark you down for that, though...would you like me to?Uremia
I guess the mark-down would be because of the way you're identifying the color space model. 3- or 4- component colors might be in LAB or HSB color spaces for example.Donnadonnamarie
@Donnadonnamarie — Thank you for pointing that out. I updated the code to switch on the color space model type rather than on the number of color components. I imagine there is some way to convert a color from LAB or HSB into RGB values, but I will leave that as an exercise for the reader, as the original question seemed to be geared toward wanting a solution to the problem for grayscale values.Uremia
There are many more color spaces than the two supported in this answer. Implementing conversion for every case seems tedious and error prone.Blazon
@NikolaiRuhe — Yes, but it is the only correct way to do it that I am aware of. The answer posted by Jesse is correct only for RGB colors. If you call Jesse's code on a color object that happens to have been the result of a call to [UIColor blackColor] or [UIColor whiteColor] it fails miserably.Uremia
Q
3

Adding to Jesse's answer, here is how to preserve alpha:

- (void)getRGBAComponents:(CGFloat [4])components forColor:(UIColor *)color {
    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char resultingPixel[4] = {0};
    CGContextRef context = CGBitmapContextCreate(&resultingPixel,
                                                 1,
                                                 1,
                                                 8,
                                                 4,
                                                 rgbColorSpace,
                                                 kCGImageAlphaPremultipliedLast);
    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, CGRectMake(0, 0, 1, 1));
    CGContextRelease(context);
    CGColorSpaceRelease(rgbColorSpace);

    CGFloat a = resultingPixel[3] / 255.0;
    CGFloat unpremultiply = (a != 0.0) ? 1.0 / a / 255.0 : 0.0;
    for (int component = 0; component < 3; component++) {
        components[component] = resultingPixel[component] * unpremultiply;
    }
    components[3] = a;
}
Quinquennial answered 24/1, 2013 at 17:47 Comment(3)
As you do not initialize resultingPixel to 0 the bitmap context contains garbage values before drawing. So this would only return correct values when the color has an alpha component of 1.Blazon
Also, you should special-case for alpha == 0. Otherwise the method would return infinite for the color components.Blazon
A clever hack! But (1) it's an awful lot of overhead when CGColorGetComponents actually works fine (see my answer about how to make that work), and (2) this clever hack actually gives incorrect results when the original color has an alpha value other than 0 or 1, because the premultiply conversion is lossy. For example, (0.8,0,0,0.1) becomes (20,0,0,26) when premultiplied. When you undo this, it results in the incorrect value (0.76923,0,0,0.10196).Uremia
D
1

Crossposted from my answer to 'How to convert colors from one color space to another?'.

To identify different color spaces you can get the CGColorSpaceModel from the color's CGColorSpaceRef:

UIColor* color = some color;
CGColorSpaceRef colorSpace = CGColorGetColorSpace([color CGColor]);
CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace);

Then you can compare colorSpaceModel with the constants defined in CoreGraphics/CGColorSpace.h. UIColor's getRed:green:blue:alpha works for kCGColorSpaceModelRGB, whereas getWhite:alpha works for kCGColorSpaceModelMonochrome.

Donnadonnamarie answered 7/6, 2013 at 10:30 Comment(0)
J
0

you can Get RGB value simply by using this

UIColor *chooseColor = view.backgroundColor;
CGFloat red,green,blue,alpha;

[chooseColor getRed:&red green:&green blue:&blue alpha:&alpha]; 

thanks :-)

Janik answered 13/6, 2014 at 10:35 Comment(1)
UIColor getRed:green:blue:alpha: suffers from the same problem CGColorGetComponents does in that it will only succeed if the color is in the RGB color space (at least on some devices/OSes).Representational

© 2022 - 2024 — McMap. All rights reserved.