views:

285

answers:

4

I'm attempting to define an extremely simple utility method that will save me from having to use a calculator to define RGB values as percentages. When I look into Apple's sample code called "QuartzCache", in the DrawView.m file, line 96, I see this:

float whiteColor[4] = {1, 1, 1, 1};

However, when I attempt to created a method like the following, the compiler hates me. A half-hour of intensive Googling has not produced any help.

 +(float[])percentagesRGBArray:(float[])rgbArray{

        float red = rgbArray[0];
        float green = rgbArray[1];
        float blue = rgbArray[2];
        float alpha = rgbArray[3];
        red = red/255;
        green = green/255;
        blue = blue/255;
        alpha = alpha;
        float percentagesRGBArray[4] = {red, green, blue, alpha};


        return  percentagesRGBArray;
    }

What is the proper way to define such a method? What am I doing wrong here?

+3  A: 

Normally, you would use Cocoa's NSColor class to handle this sort of thing, but it looks like you are doing something a little more low-level.

In that case, I would do the following:

typedef struct
{
    float red;
    float green;
    float blue;
    float alpha;
}
RGBAData;

RGBAData ConvertRGBAToPercentages(const RGBAData source)
{
    RGBAData percentages;

    percentages.red = source.red/255;
    percentages.green = source.green/255;
    percentages.blue = source.blue/255;
    percentages.alpha = source.alpha/255;

    return percentages;
}

To be used as follows:

RGBAData original = { 0xFF, 0xFF, 0x00, 0x80 }; // 50% transparent yellow
RGBAData percents = ConvertRGBAToPercentages(original);
e.James
If you *can* make use of `NSColor`, I would suggest using a handy category to simplify the interaction. I have posted one such category in one of my own questions: http://stackoverflow.com/questions/1342286/what-kind-of-category-methods-do-you-use-to-make-cocoa-programming-easier
e.James
Don't you need to declare `percentages` as `struct RGBAData percentages` (and same for `original` and `percents`), or `typedef` the `struct` as `RGBAData`? edit: I think my answer is too long-winded . . . +1 for simplicity :)
dreamlax
Ah, thank you. good point. I'll fix the declaration
e.James
Thanks. I wasn't trying to do it in a low level manner intentionally, but instead was following Apple's sample. Even in the documentation for CGContext, under CGContextSetFillPattern, it shows the second parameter as "const CGFloat components[]" and not an NSArray. So I assumed using an NSArray was verboten here. Just ignorance. Monkey see Apple sample code. Monkey do bad imitation of Apple sample code.
mwt
+5  A: 

Define a struct that contains all of the components, or wrap up each individual component in an NSNumber. Alternatively, use an NSColor instance to contain your colour components.

  • struct way:

    typedef struct
    {
        float red;
        float green;
        float blue;
        float alpha;
    } MyColor;
    
    
    - (MyColor) percentagesRGBArray:(MyColor) incoming
    {
        MyColor result;
        result.red = incoming.red / 255;
        result.green = incoming.green / 255;
        result.blue = incoming.blue / 255;
        result.alpha = incoming.alpha;
        return result;
    }
    
  • NSNumber way:

    - (NSArray *) percentagesRGBArray:(float[]) rgbArray
    {
        NSNumber *red = [NSNumber numberWithFloat:rgbArray[0] / 255];
        NSNumber *green = [NSNumber numberWithFloat:rgbArray[1] / 255];
        NSNumber *blue = [NSNumber numberWithFloat:rgbArray[2] / 255];
        NSNumber *alpha = [NSNumber numberWithFloat:rgbArray[3]];
    
    
    
    return [NSArray arrayWithObjects:red, green, blue, alpha, nil];
    
    }
  • NSColor way:

    - (NSColor *) percentagesRGBArray:(float[]) rgbArray
    {
        CGFloat red = rgbArray[0] / 255;
        CGFloat green = rgbArray[1] / 255;
        CGFloat blue = rgbArray[2] / 255;
        CGFloat alpha = rgbArray[3];
    
    
    
    return [NSColor colorWithDeviceRed:red
                                 green:green
                                  blue:blue
                                 alpha:alpha];
    
    }
dreamlax
+1 This is much more in-depth than my answer.
e.James
Thank you. I learned more from this response than you might imagine.
mwt
+2  A: 

Both e.James and dreamlax's answers give good approaches for doing this. But to answer what was wrong with your original code:

Basically, it has to do with how C arrays work. An array is essentially equivalent to a pointer to its first element. In fact, when you pass an array to a function, it decays into a pointer. You're still allowed to name the argument float myArray[4] (you have to declare the number of elements) just to make it clear that the pointer is supposed to be to an array of 4 elements — but you're still getting a pointer. Now consider the return value. What are you returning? We already established that you can't return an array by value, because it decays into a pointer. But even if you change the return type to be a pointer, it still won't work, because the array will have gone out of scope once the function returns. In order to return an array, you have to malloc the memory, and then you're responsible for freeing it later.

This is why you should avoid working with C arrays when at all possible. They're really low-level and fiddly. Even when you do use them, it's usually a good idea to hide them behind an API that takes care of the low-level details for you.

Chuck
+1 for describing the problem :)
e.James
I see. Thank you.
mwt
A: 

I think i'm late) but i have just found this thread.

the C way to do this is to create an array before invoking a function;

+(void) percentagesRGBArray:(float[])inArray toArray:(float*)outArray { ... } float array1[4]; float array2[4]; [MyClass percentagesRGBArray:array1 toArray:array2];

Remizorrr