4 // Created by Chad Weider on 2/14/07.
5 // Writtin by Chad Weider.
7 // Released into public domain on 4/10/08.
10 #ifdef MM_ENABLE_PLUGINS
12 #import "CTGradient.h"
14 @interface CTGradient (Private)
16 - (void)setBlendingMode:(CTGradientBlendingMode)mode;
17 - (void)addElement:(CTGradientElement*)newElement;
19 - (CTGradientElement *)elementAtIndex:(unsigned)index;
21 - (CTGradientElement)removeElementAtIndex:(unsigned)index;
22 - (CTGradientElement)removeElementAtPosition:(float)position;
25 //C Fuctions for color blending
26 static void linearEvaluation (void *info, const float *in, float *out);
27 static void chromaticEvaluation(void *info, const float *in, float *out);
28 static void inverseChromaticEvaluation(void *info, const float *in, float *out);
29 static void transformRGB_HSV(float *components);
30 static void transformHSV_RGB(float *components);
31 static void resolveHSV(float *color1, float *color2);
34 @implementation CTGradient
35 /////////////////////////////////////Initialization Type Stuff
43 [self setBlendingMode:CTLinearBlendingMode];
55 CGFunctionRelease(gradientFunction);
57 CTGradientElement *elementToRemove = elementList;
58 while(elementList != nil)
60 elementToRemove = elementList;
61 elementList = elementList->nextElement;
62 free(elementToRemove);
68 - (id)copyWithZone:(NSZone *)zone
70 CTGradient *copy = [[[self class] allocWithZone:zone] init];
72 //now just copy my elementlist
73 CTGradientElement *currentElement = elementList;
74 while(currentElement != nil)
76 [copy addElement:currentElement];
77 currentElement = currentElement->nextElement;
80 [copy setBlendingMode:blendingMode];
85 - (void)encodeWithCoder:(NSCoder *)coder
87 if([coder allowsKeyedCoding])
90 CTGradientElement *currentElement = elementList;
91 while(currentElement != nil)
93 [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->red)];
94 [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->green)];
95 [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->blue)];
96 [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->alpha)];
97 [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->position)];
100 currentElement = currentElement->nextElement;
102 [coder encodeInt:count forKey:@"CTGradientElementCount"];
103 [coder encodeInt:blendingMode forKey:@"CTGradientBlendingMode"];
106 [NSException raise:NSInvalidArchiveOperationException format:@"Only supports NSKeyedArchiver coders"];
109 - (id)initWithCoder:(NSCoder *)coder
113 [self setBlendingMode:[coder decodeIntForKey:@"CTGradientBlendingMode"]];
114 unsigned count = [coder decodeIntForKey:@"CTGradientElementCount"];
118 CTGradientElement newElement;
120 [coder decodeValueOfObjCType:@encode(float) at:&(newElement.red)];
121 [coder decodeValueOfObjCType:@encode(float) at:&(newElement.green)];
122 [coder decodeValueOfObjCType:@encode(float) at:&(newElement.blue)];
123 [coder decodeValueOfObjCType:@encode(float) at:&(newElement.alpha)];
124 [coder decodeValueOfObjCType:@encode(float) at:&(newElement.position)];
127 [self addElement:&newElement];
135 #pragma mark Creation
136 + (id)gradientWithBeginningColor:(NSColor *)begin endingColor:(NSColor *)end
138 id newInstance = [[[self class] alloc] init];
140 CTGradientElement color1;
141 CTGradientElement color2;
143 [[begin colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&color1.red
146 alpha:&color1.alpha];
148 [[end colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&color2.red
151 alpha:&color2.alpha];
155 [newInstance addElement:&color1];
156 [newInstance addElement:&color2];
158 return [newInstance autorelease];
161 + (id)aquaSelectedGradient
163 id newInstance = [[[self class] alloc] init];
165 CTGradientElement color1;
172 CTGradientElement color2;
177 color2.position = 11.5/23;
179 CTGradientElement color3;
184 color3.position = 11.5/23;
186 CTGradientElement color4;
193 [newInstance addElement:&color1];
194 [newInstance addElement:&color2];
195 [newInstance addElement:&color3];
196 [newInstance addElement:&color4];
198 return [newInstance autorelease];
201 + (id)aquaNormalGradient
203 id newInstance = [[[self class] alloc] init];
205 CTGradientElement color1;
206 color1.red = color1.green = color1.blue = 0.95;
210 CTGradientElement color2;
211 color2.red = color2.green = color2.blue = 0.83;
213 color2.position = 11.5/23;
215 CTGradientElement color3;
216 color3.red = color3.green = color3.blue = 0.95;
218 color3.position = 11.5/23;
220 CTGradientElement color4;
221 color4.red = color4.green = color4.blue = 0.92;
225 [newInstance addElement:&color1];
226 [newInstance addElement:&color2];
227 [newInstance addElement:&color3];
228 [newInstance addElement:&color4];
230 return [newInstance autorelease];
233 + (id)aquaPressedGradient
235 id newInstance = [[[self class] alloc] init];
237 CTGradientElement color1;
238 color1.red = color1.green = color1.blue = 0.80;
242 CTGradientElement color2;
243 color2.red = color2.green = color2.blue = 0.64;
245 color2.position = 11.5/23;
247 CTGradientElement color3;
248 color3.red = color3.green = color3.blue = 0.80;
250 color3.position = 11.5/23;
252 CTGradientElement color4;
253 color4.red = color4.green = color4.blue = 0.77;
257 [newInstance addElement:&color1];
258 [newInstance addElement:&color2];
259 [newInstance addElement:&color3];
260 [newInstance addElement:&color4];
262 return [newInstance autorelease];
265 + (id)unifiedSelectedGradient
267 id newInstance = [[[self class] alloc] init];
269 CTGradientElement color1;
270 color1.red = color1.green = color1.blue = 0.85;
274 CTGradientElement color2;
275 color2.red = color2.green = color2.blue = 0.95;
279 [newInstance addElement:&color1];
280 [newInstance addElement:&color2];
282 return [newInstance autorelease];
285 + (id)unifiedNormalGradient
287 id newInstance = [[[self class] alloc] init];
289 CTGradientElement color1;
290 color1.red = color1.green = color1.blue = 0.75;
294 CTGradientElement color2;
295 color2.red = color2.green = color2.blue = 0.90;
299 [newInstance addElement:&color1];
300 [newInstance addElement:&color2];
302 return [newInstance autorelease];
305 + (id)unifiedPressedGradient
307 id newInstance = [[[self class] alloc] init];
309 CTGradientElement color1;
310 color1.red = color1.green = color1.blue = 0.60;
314 CTGradientElement color2;
315 color2.red = color2.green = color2.blue = 0.75;
319 [newInstance addElement:&color1];
320 [newInstance addElement:&color2];
322 return [newInstance autorelease];
325 + (id)unifiedDarkGradient
327 id newInstance = [[[self class] alloc] init];
329 CTGradientElement color1;
330 color1.red = color1.green = color1.blue = 0.68;
334 CTGradientElement color2;
335 color2.red = color2.green = color2.blue = 0.83;
339 [newInstance addElement:&color1];
340 [newInstance addElement:&color2];
342 return [newInstance autorelease];
345 + (id)sourceListSelectedGradient
347 id newInstance = [[[self class] alloc] init];
349 CTGradientElement color1;
356 CTGradientElement color2;
363 [newInstance addElement:&color1];
364 [newInstance addElement:&color2];
366 return [newInstance autorelease];
369 + (id)sourceListUnselectedGradient
371 id newInstance = [[[self class] alloc] init];
373 CTGradientElement color1;
380 CTGradientElement color2;
387 [newInstance addElement:&color1];
388 [newInstance addElement:&color2];
390 return [newInstance autorelease];
393 + (id)rainbowGradient
395 id newInstance = [[[self class] alloc] init];
397 CTGradientElement color1;
402 color1.position = 0.0;
404 CTGradientElement color2;
409 color2.position = 1.0;
411 [newInstance addElement:&color1];
412 [newInstance addElement:&color2];
414 [newInstance setBlendingMode:CTChromaticBlendingMode];
416 return [newInstance autorelease];
419 + (id)hydrogenSpectrumGradient
421 id newInstance = [[[self class] alloc] init];
423 struct {float hue; float position; float width;} colorBands[4];
425 colorBands[0].hue = 22;
426 colorBands[0].position = .145;
427 colorBands[0].width = .01;
429 colorBands[1].hue = 200;
430 colorBands[1].position = .71;
431 colorBands[1].width = .008;
433 colorBands[2].hue = 253;
434 colorBands[2].position = .885;
435 colorBands[2].width = .005;
437 colorBands[3].hue = 275;
438 colorBands[3].position = .965;
439 colorBands[3].width = .003;
442 /////////////////////////////
443 for(i = 0; i < 4; i++)
446 color[0] = colorBands[i].hue - 180*colorBands[i].width;
450 transformHSV_RGB(color);
451 CTGradientElement fadeIn;
452 fadeIn.red = color[0];
453 fadeIn.green = color[1];
454 fadeIn.blue = color[2];
455 fadeIn.alpha = color[3];
456 fadeIn.position = colorBands[i].position - colorBands[i].width;
459 color[0] = colorBands[i].hue;
463 transformHSV_RGB(color);
464 CTGradientElement band;
466 band.green = color[1];
467 band.blue = color[2];
468 band.alpha = color[3];
469 band.position = colorBands[i].position;
471 color[0] = colorBands[i].hue + 180*colorBands[i].width;
475 transformHSV_RGB(color);
476 CTGradientElement fadeOut;
477 fadeOut.red = color[0];
478 fadeOut.green = color[1];
479 fadeOut.blue = color[2];
480 fadeOut.alpha = color[3];
481 fadeOut.position = colorBands[i].position + colorBands[i].width;
484 [newInstance addElement:&fadeIn];
485 [newInstance addElement:&band];
486 [newInstance addElement:&fadeOut];
489 [newInstance setBlendingMode:CTChromaticBlendingMode];
491 return [newInstance autorelease];
498 #pragma mark Modification
499 - (CTGradient *)gradientWithAlphaComponent:(float)alpha
501 id newInstance = [[[self class] alloc] init];
503 CTGradientElement *curElement = elementList;
504 CTGradientElement tempElement;
506 while(curElement != nil)
508 tempElement = *curElement;
509 tempElement.alpha = alpha;
510 [newInstance addElement:&tempElement];
512 curElement = curElement->nextElement;
515 return [newInstance autorelease];
518 - (CTGradient *)gradientWithBlendingMode:(CTGradientBlendingMode)mode
520 CTGradient *newGradient = [self copy];
522 [newGradient setBlendingMode:mode];
524 return [newGradient autorelease];
528 //Adds a color stop with <color> at <position> in elementList
529 //(if two elements are at the same position then added imediatly after the one that was there already)
530 - (CTGradient *)addColorStop:(NSColor *)color atPosition:(float)position
532 CTGradient *newGradient = [self copy];
533 CTGradientElement newGradientElement;
535 //put the components of color into the newGradientElement - must make sure it is a RGB color (not Gray or CMYK)
536 [[color colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&newGradientElement.red
537 green:&newGradientElement.green
538 blue:&newGradientElement.blue
539 alpha:&newGradientElement.alpha];
540 newGradientElement.position = position;
542 //Pass it off to addElement to take care of adding it to the elementList
543 [newGradient addElement:&newGradientElement];
545 return [newGradient autorelease];
549 //Removes the color stop at <position> from elementList
550 - (CTGradient *)removeColorStopAtPosition:(float)position
552 CTGradient *newGradient = [self copy];
553 CTGradientElement removedElement = [newGradient removeElementAtPosition:position];
555 if(isnan(removedElement.position))
556 [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtPosition:]: no such colorStop at position (%f)", [self class], position];
558 return [newGradient autorelease];
561 - (CTGradient *)removeColorStopAtIndex:(unsigned)index
563 CTGradient *newGradient = [self copy];
564 CTGradientElement removedElement = [newGradient removeElementAtIndex:index];
566 if(isnan(removedElement.position))
567 [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtIndex:]: index (%i) beyond bounds", [self class], index];
569 return [newGradient autorelease];
575 #pragma mark Information
576 - (CTGradientBlendingMode)blendingMode
581 //Returns color at <position> in gradient
582 - (NSColor *)colorStopAtIndex:(unsigned)index
584 CTGradientElement *element = [self elementAtIndex:index];
587 return [NSColor colorWithCalibratedRed:element->red
590 alpha:element->alpha];
592 [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtIndex:]: index (%i) beyond bounds", [self class], index];
597 - (NSColor *)colorAtPosition:(float)position
603 case CTLinearBlendingMode:
604 linearEvaluation(&elementList, &position, components); break;
605 case CTChromaticBlendingMode:
606 chromaticEvaluation(&elementList, &position, components); break;
607 case CTInverseChromaticBlendingMode:
608 inverseChromaticEvaluation(&elementList, &position, components); break;
612 return [NSColor colorWithCalibratedRed:components[0]/components[3] //undo premultiplication that CG requires
613 green:components[1]/components[3]
614 blue:components[2]/components[3]
615 alpha:components[3]];
622 - (void)drawSwatchInRect:(NSRect)rect
624 [self fillRect:rect angle:45];
627 - (void)fillRect:(NSRect)rect angle:(float)angle
629 //First Calculate where the beginning and ending points should be
633 if(angle == 0) //screw the calculations - we know the answer
635 startPoint = CGPointMake(NSMinX(rect), NSMinY(rect)); //right of rect
636 endPoint = CGPointMake(NSMaxX(rect), NSMinY(rect)); //left of rect
638 else if(angle == 90) //same as above
640 startPoint = CGPointMake(NSMinX(rect), NSMinY(rect)); //bottom of rect
641 endPoint = CGPointMake(NSMinX(rect), NSMaxY(rect)); //top of rect
643 else //ok, we'll do the calculations now
646 float sina, cosa, tana;
652 float rangle = angle * pi/180; //convert the angle to radians
654 if(fabsf(tan(rangle))<=1) //for range [-45,45], [135,225]
663 length = x/fabsf(cosa)+(y-x*fabsf(tana))*fabsf(sina);
665 deltax = length*cosa/2;
666 deltay = length*sina/2;
668 else //for range [45,135], [225,315]
673 sina = sin(rangle - 90*pi/180);
674 cosa = cos(rangle - 90*pi/180);
675 tana = tan(rangle - 90*pi/180);
677 length = x/fabsf(cosa)+(y-x*fabsf(tana))*fabsf(sina);
679 deltax =-length*sina/2;
680 deltay = length*cosa/2;
683 startPoint = CGPointMake(NSMidX(rect)-deltax, NSMidY(rect)-deltay);
684 endPoint = CGPointMake(NSMidX(rect)+deltax, NSMidY(rect)+deltay);
687 //Calls to CoreGraphics
688 CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
689 CGContextSaveGState(currentContext);
690 #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4
691 CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
693 CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
695 CGShadingRef myCGShading = CGShadingCreateAxial(colorspace, startPoint, endPoint, gradientFunction, false, false);
697 CGContextClipToRect (currentContext, *(CGRect *)&rect); //This is where the action happens
698 CGContextDrawShading(currentContext, myCGShading);
700 CGShadingRelease(myCGShading);
701 CGColorSpaceRelease(colorspace );
702 CGContextRestoreGState(currentContext);
705 - (void)radialFillRect:(NSRect)rect
707 CGPoint startPoint, endPoint;
708 float startRadius, endRadius;
709 float scalex, scaley, transx, transy;
711 startPoint = endPoint = CGPointMake(NSMidX(rect), NSMidY(rect));
714 if(NSHeight(rect)>NSWidth(rect))
716 scalex = NSWidth(rect)/NSHeight(rect);
717 transx = (NSHeight(rect)-NSWidth(rect))/2;
720 endRadius = NSHeight(rect)/2;
726 scaley = NSHeight(rect)/NSWidth(rect);
727 transy = (NSWidth(rect)-NSHeight(rect))/2;
728 endRadius = NSWidth(rect)/2;
731 //Calls to CoreGraphics
732 CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
733 CGContextSaveGState(currentContext);
734 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
735 CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
737 CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
739 CGShadingRef myCGShading = CGShadingCreateRadial(colorspace, startPoint, startRadius, endPoint, endRadius, gradientFunction, true, true);
741 CGContextClipToRect (currentContext, *(CGRect *)&rect);
742 CGContextScaleCTM (currentContext, scalex, scaley);
743 CGContextTranslateCTM(currentContext, transx, transy);
744 CGContextDrawShading (currentContext, myCGShading); //This is where the action happens
746 CGShadingRelease(myCGShading);
747 CGColorSpaceRelease(colorspace);
748 CGContextRestoreGState(currentContext);
751 - (void)fillBezierPath:(NSBezierPath *)path angle:(float)angle
753 NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
754 [currentContext saveGraphicsState];
755 NSAffineTransform *transform = [[NSAffineTransform alloc] init];
757 [transform rotateByDegrees:-angle];
758 [path transformUsingAffineTransform:transform];
763 [self fillRect:[path bounds] angle:0];
764 [path transformUsingAffineTransform:transform];
766 [currentContext restoreGraphicsState];
768 - (void)radialFillBezierPath:(NSBezierPath *)path
770 NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
771 [currentContext saveGraphicsState];
773 [self radialFillRect:[path bounds]];
774 [currentContext restoreGraphicsState];
780 #pragma mark Private Methods
781 - (void)setBlendingMode:(CTGradientBlendingMode)mode;
785 //Choose what blending function to use
786 void *evaluationFunction;
789 case CTLinearBlendingMode:
790 evaluationFunction = &linearEvaluation; break;
791 case CTChromaticBlendingMode:
792 evaluationFunction = &chromaticEvaluation; break;
793 case CTInverseChromaticBlendingMode:
794 evaluationFunction = &inverseChromaticEvaluation; break;
797 //replace the current CoreGraphics Function with new one
798 if(gradientFunction != NULL)
799 CGFunctionRelease(gradientFunction);
801 CGFunctionCallbacks evaluationCallbackInfo = {0 , evaluationFunction, NULL}; //Version, evaluator function, cleanup function
803 static const float input_value_range [2] = { 0, 1 }; //range for the evaluator input
804 static const float output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 }; //ranges for the evaluator output (4 returned values)
806 gradientFunction = CGFunctionCreate(&elementList, //the two transition colors
807 1, input_value_range , //number of inputs (just fraction of progression)
808 4, output_value_ranges, //number of outputs (4 - RGBa)
809 &evaluationCallbackInfo); //info for using the evaluator function
812 - (void)addElement:(CTGradientElement *)newElement
814 if(elementList == nil || newElement->position < elementList->position) //inserting at beginning of list
816 CTGradientElement *tmpNext = elementList;
817 elementList = malloc(sizeof(CTGradientElement));
818 *elementList = *newElement;
819 elementList->nextElement = tmpNext;
821 else //inserting somewhere inside list
823 CTGradientElement *curElement = elementList;
825 while(curElement->nextElement != nil && !((curElement->position <= newElement->position) && (newElement->position < curElement->nextElement->position)))
827 curElement = curElement->nextElement;
830 CTGradientElement *tmpNext = curElement->nextElement;
831 curElement->nextElement = malloc(sizeof(CTGradientElement));
832 *(curElement->nextElement) = *newElement;
833 curElement->nextElement->nextElement = tmpNext;
837 - (CTGradientElement)removeElementAtIndex:(unsigned)index
839 CTGradientElement removedElement;
841 if(elementList != nil)
845 CTGradientElement *tmpNext = elementList;
846 elementList = elementList->nextElement;
848 removedElement = *tmpNext;
851 return removedElement;
854 unsigned count = 1; //we want to start one ahead
855 CTGradientElement *currentElement = elementList;
856 while(currentElement->nextElement != nil)
860 CTGradientElement *tmpNext = currentElement->nextElement;
861 currentElement->nextElement = currentElement->nextElement->nextElement;
863 removedElement = *tmpNext;
866 return removedElement;
870 currentElement = currentElement->nextElement;
874 //element is not found, return empty element
875 removedElement.red = 0.0;
876 removedElement.green = 0.0;
877 removedElement.blue = 0.0;
878 removedElement.alpha = 0.0;
879 removedElement.position = NAN;
880 removedElement.nextElement = nil;
882 return removedElement;
885 - (CTGradientElement)removeElementAtPosition:(float)position
887 CTGradientElement removedElement;
889 if(elementList != nil)
891 if(elementList->position == position)
893 CTGradientElement *tmpNext = elementList;
894 elementList = elementList->nextElement;
896 removedElement = *tmpNext;
899 return removedElement;
903 CTGradientElement *curElement = elementList;
904 while(curElement->nextElement != nil)
906 if(curElement->nextElement->position == position)
908 CTGradientElement *tmpNext = curElement->nextElement;
909 curElement->nextElement = curElement->nextElement->nextElement;
911 removedElement = *tmpNext;
914 return removedElement;
920 //element is not found, return empty element
921 removedElement.red = 0.0;
922 removedElement.green = 0.0;
923 removedElement.blue = 0.0;
924 removedElement.alpha = 0.0;
925 removedElement.position = NAN;
926 removedElement.nextElement = nil;
928 return removedElement;
932 - (CTGradientElement *)elementAtIndex:(unsigned)index;
935 CTGradientElement *currentElement = elementList;
937 while(currentElement != nil)
940 return currentElement;
943 currentElement = currentElement->nextElement;
952 #pragma mark Core Graphics
953 //////////////////////////////////////Blending Functions/////////////////////////////////////
954 void linearEvaluation (void *info, const float *in, float *out)
956 float position = *in;
958 if(*(CTGradientElement **)info == nil) //if elementList is empty return clear color
960 out[0] = out[1] = out[2] = out[3] = 1;
964 //This grabs the first two colors in the sequence
965 CTGradientElement *color1 = *(CTGradientElement **)info;
966 CTGradientElement *color2 = color1->nextElement;
968 //make sure first color and second color are on other sides of position
969 while(color2 != nil && color2->position < position)
972 color2 = color1->nextElement;
974 //if we don't have another color then make next color the same color
980 //----------FailSafe settings----------
981 //color1->red = 1; color2->red = 0;
982 //color1->green = 1; color2->green = 0;
983 //color1->blue = 1; color2->blue = 0;
984 //color1->alpha = 1; color2->alpha = 1;
985 //color1->position = .5;
986 //color2->position = .5;
987 //-------------------------------------
989 if(position <= color1->position) //Make all below color color1's position equal to color1
991 out[0] = color1->red;
992 out[1] = color1->green;
993 out[2] = color1->blue;
994 out[3] = color1->alpha;
996 else if (position >= color2->position) //Make all above color color2's position equal to color2
998 out[0] = color2->red;
999 out[1] = color2->green;
1000 out[2] = color2->blue;
1001 out[3] = color2->alpha;
1003 else //Interpolate color at postions between color1 and color1
1005 //adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position
1006 position = (position-color1->position)/(color2->position - color1->position);
1008 out[0] = (color2->red - color1->red )*position + color1->red;
1009 out[1] = (color2->green - color1->green)*position + color1->green;
1010 out[2] = (color2->blue - color1->blue )*position + color1->blue;
1011 out[3] = (color2->alpha - color1->alpha)*position + color1->alpha;
1014 //Premultiply the color by the alpha.
1023 //Chromatic Evaluation -
1024 // This blends colors by their Hue, Saturation, and Value(Brightness) right now I just
1025 // transform the RGB values stored in the CTGradientElements to HSB, in the future I may
1026 // streamline it to avoid transforming in and out of HSB colorspace *for later*
1028 // For the chromatic blend we shift the hue of color1 to meet the hue of color2. To do
1029 // this we will add to the hue's angle (if we subtract we'll be doing the inverse
1030 // chromatic...scroll down more for that). All we need to do is keep adding to the hue
1031 // until we wrap around the colorwheel and get to color2.
1032 void chromaticEvaluation(void *info, const float *in, float *out)
1034 float position = *in;
1036 if(*(CTGradientElement **)info == nil) //if elementList is empty return clear color
1038 out[0] = out[1] = out[2] = out[3] = 1;
1042 //This grabs the first two colors in the sequence
1043 CTGradientElement *color1 = *(CTGradientElement **)info;
1044 CTGradientElement *color2 = color1->nextElement;
1049 //make sure first color and second color are on other sides of position
1050 while(color2 != nil && color2->position < position)
1053 color2 = color1->nextElement;
1055 //if we don't have another color then make next color the same color
1062 c1[0] = color1->red;
1063 c1[1] = color1->green;
1064 c1[2] = color1->blue;
1065 c1[3] = color1->alpha;
1067 c2[0] = color2->red;
1068 c2[1] = color2->green;
1069 c2[2] = color2->blue;
1070 c2[3] = color2->alpha;
1072 transformRGB_HSV(c1);
1073 transformRGB_HSV(c2);
1076 if(c1[0] > c2[0]) //if color1's hue is higher than color2's hue then
1077 c2[0] += 360; // we need to move c2 one revolution around the wheel
1080 if(position <= color1->position) //Make all below color color1's position equal to color1
1087 else if (position >= color2->position) //Make all above color color2's position equal to color2
1094 else //Interpolate color at postions between color1 and color1
1096 //adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position
1097 position = (position-color1->position)/(color2->position - color1->position);
1099 out[0] = (c2[0] - c1[0])*position + c1[0];
1100 out[1] = (c2[1] - c1[1])*position + c1[1];
1101 out[2] = (c2[2] - c1[2])*position + c1[2];
1102 out[3] = (c2[3] - c1[3])*position + c1[3];
1105 transformHSV_RGB(out);
1107 //Premultiply the color by the alpha.
1115 //Inverse Chromatic Evaluation -
1116 // Inverse Chromatic is about the same story as Chromatic Blend, but here the Hue
1117 // is strictly decreasing, that is we need to get from color1 to color2 by decreasing
1118 // the 'angle' (i.e. 90¼ -> 180¼ would be done by subtracting 270¼ and getting -180¼...
1119 // which is equivalent to 180¼ mod 360¼
1120 void inverseChromaticEvaluation(void *info, const float *in, float *out)
1122 float position = *in;
1124 if(*(CTGradientElement **)info == nil) //if elementList is empty return clear color
1126 out[0] = out[1] = out[2] = out[3] = 1;
1130 //This grabs the first two colors in the sequence
1131 CTGradientElement *color1 = *(CTGradientElement **)info;
1132 CTGradientElement *color2 = color1->nextElement;
1137 //make sure first color and second color are on other sides of position
1138 while(color2 != nil && color2->position < position)
1141 color2 = color1->nextElement;
1143 //if we don't have another color then make next color the same color
1149 c1[0] = color1->red;
1150 c1[1] = color1->green;
1151 c1[2] = color1->blue;
1152 c1[3] = color1->alpha;
1154 c2[0] = color2->red;
1155 c2[1] = color2->green;
1156 c2[2] = color2->blue;
1157 c2[3] = color2->alpha;
1159 transformRGB_HSV(c1);
1160 transformRGB_HSV(c2);
1163 if(c1[0] < c2[0]) //if color1's hue is higher than color2's hue then
1164 c1[0] += 360; // we need to move c2 one revolution back on the wheel
1167 if(position <= color1->position) //Make all below color color1's position equal to color1
1174 else if (position >= color2->position) //Make all above color color2's position equal to color2
1181 else //Interpolate color at postions between color1 and color1
1183 //adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position
1184 position = (position-color1->position)/(color2->position - color1->position);
1186 out[0] = (c2[0] - c1[0])*position + c1[0];
1187 out[1] = (c2[1] - c1[1])*position + c1[1];
1188 out[2] = (c2[2] - c1[2])*position + c1[2];
1189 out[3] = (c2[3] - c1[3])*position + c1[3];
1192 transformHSV_RGB(out);
1194 //Premultiply the color by the alpha.
1201 void transformRGB_HSV(float *components) //H,S,B -> R,G,B
1204 float R = components[0],
1208 float MAX = R > G ? (R > B ? R : B) : (G > B ? G : B),
1209 MIN = R < G ? (R < B ? R : B) : (G < B ? G : B);
1215 H = 60*(G-B)/(MAX-MIN)+0;
1217 H = 60*(G-B)/(MAX-MIN)+360;
1219 H = 60*(B-R)/(MAX-MIN)+120;
1221 H = 60*(R-G)/(MAX-MIN)+240;
1223 S = MAX == 0 ? 0 : 1 - MIN/MAX;
1231 void transformHSV_RGB(float *components) //H,S,B -> R,G,B
1234 float H = fmodf(components[0],359), //map to [0,360)
1238 int Hi = (int)floorf(H/60.) % 6;
1246 case 0: R=V;G=t;B=p; break;
1247 case 1: R=q;G=V;B=p; break;
1248 case 2: R=p;G=V;B=t; break;
1249 case 3: R=p;G=q;B=V; break;
1250 case 4: R=t;G=p;B=V; break;
1251 case 5: R=V;G=p;B=q; break;
1259 void resolveHSV(float *color1, float *color2) //H value may be undefined (i.e. graycale color)
1260 { // we want to fill it with a sensible value
1261 if(isnan(color1[0]) && isnan(color2[0]))
1262 color1[0] = color2[0] = 0;
1263 else if(isnan(color1[0]))
1264 color1[0] = color2[0];
1265 else if(isnan(color2[0]))
1266 color2[0] = color1[0];
1271 #endif // MM_ENABLE_PLUGINS