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