Rayner software

Rayner Software produces Mac and iPhone consumer applications

Angular Gradients in Cocoa

This function, createAngularGradientImage(), creates and returns a new CGImageRef containing an angular sweep gradient. This type of gradient is available in Photoshop, but not natively in CoreGraphics. They are useful for creating simulated radar screens or spinning progress indicators. The returned image is alpha-only, and can be drawn in any color.



createAngularGradientImage(

CGSizeMake(200,200), 

NO, 

30

);

ag_0

createAngularGradientImage(

CGSizeMake(200,200), 

NO, 

9999

);

ag_1

createAngularGradientImage(

CGSizeMake(200,200), 

YES, 

0

);

ag_2

// Creates and returns a CGImageRef containing an angular gradient sweep.

// Returned image is alpha-only.

// Caller responsible for releasing.

CGImageRef createAngularGradientImage(

CGSize SIZE, // The size for the returned image

BOOL direction, // YES = CW, NO = CCW

CGFloat RINGWIDTH // If >0, mask the gradient to a ring (useful for progress spinners)

)

{

uint8_t *bytes = NULL;

uint8_t *bytes_ptr = NULL;

CGContextRef bmCxt = NULL;

CGImageRef bmImg = NULL;

CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();

CGColorSpaceRef csg = CGColorSpaceCreateDeviceGray();

int32_t w, h, halfw, halfh;

CGSize sz = CGSizeMake(floor(SIZE.width),floor(SIZE.height));

w = (uint32_t)(sz.width);

h = (uint32_t)(sz.height);

halfw = w/2;

halfh = h/2;

bytes = malloc(sz.width * sz.height * 4);

bytes_ptr = bytes;

for (int32_t yi = 0; yi < h; yi++)

{

for (int32_t xi = 0; xi < w; xi++)

{

double theta = atan2(yi-halfh, xi-halfw);

if (theta < 0)

theta += (M_PI * 2.);

theta /= (M_PI * 2.);

theta = (theta<0)?0:((theta>1)?1:theta); // CLAMP(0,v,1)

if (!direction) theta = 1. - theta;

theta *= 255.;

bytes_ptr[0] = (uint8_t)theta;

bytes_ptr += 1;

}

}

bmCxt = CGBitmapContextCreate(bytes, w, h, 8, 1*w, csg, kCGImageAlphaOnly);

if (RINGWIDTH != 0)

{

CGContextRef ringCxt = NULL;

CGImageRef ringImg = NULL;

ringCxt = CGBitmapContextCreate(NULL, w, h, 8, w, csg, kCGImageAlphaOnly);

CGContextSetStrokeColorSpace(ringCxt, csg);

CGContextSetFillColorSpace(ringCxt, csg);

CGFloat blk[] = {0,1};

CGContextSetFillColor(ringCxt, blk);

CGContextFillRect(ringCxt, CGRectMake(0,0,w,h));

CGContextSetBlendMode(ringCxt, kCGBlendModeSourceOut);

CGContextFillEllipseInRect(ringCxt, CGRectInset(CGRectMake(0,0,w,h), 1, 1));

if (RINGWIDTH < MIN(halfw, halfh))

{

CGContextSetBlendMode(ringCxt, kCGBlendModeSourceIn);

CGContextFillEllipseInRect(ringCxt, CGRectInset(CGRectMake(0,0,w,h), 1+RINGWIDTH, 1+RINGWIDTH));

}

ringImg = CGBitmapContextCreateImage(ringCxt);

CGContextSaveGState(bmCxt);

CGContextSetBlendMode(bmCxt, kCGBlendModeSourceOut);

CGContextDrawImage(bmCxt, CGRectMake(0,0,w,h), ringImg);

CGContextRestoreGState(bmCxt);

CGContextRelease(ringCxt);

CGImageRelease(ringImg);

}

bmImg = CGBitmapContextCreateImage(bmCxt);

// cleanup

CGColorSpaceRelease(cs);

CGColorSpaceRelease(csg);

CGContextRelease(bmCxt);

free(bytes);

return bmImg;

}