Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Displaying and array of colors using CGShading

I have an array of CGColors that I need to display across a path. I tried doing this with a CGGradient, but I don't want the colors the blend between between values. It looks like the best solution would be to use a GGShading object, but I am having trouble figuring out exactly how they work. I'm mainly confused about what I need to have for the CGFunction input for the CGShading.

Can someone point me in the right direction on what I would need to make this CGFunction look like to to simply display an array go CGColors on a specified CGPath?

Thanks!

like image 758
Ross Kimes Avatar asked Aug 05 '11 22:08

Ross Kimes


1 Answers

Perhaps a little late, so I hope this is still of use to you. I've listed the code for a simple UIView subclass that draws a circle using the shading you described. The code with comments should be self-explanatory.

@implementation CGShadingCircle


// This is the callback of our shading function.
// info:    a pointer to our NSMutableArray of UIColor objects
// inData:  contains a single float that gives is the current position within the gradient
// outData: we fill this with the color to display at the given position
static void CGShadingCallback(void* info, const float* inData, float* outData) {
    // Our colors
    NSMutableArray* colors = (NSMutableArray*)info;
    // Position within the gradient, ranging from 0.0 to 1.0
    CGFloat position = *inData;

    // Find the color that we want to used based on the current position;
    NSUInteger colorIndex = position * [colors count];

    // Account for the edge case where position == 1.0
    if (colorIndex >= [colors count])
        colorIndex = [colors count] - 1;

    // Get our desired color from the array
    UIColor* color = [colors objectAtIndex:colorIndex];

    // Copy the 4 color components (red, green, blue, alpha) to outData
    memcpy(outData, CGColorGetComponents(color.CGColor), 4 * sizeof(CGFloat));  
}


// Set up our colors and shading function
- (void)initInternal {  
    _colors = [[NSMutableArray alloc] init];

    // Creating the colors in this way ensures that the underlying color space is UIDeviceRGBColorSpace
    // and thus has 4 color components: red, green, blue, alpha
    [_colors addObject:[UIColor colorWithRed:1.0f green:0.0f blue:0.0f alpha:1.0f]]; // Red
    [_colors addObject:[UIColor colorWithRed:0.0f green:1.0f blue:0.0f alpha:1.0f]]; // Green
    [_colors addObject:[UIColor colorWithRed:0.0f green:0.0f blue:1.0f alpha:1.0f]]; // Blue
    [_colors addObject:[UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.0f]]; // White
    [_colors addObject:[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:1.0f]]; // Black

    // Define the shading callbacks
    CGFunctionCallbacks callbacks;
    callbacks.version = 0;                      // Defaults to 0
    callbacks.evaluate = CGShadingCallback;     // This is our color selection function
    callbacks.releaseInfo = NULL;               // Not used

    // As input to our function we want 1 value in the range [0.0, 1.0].
    // This is our position within the 'gradient'.
    size_t domainDimension = 1;
    CGFloat domain[2] = {0.0f, 1.0f};

    // The output of our function is 4 values, each in the range [0.0, 1.0].
    // This is our selected color for the input position.
    // The 4 values are the red, green, blue and alpha components.
    size_t rangeDimension = 4;
    CGFloat range[8] = {0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f};

    // Create the shading function
    _shadingFunction = CGFunctionCreate(_colors, domainDimension, domain, rangeDimension, range, &callbacks);   
}

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self initInternal];
    }
    return self;
}

- (id)initWithCoder:(NSCoder*)decoder {
    self = [super initWithCoder:decoder];
    if (self) {
        [self initInternal];
    }
    return self;
}

- (void)dealloc {
    [_colors release];
    CGFunctionRelease(_shadingFunction);

    [super dealloc];
}

- (void)drawRect:(CGRect)rect {
    CGRect b = self.bounds;
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    // Create a simple elliptic path
    CGContextAddEllipseInRect(ctx, b);
    // Set the current path as the clipping path
    CGContextClip(ctx);

    // Create our shading using the function that was defined earlier.
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
    CGShadingRef shading = CGShadingCreateAxial(colorspace,
        CGPointMake(CGRectGetMinX(b), CGRectGetMidY(b)),
        CGPointMake(CGRectGetMaxX(b), CGRectGetMidY(b)),
        _shadingFunction,
        true,
        true);

    // Draw the shading
    CGContextDrawShading(ctx, shading);


    // Cleanup
    CGShadingRelease(shading);
    CGColorSpaceRelease(colorspace);
}

@end

This gives me the following output:

I hope this helps!

like image 200
Desmond Avatar answered Sep 30 '22 16:09

Desmond