4 // Created by Chad Weider on 2/14/07.
5 // Writtin by Chad Weider.
7 // Released into public domain on 4/10/08.
11 #import "CTGradient.h"
13 @interface CTGradient (Private)
15 - (void)setBlendingMode:(CTGradientBlendingMode)mode;
16 - (void)addElement:(CTGradientElement*)newElement;
18 - (CTGradientElement *)elementAtIndex:(unsigned)index;
20 - (CTGradientElement)removeElementAtIndex:(unsigned)index;
21 - (CTGradientElement)removeElementAtPosition:(float)position;
24 //C Fuctions for color blending
25 static void linearEvaluation (void *info, const float *in, float *out);
26 static void chromaticEvaluation(void *info, const float *in, float *out);
27 static void inverseChromaticEvaluation(void *info, const float *in, float *out);
28 static void transformRGB_HSV(float *components);
29 static void transformHSV_RGB(float *components);
30 static void resolveHSV(float *color1, float *color2);
33 @implementation CTGradient
34 /////////////////////////////////////Initialization Type Stuff
42 [self setBlendingMode:CTLinearBlendingMode];
54 CGFunctionRelease(gradientFunction);
56 CTGradientElement *elementToRemove = elementList;
57 while(elementList != nil)
59 elementToRemove = elementList;
60 elementList = elementList->nextElement;
61 free(elementToRemove);
67 - (id)copyWithZone:(NSZone *)zone
69 CTGradient *copy = [[[self class] allocWithZone:zone] init];
71 //now just copy my elementlist
72 CTGradientElement *currentElement = elementList;
73 while(currentElement != nil)
75 [copy addElement:currentElement];
76 currentElement = currentElement->nextElement;
79 [copy setBlendingMode:blendingMode];
84 - (void)encodeWithCoder:(NSCoder *)coder
86 if([coder allowsKeyedCoding])
89 CTGradientElement *currentElement = elementList;
90 while(currentElement != nil)
92 [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->red)];
93 [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->green)];
94 [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->blue)];
95 [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->alpha)];
96 [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->position)];
99 currentElement = currentElement->nextElement;
101 [coder encodeInt:count forKey:@"CTGradientElementCount"];
102 [coder encodeInt:blendingMode forKey:@"CTGradientBlendingMode"];
105 [NSException raise:NSInvalidArchiveOperationException format:@"Only supports NSKeyedArchiver coders"];
108 - (id)initWithCoder:(NSCoder *)coder
112 [self setBlendingMode:[coder decodeIntForKey:@"CTGradientBlendingMode"]];
113 unsigned count = [coder decodeIntForKey:@"CTGradientElementCount"];
117 CTGradientElement newElement;
119 [coder decodeValueOfObjCType:@encode(float) at:&(newElement.red)];
120 [coder decodeValueOfObjCType:@encode(float) at:&(newElement.green)];
121 [coder decodeValueOfObjCType:@encode(float) at:&(newElement.blue)];
122 [coder decodeValueOfObjCType:@encode(float) at:&(newElement.alpha)];
123 [coder decodeValueOfObjCType:@encode(float) at:&(newElement.position)];
126 [self addElement:&newElement];
134 #pragma mark Creation
135 + (id)gradientWithBeginningColor:(NSColor *)begin endingColor:(NSColor *)end
137 id newInstance = [[[self class] alloc] init];
139 CTGradientElement color1;
140 CTGradientElement color2;
142 [[begin colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&color1.red
145 alpha:&color1.alpha];
147 [[end colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&color2.red
150 alpha:&color2.alpha];
154 [newInstance addElement:&color1];
155 [newInstance addElement:&color2];
157 return [newInstance autorelease];
160 + (id)aquaSelectedGradient
162 id newInstance = [[[self class] alloc] init];
164 CTGradientElement color1;
171 CTGradientElement color2;
176 color2.position = 11.5/23;
178 CTGradientElement color3;
183 color3.position = 11.5/23;
185 CTGradientElement color4;
192 [newInstance addElement:&color1];
193 [newInstance addElement:&color2];
194 [newInstance addElement:&color3];
195 [newInstance addElement:&color4];
197 return [newInstance autorelease];
200 + (id)aquaNormalGradient
202 id newInstance = [[[self class] alloc] init];
204 CTGradientElement color1;
205 color1.red = color1.green = color1.blue = 0.95;
209 CTGradientElement color2;
210 color2.red = color2.green = color2.blue = 0.83;
212 color2.position = 11.5/23;
214 CTGradientElement color3;
215 color3.red = color3.green = color3.blue = 0.95;
217 color3.position = 11.5/23;
219 CTGradientElement color4;
220 color4.red = color4.green = color4.blue = 0.92;
224 [newInstance addElement:&color1];
225 [newInstance addElement:&color2];
226 [newInstance addElement:&color3];
227 [newInstance addElement:&color4];
229 return [newInstance autorelease];
232 + (id)aquaPressedGradient
234 id newInstance = [[[self class] alloc] init];
236 CTGradientElement color1;
237 color1.red = color1.green = color1.blue = 0.80;
241 CTGradientElement color2;
242 color2.red = color2.green = color2.blue = 0.64;
244 color2.position = 11.5/23;
246 CTGradientElement color3;
247 color3.red = color3.green = color3.blue = 0.80;
249 color3.position = 11.5/23;
251 CTGradientElement color4;
252 color4.red = color4.green = color4.blue = 0.77;
256 [newInstance addElement:&color1];
257 [newInstance addElement:&color2];
258 [newInstance addElement:&color3];
259 [newInstance addElement:&color4];
261 return [newInstance autorelease];
264 + (id)unifiedSelectedGradient
266 id newInstance = [[[self class] alloc] init];
268 CTGradientElement color1;
269 color1.red = color1.green = color1.blue = 0.85;
273 CTGradientElement color2;
274 color2.red = color2.green = color2.blue = 0.95;
278 [newInstance addElement:&color1];
279 [newInstance addElement:&color2];
281 return [newInstance autorelease];
284 + (id)unifiedNormalGradient
286 id newInstance = [[[self class] alloc] init];
288 CTGradientElement color1;
289 color1.red = color1.green = color1.blue = 0.75;
293 CTGradientElement color2;
294 color2.red = color2.green = color2.blue = 0.90;
298 [newInstance addElement:&color1];
299 [newInstance addElement:&color2];
301 return [newInstance autorelease];
304 + (id)unifiedPressedGradient
306 id newInstance = [[[self class] alloc] init];
308 CTGradientElement color1;
309 color1.red = color1.green = color1.blue = 0.60;
313 CTGradientElement color2;
314 color2.red = color2.green = color2.blue = 0.75;
318 [newInstance addElement:&color1];
319 [newInstance addElement:&color2];
321 return [newInstance autorelease];
324 + (id)unifiedDarkGradient
326 id newInstance = [[[self class] alloc] init];
328 CTGradientElement color1;
329 color1.red = color1.green = color1.blue = 0.68;
333 CTGradientElement color2;
334 color2.red = color2.green = color2.blue = 0.83;
338 [newInstance addElement:&color1];
339 [newInstance addElement:&color2];
341 return [newInstance autorelease];
344 + (id)sourceListSelectedGradient
346 id newInstance = [[[self class] alloc] init];
348 CTGradientElement color1;
355 CTGradientElement color2;
362 [newInstance addElement:&color1];
363 [newInstance addElement:&color2];
365 return [newInstance autorelease];
368 + (id)sourceListUnselectedGradient
370 id newInstance = [[[self class] alloc] init];
372 CTGradientElement color1;
379 CTGradientElement color2;
386 [newInstance addElement:&color1];
387 [newInstance addElement:&color2];
389 return [newInstance autorelease];
392 + (id)rainbowGradient
394 id newInstance = [[[self class] alloc] init];
396 CTGradientElement color1;
401 color1.position = 0.0;
403 CTGradientElement color2;
408 color2.position = 1.0;
410 [newInstance addElement:&color1];
411 [newInstance addElement:&color2];
413 [newInstance setBlendingMode:CTChromaticBlendingMode];
415 return [newInstance autorelease];
418 + (id)hydrogenSpectrumGradient
420 id newInstance = [[[self class] alloc] init];
422 struct {float hue; float position; float width;} colorBands[4];
424 colorBands[0].hue = 22;
425 colorBands[0].position = .145;
426 colorBands[0].width = .01;
428 colorBands[1].hue = 200;
429 colorBands[1].position = .71;
430 colorBands[1].width = .008;
432 colorBands[2].hue = 253;
433 colorBands[2].position = .885;
434 colorBands[2].width = .005;
436 colorBands[3].hue = 275;
437 colorBands[3].position = .965;
438 colorBands[3].width = .003;
441 /////////////////////////////
442 for(i = 0; i < 4; i++)
445 color[0] = colorBands[i].hue - 180*colorBands[i].width;
449 transformHSV_RGB(color);
450 CTGradientElement fadeIn;
451 fadeIn.red = color[0];
452 fadeIn.green = color[1];
453 fadeIn.blue = color[2];
454 fadeIn.alpha = color[3];
455 fadeIn.position = colorBands[i].position - colorBands[i].width;
458 color[0] = colorBands[i].hue;
462 transformHSV_RGB(color);
463 CTGradientElement band;
465 band.green = color[1];
466 band.blue = color[2];
467 band.alpha = color[3];
468 band.position = colorBands[i].position;
470 color[0] = colorBands[i].hue + 180*colorBands[i].width;
474 transformHSV_RGB(color);
475 CTGradientElement fadeOut;
476 fadeOut.red = color[0];
477 fadeOut.green = color[1];
478 fadeOut.blue = color[2];
479 fadeOut.alpha = color[3];
480 fadeOut.position = colorBands[i].position + colorBands[i].width;
483 [newInstance addElement:&fadeIn];
484 [newInstance addElement:&band];
485 [newInstance addElement:&fadeOut];
488 [newInstance setBlendingMode:CTChromaticBlendingMode];
490 return [newInstance autorelease];
497 #pragma mark Modification
498 - (CTGradient *)gradientWithAlphaComponent:(float)alpha
500 id newInstance = [[[self class] alloc] init];
502 CTGradientElement *curElement = elementList;
503 CTGradientElement tempElement;
505 while(curElement != nil)
507 tempElement = *curElement;
508 tempElement.alpha = alpha;
509 [newInstance addElement:&tempElement];
511 curElement = curElement->nextElement;
514 return [newInstance autorelease];
517 - (CTGradient *)gradientWithBlendingMode:(CTGradientBlendingMode)mode
519 CTGradient *newGradient = [self copy];
521 [newGradient setBlendingMode:mode];
523 return [newGradient autorelease];
527 //Adds a color stop with <color> at <position> in elementList
528 //(if two elements are at the same position then added imediatly after the one that was there already)
529 - (CTGradient *)addColorStop:(NSColor *)color atPosition:(float)position
531 CTGradient *newGradient = [self copy];
532 CTGradientElement newGradientElement;
534 //put the components of color into the newGradientElement - must make sure it is a RGB color (not Gray or CMYK)
535 [[color colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&newGradientElement.red
536 green:&newGradientElement.green
537 blue:&newGradientElement.blue
538 alpha:&newGradientElement.alpha];
539 newGradientElement.position = position;
541 //Pass it off to addElement to take care of adding it to the elementList
542 [newGradient addElement:&newGradientElement];
544 return [newGradient autorelease];
548 //Removes the color stop at <position> from elementList
549 - (CTGradient *)removeColorStopAtPosition:(float)position
551 CTGradient *newGradient = [self copy];
552 CTGradientElement removedElement = [newGradient removeElementAtPosition:position];
554 if(isnan(removedElement.position))
555 [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtPosition:]: no such colorStop at position (%f)", [self class], position];
557 return [newGradient autorelease];
560 - (CTGradient *)removeColorStopAtIndex:(unsigned)index
562 CTGradient *newGradient = [self copy];
563 CTGradientElement removedElement = [newGradient removeElementAtIndex:index];
565 if(isnan(removedElement.position))
566 [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtIndex:]: index (%i) beyond bounds", [self class], index];
568 return [newGradient autorelease];
574 #pragma mark Information
575 - (CTGradientBlendingMode)blendingMode
580 //Returns color at <position> in gradient
581 - (NSColor *)colorStopAtIndex:(unsigned)index
583 CTGradientElement *element = [self elementAtIndex:index];
586 return [NSColor colorWithCalibratedRed:element->red
589 alpha:element->alpha];
591 [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtIndex:]: index (%i) beyond bounds", [self class], index];
596 - (NSColor *)colorAtPosition:(float)position
602 case CTLinearBlendingMode:
603 linearEvaluation(&elementList, &position, components); break;
604 case CTChromaticBlendingMode:
605 chromaticEvaluation(&elementList, &position, components); break;
606 case CTInverseChromaticBlendingMode:
607 inverseChromaticEvaluation(&elementList, &position, components); break;
611 return [NSColor colorWithCalibratedRed:components[0]/components[3] //undo premultiplication that CG requires
612 green:components[1]/components[3]
613 blue:components[2]/components[3]
614 alpha:components[3]];
621 - (void)drawSwatchInRect:(NSRect)rect
623 [self fillRect:rect angle:45];
626 - (void)fillRect:(NSRect)rect angle:(float)angle
628 //First Calculate where the beginning and ending points should be
632 if(angle == 0) //screw the calculations - we know the answer
634 startPoint = CGPointMake(NSMinX(rect), NSMinY(rect)); //right of rect
635 endPoint = CGPointMake(NSMaxX(rect), NSMinY(rect)); //left of rect
637 else if(angle == 90) //same as above
639 startPoint = CGPointMake(NSMinX(rect), NSMinY(rect)); //bottom of rect
640 endPoint = CGPointMake(NSMinX(rect), NSMaxY(rect)); //top of rect
642 else //ok, we'll do the calculations now
645 float sina, cosa, tana;
651 float rangle = angle * pi/180; //convert the angle to radians
653 if(fabsf(tan(rangle))<=1) //for range [-45,45], [135,225]
662 length = x/fabsf(cosa)+(y-x*fabsf(tana))*fabsf(sina);
664 deltax = length*cosa/2;
665 deltay = length*sina/2;
667 else //for range [45,135], [225,315]
672 sina = sin(rangle - 90*pi/180);
673 cosa = cos(rangle - 90*pi/180);
674 tana = tan(rangle - 90*pi/180);
676 length = x/fabsf(cosa)+(y-x*fabsf(tana))*fabsf(sina);
678 deltax =-length*sina/2;
679 deltay = length*cosa/2;
682 startPoint = CGPointMake(NSMidX(rect)-deltax, NSMidY(rect)-deltay);
683 endPoint = CGPointMake(NSMidX(rect)+deltax, NSMidY(rect)+deltay);
686 //Calls to CoreGraphics
687 CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
688 CGContextSaveGState(currentContext);
689 #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4
690 CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
692 CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
694 CGShadingRef myCGShading = CGShadingCreateAxial(colorspace, startPoint, endPoint, gradientFunction, false, false);
696 CGContextClipToRect (currentContext, *(CGRect *)&rect); //This is where the action happens
697 CGContextDrawShading(currentContext, myCGShading);
699 CGShadingRelease(myCGShading);
700 CGColorSpaceRelease(colorspace );
701 CGContextRestoreGState(currentContext);
704 - (void)radialFillRect:(NSRect)rect
706 CGPoint startPoint, endPoint;
707 float startRadius, endRadius;
708 float scalex, scaley, transx, transy;
710 startPoint = endPoint = CGPointMake(NSMidX(rect), NSMidY(rect));
713 if(NSHeight(rect)>NSWidth(rect))
715 scalex = NSWidth(rect)/NSHeight(rect);
716 transx = (NSHeight(rect)-NSWidth(rect))/2;
719 endRadius = NSHeight(rect)/2;
725 scaley = NSHeight(rect)/NSWidth(rect);
726 transy = (NSWidth(rect)-NSHeight(rect))/2;
727 endRadius = NSWidth(rect)/2;
730 //Calls to CoreGraphics
731 CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
732 CGContextSaveGState(currentContext);
733 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
734 CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
736 CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
738 CGShadingRef myCGShading = CGShadingCreateRadial(colorspace, startPoint, startRadius, endPoint, endRadius, gradientFunction, true, true);
740 CGContextClipToRect (currentContext, *(CGRect *)&rect);
741 CGContextScaleCTM (currentContext, scalex, scaley);
742 CGContextTranslateCTM(currentContext, transx, transy);
743 CGContextDrawShading (currentContext, myCGShading); //This is where the action happens
745 CGShadingRelease(myCGShading);
746 CGColorSpaceRelease(colorspace);
747 CGContextRestoreGState(currentContext);
750 - (void)fillBezierPath:(NSBezierPath *)path angle:(float)angle
752 NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
753 [currentContext saveGraphicsState];
754 NSAffineTransform *transform = [[NSAffineTransform alloc] init];
756 [transform rotateByDegrees:-angle];
757 [path transformUsingAffineTransform:transform];
762 [self fillRect:[path bounds] angle:0];
763 [path transformUsingAffineTransform:transform];
765 [currentContext restoreGraphicsState];
767 - (void)radialFillBezierPath:(NSBezierPath *)path
769 NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
770 [currentContext saveGraphicsState];
772 [self radialFillRect:[path bounds]];
773 [currentContext restoreGraphicsState];
779 #pragma mark Private Methods
780 - (void)setBlendingMode:(CTGradientBlendingMode)mode;
784 //Choose what blending function to use
785 void *evaluationFunction;
788 case CTLinearBlendingMode:
789 evaluationFunction = &linearEvaluation; break;
790 case CTChromaticBlendingMode:
791 evaluationFunction = &chromaticEvaluation; break;
792 case CTInverseChromaticBlendingMode:
793 evaluationFunction = &inverseChromaticEvaluation; break;
796 //replace the current CoreGraphics Function with new one
797 if(gradientFunction != NULL)
798 CGFunctionRelease(gradientFunction);
800 CGFunctionCallbacks evaluationCallbackInfo = {0 , evaluationFunction, NULL}; //Version, evaluator function, cleanup function
802 static const float input_value_range [2] = { 0, 1 }; //range for the evaluator input
803 static const float output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 }; //ranges for the evaluator output (4 returned values)
805 gradientFunction = CGFunctionCreate(&elementList, //the two transition colors
806 1, input_value_range , //number of inputs (just fraction of progression)
807 4, output_value_ranges, //number of outputs (4 - RGBa)
808 &evaluationCallbackInfo); //info for using the evaluator function
811 - (void)addElement:(CTGradientElement *)newElement
813 if(elementList == nil || newElement->position < elementList->position) //inserting at beginning of list
815 CTGradientElement *tmpNext = elementList;
816 elementList = malloc(sizeof(CTGradientElement));
817 *elementList = *newElement;
818 elementList->nextElement = tmpNext;
820 else //inserting somewhere inside list
822 CTGradientElement *curElement = elementList;
824 while(curElement->nextElement != nil && !((curElement->position <= newElement->position) && (newElement->position < curElement->nextElement->position)))
826 curElement = curElement->nextElement;
829 CTGradientElement *tmpNext = curElement->nextElement;
830 curElement->nextElement = malloc(sizeof(CTGradientElement));
831 *(curElement->nextElement) = *newElement;
832 curElement->nextElement->nextElement = tmpNext;
836 - (CTGradientElement)removeElementAtIndex:(unsigned)index
838 CTGradientElement removedElement;
840 if(elementList != nil)
844 CTGradientElement *tmpNext = elementList;
845 elementList = elementList->nextElement;
847 removedElement = *tmpNext;
850 return removedElement;
853 unsigned count = 1; //we want to start one ahead
854 CTGradientElement *currentElement = elementList;
855 while(currentElement->nextElement != nil)
859 CTGradientElement *tmpNext = currentElement->nextElement;
860 currentElement->nextElement = currentElement->nextElement->nextElement;
862 removedElement = *tmpNext;
865 return removedElement;
869 currentElement = currentElement->nextElement;
873 //element is not found, return empty element
874 removedElement.red = 0.0;
875 removedElement.green = 0.0;
876 removedElement.blue = 0.0;
877 removedElement.alpha = 0.0;
878 removedElement.position = NAN;
879 removedElement.nextElement = nil;
881 return removedElement;
884 - (CTGradientElement)removeElementAtPosition:(float)position
886 CTGradientElement removedElement;
888 if(elementList != nil)
890 if(elementList->position == position)
892 CTGradientElement *tmpNext = elementList;
893 elementList = elementList->nextElement;
895 removedElement = *tmpNext;
898 return removedElement;
902 CTGradientElement *curElement = elementList;
903 while(curElement->nextElement != nil)
905 if(curElement->nextElement->position == position)
907 CTGradientElement *tmpNext = curElement->nextElement;
908 curElement->nextElement = curElement->nextElement->nextElement;
910 removedElement = *tmpNext;
913 return removedElement;
919 //element is not found, return empty element
920 removedElement.red = 0.0;
921 removedElement.green = 0.0;
922 removedElement.blue = 0.0;
923 removedElement.alpha = 0.0;
924 removedElement.position = NAN;
925 removedElement.nextElement = nil;
927 return removedElement;
931 - (CTGradientElement *)elementAtIndex:(unsigned)index;
934 CTGradientElement *currentElement = elementList;
936 while(currentElement != nil)
939 return currentElement;
942 currentElement = currentElement->nextElement;
951 #pragma mark Core Graphics
952 //////////////////////////////////////Blending Functions/////////////////////////////////////
953 void linearEvaluation (void *info, const float *in, float *out)
955 float position = *in;
957 if(*(CTGradientElement **)info == nil) //if elementList is empty return clear color
959 out[0] = out[1] = out[2] = out[3] = 1;
963 //This grabs the first two colors in the sequence
964 CTGradientElement *color1 = *(CTGradientElement **)info;
965 CTGradientElement *color2 = color1->nextElement;
967 //make sure first color and second color are on other sides of position
968 while(color2 != nil && color2->position < position)
971 color2 = color1->nextElement;
973 //if we don't have another color then make next color the same color
979 //----------FailSafe settings----------
980 //color1->red = 1; color2->red = 0;
981 //color1->green = 1; color2->green = 0;
982 //color1->blue = 1; color2->blue = 0;
983 //color1->alpha = 1; color2->alpha = 1;
984 //color1->position = .5;
985 //color2->position = .5;
986 //-------------------------------------
988 if(position <= color1->position) //Make all below color color1's position equal to color1
990 out[0] = color1->red;
991 out[1] = color1->green;
992 out[2] = color1->blue;
993 out[3] = color1->alpha;
995 else if (position >= color2->position) //Make all above color color2's position equal to color2
997 out[0] = color2->red;
998 out[1] = color2->green;
999 out[2] = color2->blue;
1000 out[3] = color2->alpha;
1002 else //Interpolate color at postions between color1 and color1
1004 //adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position
1005 position = (position-color1->position)/(color2->position - color1->position);
1007 out[0] = (color2->red - color1->red )*position + color1->red;
1008 out[1] = (color2->green - color1->green)*position + color1->green;
1009 out[2] = (color2->blue - color1->blue )*position + color1->blue;
1010 out[3] = (color2->alpha - color1->alpha)*position + color1->alpha;
1013 //Premultiply the color by the alpha.
1022 //Chromatic Evaluation -
1023 // This blends colors by their Hue, Saturation, and Value(Brightness) right now I just
1024 // transform the RGB values stored in the CTGradientElements to HSB, in the future I may
1025 // streamline it to avoid transforming in and out of HSB colorspace *for later*
1027 // For the chromatic blend we shift the hue of color1 to meet the hue of color2. To do
1028 // this we will add to the hue's angle (if we subtract we'll be doing the inverse
1029 // chromatic...scroll down more for that). All we need to do is keep adding to the hue
1030 // until we wrap around the colorwheel and get to color2.
1031 void chromaticEvaluation(void *info, const float *in, float *out)
1033 float position = *in;
1035 if(*(CTGradientElement **)info == nil) //if elementList is empty return clear color
1037 out[0] = out[1] = out[2] = out[3] = 1;
1041 //This grabs the first two colors in the sequence
1042 CTGradientElement *color1 = *(CTGradientElement **)info;
1043 CTGradientElement *color2 = color1->nextElement;
1048 //make sure first color and second color are on other sides of position
1049 while(color2 != nil && color2->position < position)
1052 color2 = color1->nextElement;
1054 //if we don't have another color then make next color the same color
1061 c1[0] = color1->red;
1062 c1[1] = color1->green;
1063 c1[2] = color1->blue;
1064 c1[3] = color1->alpha;
1066 c2[0] = color2->red;
1067 c2[1] = color2->green;
1068 c2[2] = color2->blue;
1069 c2[3] = color2->alpha;
1071 transformRGB_HSV(c1);
1072 transformRGB_HSV(c2);
1075 if(c1[0] > c2[0]) //if color1's hue is higher than color2's hue then
1076 c2[0] += 360; // we need to move c2 one revolution around the wheel
1079 if(position <= color1->position) //Make all below color color1's position equal to color1
1086 else if (position >= color2->position) //Make all above color color2's position equal to color2
1093 else //Interpolate color at postions between color1 and color1
1095 //adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position
1096 position = (position-color1->position)/(color2->position - color1->position);
1098 out[0] = (c2[0] - c1[0])*position + c1[0];
1099 out[1] = (c2[1] - c1[1])*position + c1[1];
1100 out[2] = (c2[2] - c1[2])*position + c1[2];
1101 out[3] = (c2[3] - c1[3])*position + c1[3];
1104 transformHSV_RGB(out);
1106 //Premultiply the color by the alpha.
1114 //Inverse Chromatic Evaluation -
1115 // Inverse Chromatic is about the same story as Chromatic Blend, but here the Hue
1116 // is strictly decreasing, that is we need to get from color1 to color2 by decreasing
1117 // the 'angle' (i.e. 90¼ -> 180¼ would be done by subtracting 270¼ and getting -180¼...
1118 // which is equivalent to 180¼ mod 360¼
1119 void inverseChromaticEvaluation(void *info, const float *in, float *out)
1121 float position = *in;
1123 if(*(CTGradientElement **)info == nil) //if elementList is empty return clear color
1125 out[0] = out[1] = out[2] = out[3] = 1;
1129 //This grabs the first two colors in the sequence
1130 CTGradientElement *color1 = *(CTGradientElement **)info;
1131 CTGradientElement *color2 = color1->nextElement;
1136 //make sure first color and second color are on other sides of position
1137 while(color2 != nil && color2->position < position)
1140 color2 = color1->nextElement;
1142 //if we don't have another color then make next color the same color
1148 c1[0] = color1->red;
1149 c1[1] = color1->green;
1150 c1[2] = color1->blue;
1151 c1[3] = color1->alpha;
1153 c2[0] = color2->red;
1154 c2[1] = color2->green;
1155 c2[2] = color2->blue;
1156 c2[3] = color2->alpha;
1158 transformRGB_HSV(c1);
1159 transformRGB_HSV(c2);
1162 if(c1[0] < c2[0]) //if color1's hue is higher than color2's hue then
1163 c1[0] += 360; // we need to move c2 one revolution back on the wheel
1166 if(position <= color1->position) //Make all below color color1's position equal to color1
1173 else if (position >= color2->position) //Make all above color color2's position equal to color2
1180 else //Interpolate color at postions between color1 and color1
1182 //adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position
1183 position = (position-color1->position)/(color2->position - color1->position);
1185 out[0] = (c2[0] - c1[0])*position + c1[0];
1186 out[1] = (c2[1] - c1[1])*position + c1[1];
1187 out[2] = (c2[2] - c1[2])*position + c1[2];
1188 out[3] = (c2[3] - c1[3])*position + c1[3];
1191 transformHSV_RGB(out);
1193 //Premultiply the color by the alpha.
1200 void transformRGB_HSV(float *components) //H,S,B -> R,G,B
1203 float R = components[0],
1207 float MAX = R > G ? (R > B ? R : B) : (G > B ? G : B),
1208 MIN = R < G ? (R < B ? R : B) : (G < B ? G : B);
1214 H = 60*(G-B)/(MAX-MIN)+0;
1216 H = 60*(G-B)/(MAX-MIN)+360;
1218 H = 60*(B-R)/(MAX-MIN)+120;
1220 H = 60*(R-G)/(MAX-MIN)+240;
1222 S = MAX == 0 ? 0 : 1 - MIN/MAX;
1230 void transformHSV_RGB(float *components) //H,S,B -> R,G,B
1233 float H = fmodf(components[0],359), //map to [0,360)
1237 int Hi = (int)floorf(H/60.) % 6;
1245 case 0: R=V;G=t;B=p; break;
1246 case 1: R=q;G=V;B=p; break;
1247 case 2: R=p;G=V;B=t; break;
1248 case 3: R=p;G=q;B=V; break;
1249 case 4: R=t;G=p;B=V; break;
1250 case 5: R=V;G=p;B=q; break;
1258 void resolveHSV(float *color1, float *color2) //H value may be undefined (i.e. graycale color)
1259 { // we want to fill it with a sensible value
1260 if(isnan(color1[0]) && isnan(color2[0]))
1261 color1[0] = color2[0] = 0;
1262 else if(isnan(color1[0]))
1263 color1[0] = color2[0];
1264 else if(isnan(color2[0]))
1265 color2[0] = color1[0];