Index: branches/diebuche/ChameleonPrefPane/CTGradient.m =================================================================== --- branches/diebuche/ChameleonPrefPane/CTGradient.m (revision 0) +++ branches/diebuche/ChameleonPrefPane/CTGradient.m (revision 80) @@ -0,0 +1,1308 @@ +// +// CTGradient.m +// +// Created by Chad Weider on 2/14/07. +// Copyright (c) 2007 Chad Weider. +// Some rights reserved: +// +// Version: 1.6 + +#import "CTGradient.h" + +@interface CTGradient (Private) +- (void)_commonInit; +- (void)setBlendingMode:(CTGradientBlendingMode)mode; +- (void)addElement:(CTGradientElement*)newElement; + +- (CTGradientElement *)elementAtIndex:(unsigned)index; + +- (CTGradientElement)removeElementAtIndex:(unsigned)index; +- (CTGradientElement)removeElementAtPosition:(float)position; +@end + +//C Fuctions for color blending +static void linearEvaluation (void *info, const float *in, float *out); +static void chromaticEvaluation(void *info, const float *in, float *out); +static void inverseChromaticEvaluation(void *info, const float *in, float *out); +static void transformRGB_HSV(float *components); +static void transformHSV_RGB(float *components); +static void resolveHSV(float *color1, float *color2); + + +@implementation CTGradient +/////////////////////////////////////Initialization Type Stuff +- (id)init +{ + self = [super init]; + + if (self != nil) + { + [self _commonInit]; + [self setBlendingMode:CTLinearBlendingMode]; + } + return self; +} + +- (void)_commonInit +{ + elementList = nil; +} + +- (void)dealloc +{ + CGFunctionRelease(gradientFunction); + + CTGradientElement *elementToRemove = elementList; + while(elementList != nil) + { + elementToRemove = elementList; + elementList = elementList->nextElement; + free(elementToRemove); + } + + [super dealloc]; +} + +- (id)copyWithZone:(NSZone *)zone +{ + CTGradient *copy = [[[self class] allocWithZone:zone] init]; + + //now just copy my elementlist + CTGradientElement *currentElement = elementList; + while(currentElement != nil) + { + [copy addElement:currentElement]; + currentElement = currentElement->nextElement; + } + + [copy setBlendingMode:blendingMode]; + + return copy; +} + +- (void)encodeWithCoder:(NSCoder *)coder +{ + if([coder allowsKeyedCoding]) + { + unsigned count = 0; + CTGradientElement *currentElement = elementList; + while(currentElement != nil) + { + [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->red)]; + [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->green)]; + [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->blue)]; + [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->alpha)]; + [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->position)]; + + count++; + currentElement = currentElement->nextElement; + } + [coder encodeInt:count forKey:@"CTGradientElementCount"]; + [coder encodeInt:blendingMode forKey:@"CTGradientBlendingMode"]; + } + else + [NSException raise:NSInvalidArchiveOperationException format:@"Only supports NSKeyedArchiver coders"]; +} + +- (id)initWithCoder:(NSCoder *)coder +{ + [self _commonInit]; + + [self setBlendingMode:[coder decodeIntForKey:@"CTGradientBlendingMode"]]; + unsigned count = [coder decodeIntForKey:@"CTGradientElementCount"]; + + while(count != 0) + { + CTGradientElement newElement; + + [coder decodeValueOfObjCType:@encode(float) at:&(newElement.red)]; + [coder decodeValueOfObjCType:@encode(float) at:&(newElement.green)]; + [coder decodeValueOfObjCType:@encode(float) at:&(newElement.blue)]; + [coder decodeValueOfObjCType:@encode(float) at:&(newElement.alpha)]; + [coder decodeValueOfObjCType:@encode(float) at:&(newElement.position)]; + + count--; + [self addElement:&newElement]; + } + return self; +} +#pragma mark - + + + +#pragma mark Creation ++ (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]; +} + + ++ (id)dividerGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = color1.green = color1.blue = 0.84; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = color2.green = color2.blue = 0.90; + color2.alpha = 1.00; + color2.position = 0.5; + + CTGradientElement color3; + color3.red = color3.green = color3.blue = 0.84; + color3.alpha = 1.00; + color3.position = 1.0; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + [newInstance addElement:&color3]; + + return [newInstance autorelease]; +} + + ++ (id)statusBarGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = color1.green = color1.blue = 0.872; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = color2.green = color2.blue = 0.77; + color2.alpha = 1.00; + color2.position = 0.618; + + CTGradientElement color3; + color3.red = color3.green = color3.blue = 0.74; + color3.alpha = 1.00; + color3.position = 1.0; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + [newInstance addElement:&color3]; + + return [newInstance autorelease]; +} + + ++ (id)aquaSelectedGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = 0.58; + color1.green = 0.86; + color1.blue = 0.98; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = 0.42; + color2.green = 0.68; + color2.blue = 0.90; + color2.alpha = 1.00; + color2.position = 11.5/23; + + CTGradientElement color3; + color3.red = 0.64; + color3.green = 0.80; + color3.blue = 0.94; + color3.alpha = 1.00; + color3.position = 11.5/23; + + CTGradientElement color4; + color4.red = 0.56; + color4.green = 0.70; + color4.blue = 0.90; + color4.alpha = 1.00; + color4.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + [newInstance addElement:&color3]; + [newInstance addElement:&color4]; + + return [newInstance autorelease]; +} + ++ (id)aquaNormalGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = color1.green = color1.blue = 0.95; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = color2.green = color2.blue = 0.83; + color2.alpha = 1.00; + color2.position = 11.5/23; + + CTGradientElement color3; + color3.red = color3.green = color3.blue = 0.95; + color3.alpha = 1.00; + color3.position = 11.5/23; + + CTGradientElement color4; + color4.red = color4.green = color4.blue = 0.92; + color4.alpha = 1.00; + color4.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + [newInstance addElement:&color3]; + [newInstance addElement:&color4]; + + return [newInstance autorelease]; +} + ++ (id)aquaPressedGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = color1.green = color1.blue = 0.80; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = color2.green = color2.blue = 0.64; + color2.alpha = 1.00; + color2.position = 11.5/23; + + CTGradientElement color3; + color3.red = color3.green = color3.blue = 0.80; + color3.alpha = 1.00; + color3.position = 11.5/23; + + CTGradientElement color4; + color4.red = color4.green = color4.blue = 0.77; + color4.alpha = 1.00; + color4.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + [newInstance addElement:&color3]; + [newInstance addElement:&color4]; + + return [newInstance autorelease]; +} + ++ (id)unifiedSelectedGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = color1.green = color1.blue = 0.85; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = color2.green = color2.blue = 0.95; + color2.alpha = 1.00; + color2.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + + return [newInstance autorelease]; +} + ++ (id)unifiedNormalGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = color1.green = color1.blue = 0.75; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = color2.green = color2.blue = 0.90; + color2.alpha = 1.00; + color2.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + + return [newInstance autorelease]; +} + ++ (id)unifiedPressedGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = color1.green = color1.blue = 0.60; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = color2.green = color2.blue = 0.75; + color2.alpha = 1.00; + color2.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + + return [newInstance autorelease]; +} + ++ (id)unifiedDarkGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = color1.green = color1.blue = 0.68; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = color2.green = color2.blue = 0.83; + color2.alpha = 1.00; + color2.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + + return [newInstance autorelease]; +} + ++ (id)sourceListSelectedGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = 0.06; + color1.green = 0.37; + color1.blue = 0.85; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = 0.30; + color2.green = 0.60; + color2.blue = 0.92; + color2.alpha = 1.00; + color2.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + + return [newInstance autorelease]; +} + ++ (id)sourceListUnselectedGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = 0.43; + color1.green = 0.43; + color1.blue = 0.43; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = 0.60; + color2.green = 0.60; + color2.blue = 0.60; + color2.alpha = 1.00; + color2.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + + return [newInstance autorelease]; +} + ++ (id)rainbowGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = 1.00; + color1.green = 0.00; + color1.blue = 0.00; + color1.alpha = 1.00; + color1.position = 0.0; + + CTGradientElement color2; + color2.red = 0.54; + color2.green = 0.00; + color2.blue = 1.00; + color2.alpha = 1.00; + color2.position = 1.0; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + + [newInstance setBlendingMode:CTChromaticBlendingMode]; + + return [newInstance autorelease]; +} + ++ (id)hydrogenSpectrumGradient +{ + id newInstance = [[[self class] alloc] init]; + + struct {float hue; float position; float width;} colorBands[4]; + + colorBands[0].hue = 22; + colorBands[0].position = .145; + colorBands[0].width = .01; + + colorBands[1].hue = 200; + colorBands[1].position = .71; + colorBands[1].width = .008; + + colorBands[2].hue = 253; + colorBands[2].position = .885; + colorBands[2].width = .005; + + colorBands[3].hue = 275; + colorBands[3].position = .965; + colorBands[3].width = .003; + + int i; + ///////////////////////////// + for(i = 0; i < 4; i++) + { + float color[4]; + color[0] = colorBands[i].hue - 180*colorBands[i].width; + color[1] = 1; + color[2] = 0.001; + color[3] = 1; + transformHSV_RGB(color); + CTGradientElement fadeIn; + fadeIn.red = color[0]; + fadeIn.green = color[1]; + fadeIn.blue = color[2]; + fadeIn.alpha = color[3]; + fadeIn.position = colorBands[i].position - colorBands[i].width; + + + color[0] = colorBands[i].hue; + color[1] = 1; + color[2] = 1; + color[3] = 1; + transformHSV_RGB(color); + CTGradientElement band; + band.red = color[0]; + band.green = color[1]; + band.blue = color[2]; + band.alpha = color[3]; + band.position = colorBands[i].position; + + color[0] = colorBands[i].hue + 180*colorBands[i].width; + color[1] = 1; + color[2] = 0.001; + color[3] = 1; + transformHSV_RGB(color); + CTGradientElement fadeOut; + fadeOut.red = color[0]; + fadeOut.green = color[1]; + fadeOut.blue = color[2]; + fadeOut.alpha = color[3]; + fadeOut.position = colorBands[i].position + colorBands[i].width; + + + [newInstance addElement:&fadeIn]; + [newInstance addElement:&band]; + [newInstance addElement:&fadeOut]; + } + + [newInstance setBlendingMode:CTChromaticBlendingMode]; + + return [newInstance autorelease]; +} + +#pragma mark - + + + +#pragma mark Modification +- (CTGradient *)gradientWithAlphaComponent:(float)alpha +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement *curElement = elementList; + CTGradientElement tempElement; + + while(curElement != nil) + { + tempElement = *curElement; + tempElement.alpha = alpha; + [newInstance addElement:&tempElement]; + + curElement = curElement->nextElement; + } + + return [newInstance autorelease]; +} + +- (CTGradient *)gradientWithBlendingMode:(CTGradientBlendingMode)mode +{ + CTGradient *newGradient = [self copy]; + + [newGradient setBlendingMode:mode]; + + return [newGradient autorelease]; +} + + +//Adds a color stop with at in elementList +//(if two elements are at the same position then added imediatly after the one that was there already) +- (CTGradient *)addColorStop:(NSColor *)color atPosition:(float)position +{ + CTGradient *newGradient = [self copy]; + CTGradientElement newGradientElement; + + //put the components of color into the newGradientElement - must make sure it is a RGB color (not Gray or CMYK) + [[color colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&newGradientElement.red + green:&newGradientElement.green + blue:&newGradientElement.blue + alpha:&newGradientElement.alpha]; + newGradientElement.position = position; + + //Pass it off to addElement to take care of adding it to the elementList + [newGradient addElement:&newGradientElement]; + + return [newGradient autorelease]; +} + + +//Removes the color stop at from elementList +- (CTGradient *)removeColorStopAtPosition:(float)position +{ + CTGradient *newGradient = [self copy]; + CTGradientElement removedElement = [newGradient removeElementAtPosition:position]; + + if(isnan(removedElement.position)) + [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtPosition:]: no such colorStop at position (%f)", [self class], position]; + + return [newGradient autorelease]; +} + +- (CTGradient *)removeColorStopAtIndex:(unsigned)index +{ + CTGradient *newGradient = [self copy]; + CTGradientElement removedElement = [newGradient removeElementAtIndex:index]; + + if(isnan(removedElement.position)) + [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtIndex:]: index (%i) beyond bounds", [self class], index]; + + return [newGradient autorelease]; +} +#pragma mark - + + + +#pragma mark Information +- (CTGradientBlendingMode)blendingMode +{ + return blendingMode; +} + +//Returns color at in gradient +- (NSColor *)colorStopAtIndex:(unsigned)index +{ + CTGradientElement *element = [self elementAtIndex:index]; + + if(element != nil) + return [NSColor colorWithCalibratedRed:element->red + green:element->green + blue:element->blue + alpha:element->alpha]; + + [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtIndex:]: index (%i) beyond bounds", [self class], index]; + + return nil; +} + +- (NSColor *)colorAtPosition:(float)position +{ + float components[4]; + + switch(blendingMode) + { + case CTLinearBlendingMode: + linearEvaluation(&elementList, &position, components); break; + case CTChromaticBlendingMode: + chromaticEvaluation(&elementList, &position, components); break; + case CTInverseChromaticBlendingMode: + inverseChromaticEvaluation(&elementList, &position, components); break; + } + + + return [NSColor colorWithCalibratedRed:components[0] + green:components[1] + blue:components[2] + alpha:components[3]]; +} +#pragma mark - + + + +#pragma mark Drawing +- (void)drawSwatchInRect:(NSRect)rect +{ + [self fillRect:rect angle:45]; +} + +- (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)radialFillRect:(NSRect)rect +{ + CGPoint startPoint , endPoint; + float startRadius, endRadius; + float scalex, scaley, transx, transy; + + startPoint = endPoint = CGPointMake(NSMidX(rect), NSMidY(rect)); + + startRadius = -1; + if(NSHeight(rect)>NSWidth(rect)) + { + scalex = NSWidth(rect)/NSHeight(rect); + transx = (NSHeight(rect)-NSWidth(rect))/2; + scaley = 1; + transy = 1; + endRadius = NSHeight(rect)/2; + } + else + { + scalex = 1; + transx = 1; + scaley = NSHeight(rect)/NSWidth(rect); + transy = (NSWidth(rect)-NSHeight(rect))/2; + endRadius = NSWidth(rect)/2; + } + + //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 = CGShadingCreateRadial(colorspace, startPoint, startRadius, endPoint, endRadius, gradientFunction, true, true); + + CGContextClipToRect (currentContext, *(CGRect *)&rect); + CGContextScaleCTM (currentContext, scalex, scaley); + CGContextTranslateCTM(currentContext, transx, transy); + CGContextDrawShading (currentContext, myCGShading); //This is where the action happens + + CGShadingRelease(myCGShading); + CGColorSpaceRelease(colorspace); + CGContextRestoreGState(currentContext); +} + +- (void)fillBezierPath:(NSBezierPath *)path angle:(float)angle +{ + NSGraphicsContext *currentContext = [NSGraphicsContext currentContext]; + [currentContext saveGraphicsState]; + NSAffineTransform *transform = [[NSAffineTransform alloc] init]; + + [transform rotateByDegrees:-angle]; + [path transformUsingAffineTransform:transform]; + [transform invert]; + [transform concat]; + + [path addClip]; + [self fillRect:[path bounds] angle:0]; + [path transformUsingAffineTransform:transform]; + [transform release]; + [currentContext restoreGraphicsState]; +} +- (void)radialFillBezierPath:(NSBezierPath *)path +{ + NSGraphicsContext *currentContext = [NSGraphicsContext currentContext]; + [currentContext saveGraphicsState]; + [path addClip]; + [self radialFillRect:[path bounds]]; + [currentContext restoreGraphicsState]; +} +#pragma mark - + + + +#pragma mark Private Methods +- (void)setBlendingMode:(CTGradientBlendingMode)mode; +{ + blendingMode = mode; + + //Choose what blending function to use + void *evaluationFunction; + switch(blendingMode) + { + case CTLinearBlendingMode: + evaluationFunction = &linearEvaluation; break; + case CTChromaticBlendingMode: + evaluationFunction = &chromaticEvaluation; break; + case CTInverseChromaticBlendingMode: + evaluationFunction = &inverseChromaticEvaluation; break; + } + + //replace the current CoreGraphics Function with new one + if(gradientFunction != NULL) + CGFunctionRelease(gradientFunction); + + CGFunctionCallbacks evaluationCallbackInfo = {0 , evaluationFunction, 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 +} + +- (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; + } +} + +- (CTGradientElement)removeElementAtIndex:(unsigned)index +{ + CTGradientElement removedElement; + + if(elementList != nil) + { + if(index == 0) + { + CTGradientElement *tmpNext = elementList; + elementList = elementList->nextElement; + + removedElement = *tmpNext; + free(tmpNext); + + return removedElement; + } + + unsigned count = 1; //we want to start one ahead + CTGradientElement *currentElement = elementList; + while(currentElement->nextElement != nil) + { + if(count == index) + { + CTGradientElement *tmpNext = currentElement->nextElement; + currentElement->nextElement = currentElement->nextElement->nextElement; + + removedElement = *tmpNext; + free(tmpNext); + + return removedElement; + } + + count++; + currentElement = currentElement->nextElement; + } + } + + //element is not found, return empty element + removedElement.red = 0.0; + removedElement.green = 0.0; + removedElement.blue = 0.0; + removedElement.alpha = 0.0; + removedElement.position = NAN; + removedElement.nextElement = nil; + + return removedElement; +} + +- (CTGradientElement)removeElementAtPosition:(float)position +{ + CTGradientElement removedElement; + + if(elementList != nil) + { + if(elementList->position == position) + { + CTGradientElement *tmpNext = elementList; + elementList = elementList->nextElement; + + removedElement = *tmpNext; + free(tmpNext); + + return removedElement; + } + else + { + CTGradientElement *curElement = elementList; + while(curElement->nextElement != nil) + { + if(curElement->nextElement->position == position) + { + CTGradientElement *tmpNext = curElement->nextElement; + curElement->nextElement = curElement->nextElement->nextElement; + + removedElement = *tmpNext; + free(tmpNext); + + return removedElement; + } + } + } + } + + //element is not found, return empty element + removedElement.red = 0.0; + removedElement.green = 0.0; + removedElement.blue = 0.0; + removedElement.alpha = 0.0; + removedElement.position = NAN; + removedElement.nextElement = nil; + + return removedElement; +} + + +- (CTGradientElement *)elementAtIndex:(unsigned)index; +{ + unsigned count = 0; + CTGradientElement *currentElement = elementList; + + while(currentElement != nil) + { + if(count == index) + return currentElement; + + count++; + currentElement = currentElement->nextElement; + } + + return nil; +} +#pragma mark - + + + +#pragma mark Core Graphics +//////////////////////////////////////Blending Functions///////////////////////////////////// +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; + } +} + + + + +//Chromatic Evaluation - +// This blends colors by their Hue, Saturation, and Value(Brightness) right now I just +// transform the RGB values stored in the CTGradientElements to HSB, in the future I may +// streamline it to avoid transforming in and out of HSB colorspace *for later* +// +// For the chromatic blend we shift the hue of color1 to meet the hue of color2. To do +// this we will add to the hue's angle (if we subtract we'll be doing the inverse +// chromatic...scroll down more for that). All we need to do is keep adding to the hue +// until we wrap around the colorwheel and get to color2. +void chromaticEvaluation(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; + + float c1[4]; + float c2[4]; + + //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; + } + + + c1[0] = color1->red; + c1[1] = color1->green; + c1[2] = color1->blue; + c1[3] = color1->alpha; + + c2[0] = color2->red; + c2[1] = color2->green; + c2[2] = color2->blue; + c2[3] = color2->alpha; + + transformRGB_HSV(c1); + transformRGB_HSV(c2); + resolveHSV(c1,c2); + + if(c1[0] > c2[0]) //if color1's hue is higher than color2's hue then + c2[0] += 360; // we need to move c2 one revolution around the wheel + + + if(position <= color1->position) //Make all below color color1's position equal to color1 + { + out[0] = c1[0]; + out[1] = c1[1]; + out[2] = c1[2]; + out[3] = c1[3]; + } + else if (position >= color2->position) //Make all above color color2's position equal to color2 + { + out[0] = c2[0]; + out[1] = c2[1]; + out[2] = c2[2]; + out[3] = c2[3]; + } + 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] = (c2[0] - c1[0])*position + c1[0]; + out[1] = (c2[1] - c1[1])*position + c1[1]; + out[2] = (c2[2] - c1[2])*position + c1[2]; + out[3] = (c2[3] - c1[3])*position + c1[3]; + } + + transformHSV_RGB(out); +} + + + +//Inverse Chromatic Evaluation - +// Inverse Chromatic is about the same story as Chromatic Blend, but here the Hue +// is strictly decreasing, that is we need to get from color1 to color2 by decreasing +// the 'angle' (i.e. 90å¼ -> 180å¼ would be done by subtracting 270å¼ and getting -180å¼... +// which is equivalent to 180å¼ mod 360å¼ +void inverseChromaticEvaluation(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; + + float c1[4]; + float c2[4]; + + //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; + } + + c1[0] = color1->red; + c1[1] = color1->green; + c1[2] = color1->blue; + c1[3] = color1->alpha; + + c2[0] = color2->red; + c2[1] = color2->green; + c2[2] = color2->blue; + c2[3] = color2->alpha; + + transformRGB_HSV(c1); + transformRGB_HSV(c2); + resolveHSV(c1,c2); + + if(c1[0] < c2[0]) //if color1's hue is higher than color2's hue then + c1[0] += 360; // we need to move c2 one revolution back on the wheel + + + if(position <= color1->position) //Make all below color color1's position equal to color1 + { + out[0] = c1[0]; + out[1] = c1[1]; + out[2] = c1[2]; + out[3] = c1[3]; + } + else if (position >= color2->position) //Make all above color color2's position equal to color2 + { + out[0] = c2[0]; + out[1] = c2[1]; + out[2] = c2[2]; + out[3] = c2[3]; + } + 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] = (c2[0] - c1[0])*position + c1[0]; + out[1] = (c2[1] - c1[1])*position + c1[1]; + out[2] = (c2[2] - c1[2])*position + c1[2]; + out[3] = (c2[3] - c1[3])*position + c1[3]; + } + + transformHSV_RGB(out); +} + + + +void transformRGB_HSV(float *components) //H,S,B -> R,G,B +{ + float H, S, V; + float R = components[0], + G = components[1], + B = components[2]; + + float MAX = R > G ? (R > B ? R : B) : (G > B ? G : B), + MIN = R < G ? (R < B ? R : B) : (G < B ? G : B); + + if(MAX == MIN) + H = NAN; + else if(MAX == R) + if(G >= B) + H = 60*(G-B)/(MAX-MIN)+0; + else + H = 60*(G-B)/(MAX-MIN)+360; + else if(MAX == G) + H = 60*(B-R)/(MAX-MIN)+120; + else if(MAX == B) + H = 60*(R-G)/(MAX-MIN)+240; + + S = MAX == 0 ? 0 : 1 - MIN/MAX; + V = MAX; + + components[0] = H; + components[1] = S; + components[2] = V; +} + +void transformHSV_RGB(float *components) //H,S,B -> R,G,B +{ + float R, G, B; + float H = fmodf(components[0],359), //map to [0,360) + S = components[1], + V = components[2]; + + int Hi = (int)floorf(H/60.) % 6; + float f = H/60-Hi, + p = V*(1-S), + q = V*(1-f*S), + t = V*(1-(1-f)*S); + + switch (Hi) + { + case 0: R=V;G=t;B=p; break; + case 1: R=q;G=V;B=p; break; + case 2: R=p;G=V;B=t; break; + case 3: R=p;G=q;B=V; break; + case 4: R=t;G=p;B=V; break; + case 5: R=V;G=p;B=q; break; + } + + components[0] = R; + components[1] = G; + components[2] = B; +} + +void resolveHSV(float *color1, float *color2) //H value may be undefined (i.e. graycale color) +{ // we want to fill it with a sensible value + if(isnan(color1[0]) && isnan(color2[0])) + color1[0] = color2[0] = 0; + else if(isnan(color1[0])) + color1[0] = color2[0]; + else if(isnan(color2[0])) + color2[0] = color1[0]; +} + +@end \ No newline at end of file Index: branches/diebuche/ChameleonPrefPane/Sources/RoundedBox_Prefix.pch =================================================================== --- branches/diebuche/ChameleonPrefPane/Sources/RoundedBox_Prefix.pch (revision 0) +++ branches/diebuche/ChameleonPrefPane/Sources/RoundedBox_Prefix.pch (revision 80) @@ -0,0 +1,7 @@ +// +// Prefix header for all source files of the 'RoundedBox' target in the 'RoundedBox' project +// + +#ifdef __OBJC__ + #import +#endif Index: branches/diebuche/ChameleonPrefPane/Sources/RoundedBox.h =================================================================== --- branches/diebuche/ChameleonPrefPane/Sources/RoundedBox.h (revision 0) +++ branches/diebuche/ChameleonPrefPane/Sources/RoundedBox.h (revision 80) @@ -0,0 +1,63 @@ +// +// RoundedBox.m +// RoundedBox +// +// Created by Matt Gemmell on 01/11/2005. +// Copyright 2006 Matt Gemmell. http://mattgemmell.com/ +// +// Permission to use this code: +// +// Feel free to use this code in your software, either as-is or +// in a modified form. Either way, please include a credit in +// your software's "About" box or similar, mentioning at least +// my name (Matt Gemmell). A link to my site would be nice too. +// +// Permission to redistribute this code: +// +// You can redistribute this code, as long as you keep these +// comments. You can also redistribute modified versions of the +// code, as long as you add comments to say that you've made +// modifications (keeping these original comments too). +// +// If you do use or redistribute this code, an email would be +// appreciated, just to let me know that people are finding my +// code useful. You can reach me at matt.gemmell@gmail.com +// + +#import +#import "CTGradient.h" + +@interface RoundedBox : NSBox { + float borderWidth; + NSColor *borderColor; + NSColor *titleColor; + NSColor *gradientStartColor; + NSColor *gradientEndColor; + NSColor *backgroundColor; + NSMutableDictionary *titleAttrs; + BOOL drawsFullTitleBar; + BOOL selected; + BOOL drawsGradientBackground; +} + +- (void)setDefaults; +- (NSBezierPath *)titlePathWithinRect:(NSRect)rect cornerRadius:(float)radius titleRect:(NSRect)titleRect; + +- (BOOL)drawsFullTitleBar; +- (void)setDrawsFullTitleBar:(BOOL)value; +- (BOOL)selected; +- (void)setSelected:(BOOL)newSelected; +- (NSColor *)borderColor; +- (void)setBorderColor:(NSColor *)newBorderColor; +- (NSColor *)titleColor; +- (void)setTitleColor:(NSColor *)newTitleColor; +- (NSColor *)gradientStartColor; +- (void)setGradientStartColor:(NSColor *)newGradientStartColor; +- (NSColor *)gradientEndColor; +- (void)setGradientEndColor:(NSColor *)newGradientEndColor; +- (NSColor *)backgroundColor; +- (void)setBackgroundColor:(NSColor *)newBackgroundColor; +- (BOOL)drawsGradientBackground; +- (void)setDrawsGradientBackground:(BOOL)newDrawsGradientBackground; + +@end Index: branches/diebuche/ChameleonPrefPane/Sources/RoundedBox.m =================================================================== --- branches/diebuche/ChameleonPrefPane/Sources/RoundedBox.m (revision 0) +++ branches/diebuche/ChameleonPrefPane/Sources/RoundedBox.m (revision 80) @@ -0,0 +1,371 @@ +// +// RoundedBox.m +// RoundedBox +// +// Created by Matt Gemmell on 01/11/2005. +// Copyright 2006 Matt Gemmell. http://mattgemmell.com/ +// +// Permission to use this code: +// +// Feel free to use this code in your software, either as-is or +// in a modified form. Either way, please include a credit in +// your software's "About" box or similar, mentioning at least +// my name (Matt Gemmell). A link to my site would be nice too. +// +// Permission to redistribute this code: +// +// You can redistribute this code, as long as you keep these +// comments. You can also redistribute modified versions of the +// code, as long as you add comments to say that you've made +// modifications (keeping these original comments too). +// +// If you do use or redistribute this code, an email would be +// appreciated, just to let me know that people are finding my +// code useful. You can reach me at matt.gemmell@gmail.com +// + +#import "RoundedBox.h" + + +@implementation RoundedBox + + +- (id)initWithFrame:(NSRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self setDefaults]; + } + return self; +} + + +- (void)dealloc +{ + [borderColor release]; + [titleColor release]; + [gradientStartColor release]; + [gradientEndColor release]; + [backgroundColor release]; + [titleAttrs release]; + + [super dealloc]; +} + + +- (void)setDefaults +{ + borderWidth = 2.0; + [self setBorderColor:[NSColor grayColor]]; + [self setTitleColor:[NSColor whiteColor]]; + [self setGradientStartColor:[NSColor colorWithCalibratedWhite:0.92 alpha:1.0]]; + [self setGradientEndColor:[NSColor colorWithCalibratedWhite:0.82 alpha:1.0]]; + [self setBackgroundColor:[NSColor colorWithCalibratedWhite:0.90 alpha:1.0]]; + [self setTitleFont:[NSFont boldSystemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; + + // Set up text attributes for drawing + NSMutableParagraphStyle *paragraphStyle; + paragraphStyle = [[NSMutableParagraphStyle alloc] init]; + [paragraphStyle setParagraphStyle:[NSParagraphStyle defaultParagraphStyle]]; + [paragraphStyle setAlignment:NSLeftTextAlignment]; + [paragraphStyle setLineBreakMode:NSLineBreakByTruncatingTail]; + + titleAttrs = [[NSMutableDictionary dictionaryWithObjectsAndKeys: + [self titleFont], NSFontAttributeName, + [self titleColor], NSForegroundColorAttributeName, + [paragraphStyle autorelease], NSParagraphStyleAttributeName, + nil] retain]; + + [self setDrawsFullTitleBar:NO]; + [self setSelected:NO]; + [self setDrawsGradientBackground:YES]; +} + + +- (void)awakeFromNib +{ + // For when we've been created in a nib file + [self setDefaults]; +} + + +- (BOOL)preservesContentDuringLiveResize +{ + // NSBox returns YES for this, but doing so would screw up the gradients. + return NO; +} + + +- (void)drawRect:(NSRect)rect { + + // Construct rounded rect path + NSRect boxRect = [self bounds]; + NSRect bgRect = boxRect; + bgRect = NSInsetRect(boxRect, borderWidth / 2.0, borderWidth / 2.0); + bgRect = NSIntegralRect(bgRect); + bgRect.origin.x += 0.5; + bgRect.origin.y += 0.5; + int minX = NSMinX(bgRect); + int midX = NSMidX(bgRect); + int maxX = NSMaxX(bgRect); + int minY = NSMinY(bgRect); + int midY = NSMidY(bgRect); + int maxY = NSMaxY(bgRect); + float radius = 4.0; + NSBezierPath *bgPath = [NSBezierPath bezierPath]; + + // Bottom edge and bottom-right curve + [bgPath moveToPoint:NSMakePoint(midX, minY)]; + [bgPath appendBezierPathWithArcFromPoint:NSMakePoint(maxX, minY) + toPoint:NSMakePoint(maxX, midY) + radius:radius]; + + // Right edge and top-right curve + [bgPath appendBezierPathWithArcFromPoint:NSMakePoint(maxX, maxY) + toPoint:NSMakePoint(midX, maxY) + radius:radius]; + + // Top edge and top-left curve + [bgPath appendBezierPathWithArcFromPoint:NSMakePoint(minX, maxY) + toPoint:NSMakePoint(minX, midY) + radius:radius]; + + // Left edge and bottom-left curve + [bgPath appendBezierPathWithArcFromPoint:NSMakePoint(minX, minY) + toPoint:NSMakePoint(midX, minY) + radius:radius]; + [bgPath closePath]; + + + // Draw background + + if ([self drawsGradientBackground]) { + // Draw gradient background + NSGraphicsContext *nsContext = [NSGraphicsContext currentContext]; + [nsContext saveGraphicsState]; + [bgPath addClip]; + CTGradient *gradient = [CTGradient gradientWithBeginningColor:[self gradientStartColor] endingColor:[self gradientEndColor]]; + NSRect gradientRect = [bgPath bounds]; + [gradient fillRect:gradientRect angle:270.0]; + [nsContext restoreGraphicsState]; + } else { + // Draw solid color background + [backgroundColor set]; + [bgPath fill]; + } + + + // Create drawing rectangle for title + + float titleHInset = borderWidth + 4.0; + float titleVInset = borderWidth; + NSSize titleSize = [[self title] sizeWithAttributes:titleAttrs]; + NSRect titleRect = NSMakeRect(boxRect.origin.x + titleHInset, + boxRect.origin.y + boxRect.size.height - titleSize.height - (titleVInset * 2.0), + titleSize.width + borderWidth, + titleSize.height); + titleRect.size.width = MIN(titleRect.size.width, boxRect.size.width - (2.0 * titleHInset)); + + if ([self selected]) { + [[NSColor alternateSelectedControlColor] set]; + // We use the alternate (darker) selectedControlColor since the regular one is too light. + // The alternate one is the highlight color for NSTableView, NSOutlineView, etc. + // This mimics how Automator highlights the selected action in a workflow. + } else { + [borderColor set]; + } + + + // Draw title background + //NSRectFill(titleRect); + [[self titlePathWithinRect:bgRect cornerRadius:radius titleRect:titleRect] fill]; + + + // Draw rounded rect around entire box + if (borderWidth > 0.0) { + [bgPath setLineWidth:borderWidth]; + [bgPath stroke]; + } + + + // Draw title text + [[self title] drawInRect:titleRect withAttributes:titleAttrs]; + //[[self titleCell] setTextColor:[self titleColor]]; + //[[self titleCell] drawWithFrame:titleRect inView:self]; +} + + +- (NSBezierPath *)titlePathWithinRect:(NSRect)rect cornerRadius:(float)radius titleRect:(NSRect)titleRect +{ + // Construct rounded rect path + + NSRect bgRect = rect; + int minX = NSMinX(bgRect); + int maxX = minX + titleRect.size.width + ((titleRect.origin.x - rect.origin.x) * 2.0); + int maxY = NSMaxY(bgRect); + int minY = NSMinY(titleRect) - (maxY - (titleRect.origin.y + titleRect.size.height)); + float titleExpansionThreshold = 20.0; + // i.e. if there's less than 20px space to the right of the short titlebar, just draw the full one. + + NSBezierPath *path = [NSBezierPath bezierPath]; + + [path moveToPoint:NSMakePoint(minX, minY)]; + + if (bgRect.size.width - titleRect.size.width >= titleExpansionThreshold && ![self drawsFullTitleBar]) { + // Draw a short titlebar + [path appendBezierPathWithArcFromPoint:NSMakePoint(maxX, minY) + toPoint:NSMakePoint(maxX, maxY) + radius:radius]; + [path lineToPoint:NSMakePoint(maxX, maxY)]; + } else { + // Draw full titlebar, since we're either set to always do so, or we don't have room for a short one. + [path lineToPoint:NSMakePoint(NSMaxX(bgRect), minY)]; + [path appendBezierPathWithArcFromPoint:NSMakePoint(NSMaxX(bgRect), maxY) + toPoint:NSMakePoint(NSMaxX(bgRect) - (bgRect.size.width / 2.0), maxY) + radius:radius]; + } + + [path appendBezierPathWithArcFromPoint:NSMakePoint(minX, maxY) + toPoint:NSMakePoint(minX, minY) + radius:radius]; + + [path closePath]; + + return path; +} + + +- (void)setTitle:(NSString *)newTitle +{ + [super setTitle:newTitle]; + [self setNeedsDisplay:YES]; +} + + +- (BOOL)drawsFullTitleBar +{ + return drawsFullTitleBar; +} + + +- (void)setDrawsFullTitleBar:(BOOL)newDrawsFullTitleBar +{ + drawsFullTitleBar = newDrawsFullTitleBar; + [self setNeedsDisplay:YES]; +} + + +- (BOOL)selected +{ + return selected; +} + + +- (void)setSelected:(BOOL)newSelected +{ + selected = newSelected; + [self setNeedsDisplay:YES]; +} + + +- (NSColor *)borderColor +{ + return borderColor; +} + + +- (void)setBorderColor:(NSColor *)newBorderColor +{ + [newBorderColor retain]; + [borderColor release]; + borderColor = newBorderColor; + [self setNeedsDisplay:YES]; +} + + +- (NSColor *)titleColor +{ + return titleColor; +} + + +- (void)setTitleColor:(NSColor *)newTitleColor +{ + [newTitleColor retain]; + [titleColor release]; + titleColor = newTitleColor; + + [titleAttrs setObject:titleColor forKey:NSForegroundColorAttributeName]; + + [self setNeedsDisplay:YES]; +} + + +- (NSColor *)gradientStartColor +{ + return gradientStartColor; +} + + +- (void)setGradientStartColor:(NSColor *)newGradientStartColor +{ + // Must ensure gradient colors are in NSCalibratedRGBColorSpace, or Core Image gets angry. + NSColor *newCalibratedGradientStartColor = [newGradientStartColor colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; + [newCalibratedGradientStartColor retain]; + [gradientStartColor release]; + gradientStartColor = newCalibratedGradientStartColor; + if ([self drawsGradientBackground]) { + [self setNeedsDisplay:YES]; + } +} + + +- (NSColor *)gradientEndColor +{ + return gradientEndColor; +} + + +- (void)setGradientEndColor:(NSColor *)newGradientEndColor +{ + // Must ensure gradient colors are in NSCalibratedRGBColorSpace, or Core Image gets angry. + NSColor *newCalibratedGradientEndColor = [newGradientEndColor colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; + [newCalibratedGradientEndColor retain]; + [gradientEndColor release]; + gradientEndColor = newCalibratedGradientEndColor; + if ([self drawsGradientBackground]) { + [self setNeedsDisplay:YES]; + } +} + + +- (NSColor *)backgroundColor +{ + return backgroundColor; +} + + +- (void)setBackgroundColor:(NSColor *)newBackgroundColor +{ + [newBackgroundColor retain]; + [backgroundColor release]; + backgroundColor = newBackgroundColor; + if (![self drawsGradientBackground]) { + [self setNeedsDisplay:YES]; + } +} + + +- (BOOL)drawsGradientBackground +{ + return drawsGradientBackground; +} + + +- (void)setDrawsGradientBackground:(BOOL)newDrawsGradientBackground +{ + drawsGradientBackground = newDrawsGradientBackground; + [self setNeedsDisplay:YES]; +} + + +@end Index: branches/diebuche/ChameleonPrefPane/Sources/CTGradient.h =================================================================== --- branches/diebuche/ChameleonPrefPane/Sources/CTGradient.h (revision 0) +++ branches/diebuche/ChameleonPrefPane/Sources/CTGradient.h (revision 80) @@ -0,0 +1,72 @@ +// +// CTGradient.h +// +// Created by Chad Weider on 2/14/07. +// Copyright (c) 2007 Chad Weider. +// Some rights reserved: +// +// Version: 1.6 + +#import + +typedef struct _CTGradientElement +{ + float red, green, blue, alpha; + float position; + + struct _CTGradientElement *nextElement; +} CTGradientElement; + +typedef enum _CTBlendingMode +{ + CTLinearBlendingMode, + CTChromaticBlendingMode, + CTInverseChromaticBlendingMode +} CTGradientBlendingMode; + + +@interface CTGradient : NSObject +{ + CTGradientElement* elementList; + CTGradientBlendingMode blendingMode; + + CGFunctionRef gradientFunction; +} + ++ (id)gradientWithBeginningColor:(NSColor *)begin endingColor:(NSColor *)end; + ++ (id)aquaSelectedGradient; ++ (id)aquaNormalGradient; ++ (id)aquaPressedGradient; + ++ (id)unifiedSelectedGradient; ++ (id)unifiedNormalGradient; ++ (id)unifiedPressedGradient; ++ (id)unifiedDarkGradient; + ++ (id)sourceListSelectedGradient; ++ (id)sourceListUnselectedGradient; + ++ (id)rainbowGradient; ++ (id)hydrogenSpectrumGradient; + +- (CTGradient *)gradientWithAlphaComponent:(float)alpha; + +- (CTGradient *)addColorStop:(NSColor *)color atPosition:(float)position; //positions given relative to [0,1] +- (CTGradient *)removeColorStopAtIndex:(unsigned)index; +- (CTGradient *)removeColorStopAtPosition:(float)position; + +- (CTGradientBlendingMode)blendingMode; +- (NSColor *)colorStopAtIndex:(unsigned)index; +- (NSColor *)colorAtPosition:(float)position; + + +- (void)drawSwatchInRect:(NSRect)rect; +- (void)fillRect:(NSRect)rect angle:(float)angle; //fills rect with axial gradient +// angle in degrees +- (void)radialFillRect:(NSRect)rect; //fills rect with radial gradient +// gradient from center outwards +- (void)fillBezierPath:(NSBezierPath *)path angle:(float)angle; +- (void)radialFillBezierPath:(NSBezierPath *)path; + +@end \ No newline at end of file Index: branches/diebuche/ChameleonPrefPane/Sources/CTGradient.m =================================================================== --- branches/diebuche/ChameleonPrefPane/Sources/CTGradient.m (revision 0) +++ branches/diebuche/ChameleonPrefPane/Sources/CTGradient.m (revision 80) @@ -0,0 +1,1308 @@ +// +// CTGradient.m +// +// Created by Chad Weider on 2/14/07. +// Copyright (c) 2007 Chad Weider. +// Some rights reserved: +// +// Version: 1.6 + +#import "CTGradient.h" + +@interface CTGradient (Private) +- (void)_commonInit; +- (void)setBlendingMode:(CTGradientBlendingMode)mode; +- (void)addElement:(CTGradientElement*)newElement; + +- (CTGradientElement *)elementAtIndex:(unsigned)index; + +- (CTGradientElement)removeElementAtIndex:(unsigned)index; +- (CTGradientElement)removeElementAtPosition:(float)position; +@end + +//C Fuctions for color blending +static void linearEvaluation (void *info, const float *in, float *out); +static void chromaticEvaluation(void *info, const float *in, float *out); +static void inverseChromaticEvaluation(void *info, const float *in, float *out); +static void transformRGB_HSV(float *components); +static void transformHSV_RGB(float *components); +static void resolveHSV(float *color1, float *color2); + + +@implementation CTGradient +/////////////////////////////////////Initialization Type Stuff +- (id)init +{ + self = [super init]; + + if (self != nil) + { + [self _commonInit]; + [self setBlendingMode:CTLinearBlendingMode]; + } + return self; +} + +- (void)_commonInit +{ + elementList = nil; +} + +- (void)dealloc +{ + CGFunctionRelease(gradientFunction); + + CTGradientElement *elementToRemove = elementList; + while(elementList != nil) + { + elementToRemove = elementList; + elementList = elementList->nextElement; + free(elementToRemove); + } + + [super dealloc]; +} + +- (id)copyWithZone:(NSZone *)zone +{ + CTGradient *copy = [[[self class] allocWithZone:zone] init]; + + //now just copy my elementlist + CTGradientElement *currentElement = elementList; + while(currentElement != nil) + { + [copy addElement:currentElement]; + currentElement = currentElement->nextElement; + } + + [copy setBlendingMode:blendingMode]; + + return copy; +} + +- (void)encodeWithCoder:(NSCoder *)coder +{ + if([coder allowsKeyedCoding]) + { + unsigned count = 0; + CTGradientElement *currentElement = elementList; + while(currentElement != nil) + { + [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->red)]; + [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->green)]; + [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->blue)]; + [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->alpha)]; + [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->position)]; + + count++; + currentElement = currentElement->nextElement; + } + [coder encodeInt:count forKey:@"CTGradientElementCount"]; + [coder encodeInt:blendingMode forKey:@"CTGradientBlendingMode"]; + } + else + [NSException raise:NSInvalidArchiveOperationException format:@"Only supports NSKeyedArchiver coders"]; +} + +- (id)initWithCoder:(NSCoder *)coder +{ + [self _commonInit]; + + [self setBlendingMode:[coder decodeIntForKey:@"CTGradientBlendingMode"]]; + unsigned count = [coder decodeIntForKey:@"CTGradientElementCount"]; + + while(count != 0) + { + CTGradientElement newElement; + + [coder decodeValueOfObjCType:@encode(float) at:&(newElement.red)]; + [coder decodeValueOfObjCType:@encode(float) at:&(newElement.green)]; + [coder decodeValueOfObjCType:@encode(float) at:&(newElement.blue)]; + [coder decodeValueOfObjCType:@encode(float) at:&(newElement.alpha)]; + [coder decodeValueOfObjCType:@encode(float) at:&(newElement.position)]; + + count--; + [self addElement:&newElement]; + } + return self; +} +#pragma mark - + + + +#pragma mark Creation ++ (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]; +} + + ++ (id)dividerGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = color1.green = color1.blue = 0.84; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = color2.green = color2.blue = 0.90; + color2.alpha = 1.00; + color2.position = 0.5; + + CTGradientElement color3; + color3.red = color3.green = color3.blue = 0.84; + color3.alpha = 1.00; + color3.position = 1.0; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + [newInstance addElement:&color3]; + + return [newInstance autorelease]; +} + + ++ (id)statusBarGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = color1.green = color1.blue = 0.872; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = color2.green = color2.blue = 0.77; + color2.alpha = 1.00; + color2.position = 0.618; + + CTGradientElement color3; + color3.red = color3.green = color3.blue = 0.74; + color3.alpha = 1.00; + color3.position = 1.0; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + [newInstance addElement:&color3]; + + return [newInstance autorelease]; +} + + ++ (id)aquaSelectedGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = 0.58; + color1.green = 0.86; + color1.blue = 0.98; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = 0.42; + color2.green = 0.68; + color2.blue = 0.90; + color2.alpha = 1.00; + color2.position = 11.5/23; + + CTGradientElement color3; + color3.red = 0.64; + color3.green = 0.80; + color3.blue = 0.94; + color3.alpha = 1.00; + color3.position = 11.5/23; + + CTGradientElement color4; + color4.red = 0.56; + color4.green = 0.70; + color4.blue = 0.90; + color4.alpha = 1.00; + color4.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + [newInstance addElement:&color3]; + [newInstance addElement:&color4]; + + return [newInstance autorelease]; +} + ++ (id)aquaNormalGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = color1.green = color1.blue = 0.95; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = color2.green = color2.blue = 0.83; + color2.alpha = 1.00; + color2.position = 11.5/23; + + CTGradientElement color3; + color3.red = color3.green = color3.blue = 0.95; + color3.alpha = 1.00; + color3.position = 11.5/23; + + CTGradientElement color4; + color4.red = color4.green = color4.blue = 0.92; + color4.alpha = 1.00; + color4.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + [newInstance addElement:&color3]; + [newInstance addElement:&color4]; + + return [newInstance autorelease]; +} + ++ (id)aquaPressedGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = color1.green = color1.blue = 0.80; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = color2.green = color2.blue = 0.64; + color2.alpha = 1.00; + color2.position = 11.5/23; + + CTGradientElement color3; + color3.red = color3.green = color3.blue = 0.80; + color3.alpha = 1.00; + color3.position = 11.5/23; + + CTGradientElement color4; + color4.red = color4.green = color4.blue = 0.77; + color4.alpha = 1.00; + color4.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + [newInstance addElement:&color3]; + [newInstance addElement:&color4]; + + return [newInstance autorelease]; +} + ++ (id)unifiedSelectedGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = color1.green = color1.blue = 0.85; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = color2.green = color2.blue = 0.95; + color2.alpha = 1.00; + color2.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + + return [newInstance autorelease]; +} + ++ (id)unifiedNormalGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = color1.green = color1.blue = 0.75; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = color2.green = color2.blue = 0.90; + color2.alpha = 1.00; + color2.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + + return [newInstance autorelease]; +} + ++ (id)unifiedPressedGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = color1.green = color1.blue = 0.60; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = color2.green = color2.blue = 0.75; + color2.alpha = 1.00; + color2.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + + return [newInstance autorelease]; +} + ++ (id)unifiedDarkGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = color1.green = color1.blue = 0.68; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = color2.green = color2.blue = 0.83; + color2.alpha = 1.00; + color2.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + + return [newInstance autorelease]; +} + ++ (id)sourceListSelectedGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = 0.06; + color1.green = 0.37; + color1.blue = 0.85; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = 0.30; + color2.green = 0.60; + color2.blue = 0.92; + color2.alpha = 1.00; + color2.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + + return [newInstance autorelease]; +} + ++ (id)sourceListUnselectedGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = 0.43; + color1.green = 0.43; + color1.blue = 0.43; + color1.alpha = 1.00; + color1.position = 0; + + CTGradientElement color2; + color2.red = 0.60; + color2.green = 0.60; + color2.blue = 0.60; + color2.alpha = 1.00; + color2.position = 1; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + + return [newInstance autorelease]; +} + ++ (id)rainbowGradient +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement color1; + color1.red = 1.00; + color1.green = 0.00; + color1.blue = 0.00; + color1.alpha = 1.00; + color1.position = 0.0; + + CTGradientElement color2; + color2.red = 0.54; + color2.green = 0.00; + color2.blue = 1.00; + color2.alpha = 1.00; + color2.position = 1.0; + + [newInstance addElement:&color1]; + [newInstance addElement:&color2]; + + [newInstance setBlendingMode:CTChromaticBlendingMode]; + + return [newInstance autorelease]; +} + ++ (id)hydrogenSpectrumGradient +{ + id newInstance = [[[self class] alloc] init]; + + struct {float hue; float position; float width;} colorBands[4]; + + colorBands[0].hue = 22; + colorBands[0].position = .145; + colorBands[0].width = .01; + + colorBands[1].hue = 200; + colorBands[1].position = .71; + colorBands[1].width = .008; + + colorBands[2].hue = 253; + colorBands[2].position = .885; + colorBands[2].width = .005; + + colorBands[3].hue = 275; + colorBands[3].position = .965; + colorBands[3].width = .003; + + int i; + ///////////////////////////// + for(i = 0; i < 4; i++) + { + float color[4]; + color[0] = colorBands[i].hue - 180*colorBands[i].width; + color[1] = 1; + color[2] = 0.001; + color[3] = 1; + transformHSV_RGB(color); + CTGradientElement fadeIn; + fadeIn.red = color[0]; + fadeIn.green = color[1]; + fadeIn.blue = color[2]; + fadeIn.alpha = color[3]; + fadeIn.position = colorBands[i].position - colorBands[i].width; + + + color[0] = colorBands[i].hue; + color[1] = 1; + color[2] = 1; + color[3] = 1; + transformHSV_RGB(color); + CTGradientElement band; + band.red = color[0]; + band.green = color[1]; + band.blue = color[2]; + band.alpha = color[3]; + band.position = colorBands[i].position; + + color[0] = colorBands[i].hue + 180*colorBands[i].width; + color[1] = 1; + color[2] = 0.001; + color[3] = 1; + transformHSV_RGB(color); + CTGradientElement fadeOut; + fadeOut.red = color[0]; + fadeOut.green = color[1]; + fadeOut.blue = color[2]; + fadeOut.alpha = color[3]; + fadeOut.position = colorBands[i].position + colorBands[i].width; + + + [newInstance addElement:&fadeIn]; + [newInstance addElement:&band]; + [newInstance addElement:&fadeOut]; + } + + [newInstance setBlendingMode:CTChromaticBlendingMode]; + + return [newInstance autorelease]; +} + +#pragma mark - + + + +#pragma mark Modification +- (CTGradient *)gradientWithAlphaComponent:(float)alpha +{ + id newInstance = [[[self class] alloc] init]; + + CTGradientElement *curElement = elementList; + CTGradientElement tempElement; + + while(curElement != nil) + { + tempElement = *curElement; + tempElement.alpha = alpha; + [newInstance addElement:&tempElement]; + + curElement = curElement->nextElement; + } + + return [newInstance autorelease]; +} + +- (CTGradient *)gradientWithBlendingMode:(CTGradientBlendingMode)mode +{ + CTGradient *newGradient = [self copy]; + + [newGradient setBlendingMode:mode]; + + return [newGradient autorelease]; +} + + +//Adds a color stop with at in elementList +//(if two elements are at the same position then added imediatly after the one that was there already) +- (CTGradient *)addColorStop:(NSColor *)color atPosition:(float)position +{ + CTGradient *newGradient = [self copy]; + CTGradientElement newGradientElement; + + //put the components of color into the newGradientElement - must make sure it is a RGB color (not Gray or CMYK) + [[color colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&newGradientElement.red + green:&newGradientElement.green + blue:&newGradientElement.blue + alpha:&newGradientElement.alpha]; + newGradientElement.position = position; + + //Pass it off to addElement to take care of adding it to the elementList + [newGradient addElement:&newGradientElement]; + + return [newGradient autorelease]; +} + + +//Removes the color stop at from elementList +- (CTGradient *)removeColorStopAtPosition:(float)position +{ + CTGradient *newGradient = [self copy]; + CTGradientElement removedElement = [newGradient removeElementAtPosition:position]; + + if(isnan(removedElement.position)) + [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtPosition:]: no such colorStop at position (%f)", [self class], position]; + + return [newGradient autorelease]; +} + +- (CTGradient *)removeColorStopAtIndex:(unsigned)index +{ + CTGradient *newGradient = [self copy]; + CTGradientElement removedElement = [newGradient removeElementAtIndex:index]; + + if(isnan(removedElement.position)) + [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtIndex:]: index (%i) beyond bounds", [self class], index]; + + return [newGradient autorelease]; +} +#pragma mark - + + + +#pragma mark Information +- (CTGradientBlendingMode)blendingMode +{ + return blendingMode; +} + +//Returns color at in gradient +- (NSColor *)colorStopAtIndex:(unsigned)index +{ + CTGradientElement *element = [self elementAtIndex:index]; + + if(element != nil) + return [NSColor colorWithCalibratedRed:element->red + green:element->green + blue:element->blue + alpha:element->alpha]; + + [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtIndex:]: index (%i) beyond bounds", [self class], index]; + + return nil; +} + +- (NSColor *)colorAtPosition:(float)position +{ + float components[4]; + + switch(blendingMode) + { + case CTLinearBlendingMode: + linearEvaluation(&elementList, &position, components); break; + case CTChromaticBlendingMode: + chromaticEvaluation(&elementList, &position, components); break; + case CTInverseChromaticBlendingMode: + inverseChromaticEvaluation(&elementList, &position, components); break; + } + + + return [NSColor colorWithCalibratedRed:components[0] + green:components[1] + blue:components[2] + alpha:components[3]]; +} +#pragma mark - + + + +#pragma mark Drawing +- (void)drawSwatchInRect:(NSRect)rect +{ + [self fillRect:rect angle:45]; +} + +- (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)radialFillRect:(NSRect)rect +{ + CGPoint startPoint , endPoint; + float startRadius, endRadius; + float scalex, scaley, transx, transy; + + startPoint = endPoint = CGPointMake(NSMidX(rect), NSMidY(rect)); + + startRadius = -1; + if(NSHeight(rect)>NSWidth(rect)) + { + scalex = NSWidth(rect)/NSHeight(rect); + transx = (NSHeight(rect)-NSWidth(rect))/2; + scaley = 1; + transy = 1; + endRadius = NSHeight(rect)/2; + } + else + { + scalex = 1; + transx = 1; + scaley = NSHeight(rect)/NSWidth(rect); + transy = (NSWidth(rect)-NSHeight(rect))/2; + endRadius = NSWidth(rect)/2; + } + + //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 = CGShadingCreateRadial(colorspace, startPoint, startRadius, endPoint, endRadius, gradientFunction, true, true); + + CGContextClipToRect (currentContext, *(CGRect *)&rect); + CGContextScaleCTM (currentContext, scalex, scaley); + CGContextTranslateCTM(currentContext, transx, transy); + CGContextDrawShading (currentContext, myCGShading); //This is where the action happens + + CGShadingRelease(myCGShading); + CGColorSpaceRelease(colorspace); + CGContextRestoreGState(currentContext); +} + +- (void)fillBezierPath:(NSBezierPath *)path angle:(float)angle +{ + NSGraphicsContext *currentContext = [NSGraphicsContext currentContext]; + [currentContext saveGraphicsState]; + NSAffineTransform *transform = [[NSAffineTransform alloc] init]; + + [transform rotateByDegrees:-angle]; + [path transformUsingAffineTransform:transform]; + [transform invert]; + [transform concat]; + + [path addClip]; + [self fillRect:[path bounds] angle:0]; + [path transformUsingAffineTransform:transform]; + [transform release]; + [currentContext restoreGraphicsState]; +} +- (void)radialFillBezierPath:(NSBezierPath *)path +{ + NSGraphicsContext *currentContext = [NSGraphicsContext currentContext]; + [currentContext saveGraphicsState]; + [path addClip]; + [self radialFillRect:[path bounds]]; + [currentContext restoreGraphicsState]; +} +#pragma mark - + + + +#pragma mark Private Methods +- (void)setBlendingMode:(CTGradientBlendingMode)mode; +{ + blendingMode = mode; + + //Choose what blending function to use + void *evaluationFunction; + switch(blendingMode) + { + case CTLinearBlendingMode: + evaluationFunction = &linearEvaluation; break; + case CTChromaticBlendingMode: + evaluationFunction = &chromaticEvaluation; break; + case CTInverseChromaticBlendingMode: + evaluationFunction = &inverseChromaticEvaluation; break; + } + + //replace the current CoreGraphics Function with new one + if(gradientFunction != NULL) + CGFunctionRelease(gradientFunction); + + CGFunctionCallbacks evaluationCallbackInfo = {0 , evaluationFunction, 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 +} + +- (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; + } +} + +- (CTGradientElement)removeElementAtIndex:(unsigned)index +{ + CTGradientElement removedElement; + + if(elementList != nil) + { + if(index == 0) + { + CTGradientElement *tmpNext = elementList; + elementList = elementList->nextElement; + + removedElement = *tmpNext; + free(tmpNext); + + return removedElement; + } + + unsigned count = 1; //we want to start one ahead + CTGradientElement *currentElement = elementList; + while(currentElement->nextElement != nil) + { + if(count == index) + { + CTGradientElement *tmpNext = currentElement->nextElement; + currentElement->nextElement = currentElement->nextElement->nextElement; + + removedElement = *tmpNext; + free(tmpNext); + + return removedElement; + } + + count++; + currentElement = currentElement->nextElement; + } + } + + //element is not found, return empty element + removedElement.red = 0.0; + removedElement.green = 0.0; + removedElement.blue = 0.0; + removedElement.alpha = 0.0; + removedElement.position = NAN; + removedElement.nextElement = nil; + + return removedElement; +} + +- (CTGradientElement)removeElementAtPosition:(float)position +{ + CTGradientElement removedElement; + + if(elementList != nil) + { + if(elementList->position == position) + { + CTGradientElement *tmpNext = elementList; + elementList = elementList->nextElement; + + removedElement = *tmpNext; + free(tmpNext); + + return removedElement; + } + else + { + CTGradientElement *curElement = elementList; + while(curElement->nextElement != nil) + { + if(curElement->nextElement->position == position) + { + CTGradientElement *tmpNext = curElement->nextElement; + curElement->nextElement = curElement->nextElement->nextElement; + + removedElement = *tmpNext; + free(tmpNext); + + return removedElement; + } + } + } + } + + //element is not found, return empty element + removedElement.red = 0.0; + removedElement.green = 0.0; + removedElement.blue = 0.0; + removedElement.alpha = 0.0; + removedElement.position = NAN; + removedElement.nextElement = nil; + + return removedElement; +} + + +- (CTGradientElement *)elementAtIndex:(unsigned)index; +{ + unsigned count = 0; + CTGradientElement *currentElement = elementList; + + while(currentElement != nil) + { + if(count == index) + return currentElement; + + count++; + currentElement = currentElement->nextElement; + } + + return nil; +} +#pragma mark - + + + +#pragma mark Core Graphics +//////////////////////////////////////Blending Functions///////////////////////////////////// +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; + } +} + + + + +//Chromatic Evaluation - +// This blends colors by their Hue, Saturation, and Value(Brightness) right now I just +// transform the RGB values stored in the CTGradientElements to HSB, in the future I may +// streamline it to avoid transforming in and out of HSB colorspace *for later* +// +// For the chromatic blend we shift the hue of color1 to meet the hue of color2. To do +// this we will add to the hue's angle (if we subtract we'll be doing the inverse +// chromatic...scroll down more for that). All we need to do is keep adding to the hue +// until we wrap around the colorwheel and get to color2. +void chromaticEvaluation(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; + + float c1[4]; + float c2[4]; + + //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; + } + + + c1[0] = color1->red; + c1[1] = color1->green; + c1[2] = color1->blue; + c1[3] = color1->alpha; + + c2[0] = color2->red; + c2[1] = color2->green; + c2[2] = color2->blue; + c2[3] = color2->alpha; + + transformRGB_HSV(c1); + transformRGB_HSV(c2); + resolveHSV(c1,c2); + + if(c1[0] > c2[0]) //if color1's hue is higher than color2's hue then + c2[0] += 360; // we need to move c2 one revolution around the wheel + + + if(position <= color1->position) //Make all below color color1's position equal to color1 + { + out[0] = c1[0]; + out[1] = c1[1]; + out[2] = c1[2]; + out[3] = c1[3]; + } + else if (position >= color2->position) //Make all above color color2's position equal to color2 + { + out[0] = c2[0]; + out[1] = c2[1]; + out[2] = c2[2]; + out[3] = c2[3]; + } + 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] = (c2[0] - c1[0])*position + c1[0]; + out[1] = (c2[1] - c1[1])*position + c1[1]; + out[2] = (c2[2] - c1[2])*position + c1[2]; + out[3] = (c2[3] - c1[3])*position + c1[3]; + } + + transformHSV_RGB(out); +} + + + +//Inverse Chromatic Evaluation - +// Inverse Chromatic is about the same story as Chromatic Blend, but here the Hue +// is strictly decreasing, that is we need to get from color1 to color2 by decreasing +// the 'angle' (i.e. 90å¼ -> 180å¼ would be done by subtracting 270å¼ and getting -180å¼... +// which is equivalent to 180å¼ mod 360å¼ +void inverseChromaticEvaluation(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; + + float c1[4]; + float c2[4]; + + //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; + } + + c1[0] = color1->red; + c1[1] = color1->green; + c1[2] = color1->blue; + c1[3] = color1->alpha; + + c2[0] = color2->red; + c2[1] = color2->green; + c2[2] = color2->blue; + c2[3] = color2->alpha; + + transformRGB_HSV(c1); + transformRGB_HSV(c2); + resolveHSV(c1,c2); + + if(c1[0] < c2[0]) //if color1's hue is higher than color2's hue then + c1[0] += 360; // we need to move c2 one revolution back on the wheel + + + if(position <= color1->position) //Make all below color color1's position equal to color1 + { + out[0] = c1[0]; + out[1] = c1[1]; + out[2] = c1[2]; + out[3] = c1[3]; + } + else if (position >= color2->position) //Make all above color color2's position equal to color2 + { + out[0] = c2[0]; + out[1] = c2[1]; + out[2] = c2[2]; + out[3] = c2[3]; + } + 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] = (c2[0] - c1[0])*position + c1[0]; + out[1] = (c2[1] - c1[1])*position + c1[1]; + out[2] = (c2[2] - c1[2])*position + c1[2]; + out[3] = (c2[3] - c1[3])*position + c1[3]; + } + + transformHSV_RGB(out); +} + + + +void transformRGB_HSV(float *components) //H,S,B -> R,G,B +{ + float H, S, V; + float R = components[0], + G = components[1], + B = components[2]; + + float MAX = R > G ? (R > B ? R : B) : (G > B ? G : B), + MIN = R < G ? (R < B ? R : B) : (G < B ? G : B); + + if(MAX == MIN) + H = NAN; + else if(MAX == R) + if(G >= B) + H = 60*(G-B)/(MAX-MIN)+0; + else + H = 60*(G-B)/(MAX-MIN)+360; + else if(MAX == G) + H = 60*(B-R)/(MAX-MIN)+120; + else if(MAX == B) + H = 60*(R-G)/(MAX-MIN)+240; + + S = MAX == 0 ? 0 : 1 - MIN/MAX; + V = MAX; + + components[0] = H; + components[1] = S; + components[2] = V; +} + +void transformHSV_RGB(float *components) //H,S,B -> R,G,B +{ + float R, G, B; + float H = fmodf(components[0],359), //map to [0,360) + S = components[1], + V = components[2]; + + int Hi = (int)floorf(H/60.) % 6; + float f = H/60-Hi, + p = V*(1-S), + q = V*(1-f*S), + t = V*(1-(1-f)*S); + + switch (Hi) + { + case 0: R=V;G=t;B=p; break; + case 1: R=q;G=V;B=p; break; + case 2: R=p;G=V;B=t; break; + case 3: R=p;G=q;B=V; break; + case 4: R=t;G=p;B=V; break; + case 5: R=V;G=p;B=q; break; + } + + components[0] = R; + components[1] = G; + components[2] = B; +} + +void resolveHSV(float *color1, float *color2) //H value may be undefined (i.e. graycale color) +{ // we want to fill it with a sensible value + if(isnan(color1[0]) && isnan(color2[0])) + color1[0] = color2[0] = 0; + else if(isnan(color1[0])) + color1[0] = color2[0]; + else if(isnan(color2[0])) + color2[0] = color1[0]; +} + +@end \ No newline at end of file Index: branches/diebuche/ChameleonPrefPane/ChameleonPrefPane.xcodeproj/project.pbxproj =================================================================== --- branches/diebuche/ChameleonPrefPane/ChameleonPrefPane.xcodeproj/project.pbxproj (revision 79) +++ branches/diebuche/ChameleonPrefPane/ChameleonPrefPane.xcodeproj/project.pbxproj (revision 80) @@ -64,14 +64,14 @@ B3981D8411132A13009E2520 /* string_util.h in Headers */ = {isa = PBXBuildFile; fileRef = B3981D8311132A13009E2520 /* string_util.h */; }; B3981E4211132AE0009E2520 /* cham.png in Resources */ = {isa = PBXBuildFile; fileRef = B3981E4111132AE0009E2520 /* cham.png */; }; B3981E4411132AEC009E2520 /* background.png in Resources */ = {isa = PBXBuildFile; fileRef = B3981E4311132AEC009E2520 /* background.png */; }; - B3981F2B11132EC0009E2520 /* CTGradient.h in Headers */ = {isa = PBXBuildFile; fileRef = B3981F2911132EC0009E2520 /* CTGradient.h */; }; B3981F2C11132EC0009E2520 /* CTGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = B3981F2A11132EC0009E2520 /* CTGradient.m */; }; - B3981F3211132EE8009E2520 /* RoundedBox.h in Headers */ = {isa = PBXBuildFile; fileRef = B3981F2F11132EE8009E2520 /* RoundedBox.h */; }; - B3981F3311132EE8009E2520 /* RoundedBox.m in Sources */ = {isa = PBXBuildFile; fileRef = B3981F3011132EE8009E2520 /* RoundedBox.m */; }; - B3981F3411132EE8009E2520 /* RoundedBox_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = B3981F3111132EE8009E2520 /* RoundedBox_Prefix.pch */; }; B3981F48111332FB009E2520 /* name.png in Resources */ = {isa = PBXBuildFile; fileRef = B3981F47111332FB009E2520 /* name.png */; }; B3981F7D1113376C009E2520 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3981F7C1113376C009E2520 /* QuartzCore.framework */; }; B3981FB811133775009E2520 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3981FB711133775009E2520 /* CoreData.framework */; }; + B398220C111349F5009E2520 /* RoundedBox_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = B3982209111349F5009E2520 /* RoundedBox_Prefix.pch */; }; + B398220D111349F5009E2520 /* RoundedBox.h in Headers */ = {isa = PBXBuildFile; fileRef = B398220A111349F5009E2520 /* RoundedBox.h */; }; + B398220E111349F5009E2520 /* RoundedBox.m in Sources */ = {isa = PBXBuildFile; fileRef = B398220B111349F5009E2520 /* RoundedBox.m */; }; + B398221111134A33009E2520 /* CTGradient.h in Headers */ = {isa = PBXBuildFile; fileRef = B398220F11134A33009E2520 /* CTGradient.h */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -134,14 +134,14 @@ B3981D8311132A13009E2520 /* string_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = string_util.h; path = Sources/string_util.h; sourceTree = ""; }; B3981E4111132AE0009E2520 /* cham.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = cham.png; path = Resources/cham.png; sourceTree = ""; }; B3981E4311132AEC009E2520 /* background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = background.png; path = Resources/background.png; sourceTree = ""; }; - B3981F2911132EC0009E2520 /* CTGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CTGradient.h; sourceTree = ""; }; - B3981F2A11132EC0009E2520 /* CTGradient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CTGradient.m; sourceTree = ""; }; - B3981F2F11132EE8009E2520 /* RoundedBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RoundedBox.h; path = /Volumes/Azazel/Downloads/RoundedBox/Source/RoundedBox.h; sourceTree = ""; }; - B3981F3011132EE8009E2520 /* RoundedBox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RoundedBox.m; path = /Volumes/Azazel/Downloads/RoundedBox/Source/RoundedBox.m; sourceTree = ""; }; - B3981F3111132EE8009E2520 /* RoundedBox_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RoundedBox_Prefix.pch; path = /Volumes/Azazel/Downloads/RoundedBox/Source/RoundedBox_Prefix.pch; sourceTree = ""; }; + B3981F2A11132EC0009E2520 /* CTGradient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CTGradient.m; path = Sources/CTGradient.m; sourceTree = ""; }; B3981F47111332FB009E2520 /* name.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = name.png; path = Resources/name.png; sourceTree = ""; }; B3981F7C1113376C009E2520 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = /System/Library/Frameworks/QuartzCore.framework; sourceTree = ""; }; B3981FB711133775009E2520 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; + B3982209111349F5009E2520 /* RoundedBox_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RoundedBox_Prefix.pch; path = Sources/RoundedBox_Prefix.pch; sourceTree = ""; }; + B398220A111349F5009E2520 /* RoundedBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RoundedBox.h; path = Sources/RoundedBox.h; sourceTree = ""; }; + B398220B111349F5009E2520 /* RoundedBox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RoundedBox.m; path = Sources/RoundedBox.m; sourceTree = ""; }; + B398220F11134A33009E2520 /* CTGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CTGradient.h; path = Sources/CTGradient.h; sourceTree = ""; }; F506C035013D953901CA16C8 /* PreferencePanes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PreferencePanes.framework; path = /System/Library/Frameworks/PreferencePanes.framework; sourceTree = ""; }; F506C043013D9D8C01CA16C8 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/Chameleon.xib; sourceTree = ""; }; /* End PBXFileReference section */ @@ -319,11 +319,11 @@ B3981F2E11132ED5009E2520 /* RoundedBox */ = { isa = PBXGroup; children = ( - B3981F2911132EC0009E2520 /* CTGradient.h */, + B3982209111349F5009E2520 /* RoundedBox_Prefix.pch */, + B398220A111349F5009E2520 /* RoundedBox.h */, + B398220B111349F5009E2520 /* RoundedBox.m */, + B398220F11134A33009E2520 /* CTGradient.h */, B3981F2A11132EC0009E2520 /* CTGradient.m */, - B3981F2F11132EE8009E2520 /* RoundedBox.h */, - B3981F3011132EE8009E2520 /* RoundedBox.m */, - B3981F3111132EE8009E2520 /* RoundedBox_Prefix.pch */, ); name = RoundedBox; sourceTree = ""; @@ -353,9 +353,9 @@ 01A25D4A111108C80024EA7E /* SmbiosController.h in Headers */, B3981D401113292A009E2520 /* CustomTableView.h in Headers */, B3981D8411132A13009E2520 /* string_util.h in Headers */, - B3981F2B11132EC0009E2520 /* CTGradient.h in Headers */, - B3981F3211132EE8009E2520 /* RoundedBox.h in Headers */, - B3981F3411132EE8009E2520 /* RoundedBox_Prefix.pch in Headers */, + B398220C111349F5009E2520 /* RoundedBox_Prefix.pch in Headers */, + B398220D111349F5009E2520 /* RoundedBox.h in Headers */, + B398221111134A33009E2520 /* CTGradient.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -461,7 +461,7 @@ 01A25D4B111108C80024EA7E /* SmbiosController.mm in Sources */, 01A25D4C111108C80024EA7E /* string_util.cpp in Sources */, B3981F2C11132EC0009E2520 /* CTGradient.m in Sources */, - B3981F3311132EE8009E2520 /* RoundedBox.m in Sources */, + B398220E111349F5009E2520 /* RoundedBox.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; Index: branches/diebuche/ChameleonPrefPane/CTGradient.h =================================================================== --- branches/diebuche/ChameleonPrefPane/CTGradient.h (revision 0) +++ branches/diebuche/ChameleonPrefPane/CTGradient.h (revision 80) @@ -0,0 +1,72 @@ +// +// CTGradient.h +// +// Created by Chad Weider on 2/14/07. +// Copyright (c) 2007 Chad Weider. +// Some rights reserved: +// +// Version: 1.6 + +#import + +typedef struct _CTGradientElement +{ + float red, green, blue, alpha; + float position; + + struct _CTGradientElement *nextElement; +} CTGradientElement; + +typedef enum _CTBlendingMode +{ + CTLinearBlendingMode, + CTChromaticBlendingMode, + CTInverseChromaticBlendingMode +} CTGradientBlendingMode; + + +@interface CTGradient : NSObject +{ + CTGradientElement* elementList; + CTGradientBlendingMode blendingMode; + + CGFunctionRef gradientFunction; +} + ++ (id)gradientWithBeginningColor:(NSColor *)begin endingColor:(NSColor *)end; + ++ (id)aquaSelectedGradient; ++ (id)aquaNormalGradient; ++ (id)aquaPressedGradient; + ++ (id)unifiedSelectedGradient; ++ (id)unifiedNormalGradient; ++ (id)unifiedPressedGradient; ++ (id)unifiedDarkGradient; + ++ (id)sourceListSelectedGradient; ++ (id)sourceListUnselectedGradient; + ++ (id)rainbowGradient; ++ (id)hydrogenSpectrumGradient; + +- (CTGradient *)gradientWithAlphaComponent:(float)alpha; + +- (CTGradient *)addColorStop:(NSColor *)color atPosition:(float)position; //positions given relative to [0,1] +- (CTGradient *)removeColorStopAtIndex:(unsigned)index; +- (CTGradient *)removeColorStopAtPosition:(float)position; + +- (CTGradientBlendingMode)blendingMode; +- (NSColor *)colorStopAtIndex:(unsigned)index; +- (NSColor *)colorAtPosition:(float)position; + + +- (void)drawSwatchInRect:(NSRect)rect; +- (void)fillRect:(NSRect)rect angle:(float)angle; //fills rect with axial gradient +// angle in degrees +- (void)radialFillRect:(NSRect)rect; //fills rect with radial gradient +// gradient from center outwards +- (void)fillBezierPath:(NSBezierPath *)path angle:(float)angle; +- (void)radialFillBezierPath:(NSBezierPath *)path; + +@end \ No newline at end of file Index: branches/diebuche/ChameleonPrefPane/Resources/background.png =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: branches/diebuche/ChameleonPrefPane/Resources/background.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Index: branches/diebuche/ChameleonPrefPane/Resources/name.png =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: branches/diebuche/ChameleonPrefPane/Resources/name.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Index: branches/diebuche/ChameleonPrefPane/Resources/cham.png =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: branches/diebuche/ChameleonPrefPane/Resources/cham.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream