I am trying to write a CGFunctionRef that will act as a shading function for a CGShadingRef object in my project that uses ARC. I am trying to pass a NSMutableArray (filled with UIColors) into my CGFunctionRef callback.
Here is my init method
- (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);
}
Here is my callback method
static void CGShadingCallback(void* info, const float* inData, float* outData) {
// Our colors
NSMutableArray* colors = (__bridge_transfer 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));
}
Here is my drawRect method
- (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);
}
If I just use __bridge in my callback method, then is crashes on
NSMutableArray* colors = (__bridge NSMutableArray*)info; with an EXC_BAD_ACCESS.
If I use __bridge_transfer, it crashes on CGContextDrawShading(ctx, shading); in the drawRect with an EXC_BAD_ACCESS.
You create a array and use that array as the context of a CGFunction
.
So that array needs to be retained when given to the function. You can do this with the __bridge_retained
keyword:
CGFunctionCallbacks callbacks;
callbacks.version = 0;
callbacks.evaluate = CGShadingCallback;
callbacks.releaseInfo = myReleaseCallback;
_shadingFunction = CGFunctionCreate((__bridge_retained void *)_colors, domainDimension, domain, rangeDimension, range, &callbacks);
Then, you must not use in __bridge_transfer
in the drawing callback. __bridge_transfer
cast the value into a strong reference without retaining it (ownership is transferred). This is equivalent to releasing the array. As your callback may be invoked many times, this is not the right place to release the array.
The array must be released when the function is destroyed. This is the purpose of the releaseInfo
callback:
static void myReleaseCallback(void *info) {
CFRelease(info);
}
You can also do this with a __bridge_transfer
cast but that's is not very elegant:
static void myReleaseCallback(void *info) {
NSArray *array = (__bridge_transfer NSArray *)info;
// now we need to do something with the array if we don't want a compiler warning
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.