// // CTGradient.m // // Created by Chad Weider on 2/14/07. // Copyright (c) 2007 Chad Weider. // Some rights reserved: // // Version: 1.6 #import "CTGradient.h" //C Fuctions for color blending static void linearEvaluation (void *info, const float *in, float *out); @implementation CTGradient - (id)init { self = [super init]; if (self != nil) { if(gradientFunction != NULL) CGFunctionRelease(gradientFunction); CGFunctionCallbacks evaluationCallbackInfo = {0 , &linearEvaluation, NULL}; //Version, evaluator function, cleanup function static const float input_value_range [2] = { 0, 1 }; //range for the evaluator input static const float output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 }; //ranges for the evaluator output (4 returned values) gradientFunction = CGFunctionCreate(&elementList, //the two transition colors 1, input_value_range , //number of inputs (just fraction of progression) 4, output_value_ranges, //number of outputs (4 - RGBa) &evaluationCallbackInfo); //info for using the evaluator function } return self; } - (void)dealloc { CGFunctionRelease(gradientFunction); CTGradientElement *elementToRemove = elementList; while(elementList != nil) { elementToRemove = elementList; elementList = elementList->nextElement; free(elementToRemove); } [super dealloc]; } + (id)gradientWithBeginningColor:(NSColor *)begin endingColor:(NSColor *)end { id newInstance = [[[self class] alloc] init]; CTGradientElement color1; CTGradientElement color2; [[begin colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&color1.red green:&color1.green blue:&color1.blue alpha:&color1.alpha]; [[end colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&color2.red green:&color2.green blue:&color2.blue alpha:&color2.alpha]; color1.position = 0; color2.position = 1; [newInstance addElement:&color1]; [newInstance addElement:&color2]; return [newInstance autorelease]; } - (void)fillRect:(NSRect)rect angle:(float)angle { //First Calculate where the beginning and ending points should be CGPoint startPoint; CGPoint endPoint; if(angle == 0) { //screw the calculations - we know the answer startPoint = CGPointMake(NSMinX(rect), NSMinY(rect)); //right of rect endPoint = CGPointMake(NSMaxX(rect), NSMinY(rect)); //left of rect } else if(angle == 90){ //same as above startPoint = CGPointMake(NSMinX(rect), NSMinY(rect)); //bottom of rect endPoint = CGPointMake(NSMinX(rect), NSMaxY(rect)); //top of rect } else { //ok, we'll do the calculations now float x,y; float sina, cosa, tana; float length; float deltax, deltay; float rangle = angle * pi/180; //convert the angle to radians if(fabsf(tan(rangle))<=1) { //for range [-45,45], [135,225] x = NSWidth(rect); y = NSHeight(rect); sina = sin(rangle); cosa = cos(rangle); tana = tan(rangle); length = x/fabsf(cosa)+(y-x*fabsf(tana))*fabsf(sina); deltax = length*cosa/2; deltay = length*sina/2; } else { //for range [45,135], [225,315] x = NSHeight(rect); y = NSWidth(rect); sina = sin(rangle - 90*pi/180); cosa = cos(rangle - 90*pi/180); tana = tan(rangle - 90*pi/180); length = x/fabsf(cosa)+(y-x*fabsf(tana))*fabsf(sina); deltax =-length*sina/2; deltay = length*cosa/2; } startPoint = CGPointMake(NSMidX(rect)-deltax, NSMidY(rect)-deltay); endPoint = CGPointMake(NSMidX(rect)+deltax, NSMidY(rect)+deltay); } //Calls to CoreGraphics CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; CGContextSaveGState(currentContext); #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); #else CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); #endif CGShadingRef myCGShading = CGShadingCreateAxial(colorspace, startPoint, endPoint, gradientFunction, false, false); CGContextClipToRect (currentContext, *(CGRect *)&rect); //This is where the action happens CGContextDrawShading(currentContext, myCGShading); CGShadingRelease(myCGShading); CGColorSpaceRelease(colorspace ); CGContextRestoreGState(currentContext); } - (void)addElement:(CTGradientElement *)newElement { if(elementList == nil || newElement->position < elementList->position) { //inserting at beginning of list CTGradientElement *tmpNext = elementList; elementList = malloc(sizeof(CTGradientElement)); *elementList = *newElement; elementList->nextElement = tmpNext; } else { //inserting somewhere inside list CTGradientElement *curElement = elementList; while(curElement->nextElement != nil && !((curElement->position <= newElement->position) && (newElement->position < curElement->nextElement->position))) curElement = curElement->nextElement; CTGradientElement *tmpNext = curElement->nextElement; curElement->nextElement = malloc(sizeof(CTGradientElement)); *(curElement->nextElement) = *newElement; curElement->nextElement->nextElement = tmpNext; } } void linearEvaluation (void *info, const float *in, float *out) { float position = *in; if(*(CTGradientElement **)info == nil) { //if elementList is empty return clear color out[0] = out[1] = out[2] = out[3] = 1; return; } //This grabs the first two colors in the sequence CTGradientElement *color1 = *(CTGradientElement **)info; CTGradientElement *color2 = color1->nextElement; //make sure first color and second color are on other sides of position while(color2 != nil && color2->position < position) { color1 = color2; color2 = color1->nextElement; } //if we don't have another color then make next color the same color if(color2 == nil) color2 = color1; //----------FailSafe settings---------- //color1->red = 1; color2->red = 0; //color1->green = 1; color2->green = 0; //color1->blue = 1; color2->blue = 0; //color1->alpha = 1; color2->alpha = 1; //color1->position = .5; //color2->position = .5; //------------------------------------- if(position <= color1->position) { //Make all below color color1's position equal to color1 out[0] = color1->red; out[1] = color1->green; out[2] = color1->blue; out[3] = color1->alpha; } else if(position >= color2->position) { //Make all above color color2's position equal to color2 out[0] = color2->red; out[1] = color2->green; out[2] = color2->blue; out[3] = color2->alpha; } else { //Interpolate color at postions between color1 and color1 //adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position position = (position-color1->position)/(color2->position - color1->position); out[0] = (color2->red - color1->red )*position + color1->red; out[1] = (color2->green - color1->green)*position + color1->green; out[2] = (color2->blue - color1->blue )*position + color1->blue; out[3] = (color2->alpha - color1->alpha)*position + color1->alpha; } } @end