Change name of window menu items
[MacVim.git] / src / MacVim / CTGradient.m
blob471c4aae7b68c1fb0037654e41f15dfff745374a
1 //
2 //  CTGradient.m
3 //
4 //  Created by Chad Weider on 2/14/07.
5 //  Writtin by Chad Weider.
6 //
7 //  Released into public domain on 4/10/08.
8 //
9 //  Version: 1.8
11 #import "CTGradient.h"
13 @interface CTGradient (Private)
14 - (void)_commonInit;
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;
22 @end
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
35 - (id)init
36   {
37   self = [super init];
38   
39   if (self != nil)
40         {
41         [self _commonInit];
42         [self setBlendingMode:CTLinearBlendingMode];
43         }
44   return self;
45   }
47 - (void)_commonInit
48   {
49   elementList = nil;
50   }
52 - (void)dealloc
53   {
54   CGFunctionRelease(gradientFunction);
55   
56   CTGradientElement *elementToRemove = elementList;
57   while(elementList != nil)
58         {
59         elementToRemove = elementList;
60         elementList = elementList->nextElement;
61         free(elementToRemove);
62         }
63   
64   [super dealloc];
65   }
67 - (id)copyWithZone:(NSZone *)zone
68   {
69   CTGradient *copy = [[[self class] allocWithZone:zone] init];
70   
71   //now just copy my elementlist
72   CTGradientElement *currentElement = elementList;
73   while(currentElement != nil)
74         {
75         [copy addElement:currentElement];
76         currentElement = currentElement->nextElement;
77         }
78   
79   [copy setBlendingMode:blendingMode];
80   
81   return copy;
82   }
84 - (void)encodeWithCoder:(NSCoder *)coder
85   {
86   if([coder allowsKeyedCoding])
87         {
88         unsigned count = 0;
89         CTGradientElement *currentElement = elementList;
90         while(currentElement != nil)
91                 {
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)];
97                 
98                 count++;
99                 currentElement = currentElement->nextElement;
100                 }
101         [coder encodeInt:count forKey:@"CTGradientElementCount"];
102         [coder encodeInt:blendingMode forKey:@"CTGradientBlendingMode"];
103         }
104   else
105         [NSException raise:NSInvalidArchiveOperationException format:@"Only supports NSKeyedArchiver coders"];
106   }
108 - (id)initWithCoder:(NSCoder *)coder
109   {
110   [self _commonInit];
111   
112   [self setBlendingMode:[coder decodeIntForKey:@"CTGradientBlendingMode"]];
113   unsigned count = [coder decodeIntForKey:@"CTGradientElementCount"];
114   
115   while(count != 0)
116         {
117     CTGradientElement newElement;
118         
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)];
124         
125         count--;
126         [self addElement:&newElement];
127         }
128   return self;
129   }
130 #pragma mark -
134 #pragma mark Creation
135 + (id)gradientWithBeginningColor:(NSColor *)begin endingColor:(NSColor *)end
136   {
137   id newInstance = [[[self class] alloc] init];
138   
139   CTGradientElement color1;
140   CTGradientElement color2;
141   
142   [[begin colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&color1.red
143                                                                                                                            green:&color1.green
144                                                                                                                                 blue:&color1.blue
145                                                                                                                            alpha:&color1.alpha];
146   
147   [[end   colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&color2.red
148                                                                                                                            green:&color2.green
149                                                                                                                                 blue:&color2.blue
150                                                                                                                            alpha:&color2.alpha];  
151   color1.position = 0;
152   color2.position = 1;
153   
154   [newInstance addElement:&color1];
155   [newInstance addElement:&color2];
156   
157   return [newInstance autorelease];
158   }
160 + (id)aquaSelectedGradient
161   {
162   id newInstance = [[[self class] alloc] init];
163   
164   CTGradientElement color1;
165   color1.red   = 0.58;
166   color1.green = 0.86;
167   color1.blue  = 0.98;
168   color1.alpha = 1.00;
169   color1.position = 0;
170   
171   CTGradientElement color2;
172   color2.red   = 0.42;
173   color2.green = 0.68;
174   color2.blue  = 0.90;
175   color2.alpha = 1.00;
176   color2.position = 11.5/23;
177   
178   CTGradientElement color3;
179   color3.red   = 0.64;
180   color3.green = 0.80;
181   color3.blue  = 0.94;
182   color3.alpha = 1.00;
183   color3.position = 11.5/23;
184   
185   CTGradientElement color4;
186   color4.red   = 0.56;
187   color4.green = 0.70;
188   color4.blue  = 0.90;
189   color4.alpha = 1.00;
190   color4.position = 1;
191   
192   [newInstance addElement:&color1];
193   [newInstance addElement:&color2];
194   [newInstance addElement:&color3];
195   [newInstance addElement:&color4];
196   
197   return [newInstance autorelease];
198   }
200 + (id)aquaNormalGradient
201   {
202   id newInstance = [[[self class] alloc] init];
203   
204   CTGradientElement color1;
205   color1.red = color1.green = color1.blue  = 0.95;
206   color1.alpha = 1.00;
207   color1.position = 0;
208   
209   CTGradientElement color2;
210   color2.red = color2.green = color2.blue  = 0.83;
211   color2.alpha = 1.00;
212   color2.position = 11.5/23;
213   
214   CTGradientElement color3;
215   color3.red = color3.green = color3.blue  = 0.95;
216   color3.alpha = 1.00;
217   color3.position = 11.5/23;
218   
219   CTGradientElement color4;
220   color4.red = color4.green = color4.blue  = 0.92;
221   color4.alpha = 1.00;
222   color4.position = 1;
223   
224   [newInstance addElement:&color1];
225   [newInstance addElement:&color2];
226   [newInstance addElement:&color3];
227   [newInstance addElement:&color4];
228   
229   return [newInstance autorelease];
230   }
232 + (id)aquaPressedGradient
233   {
234   id newInstance = [[[self class] alloc] init];
235   
236   CTGradientElement color1;
237   color1.red = color1.green = color1.blue  = 0.80;
238   color1.alpha = 1.00;
239   color1.position = 0;
240   
241   CTGradientElement color2;
242   color2.red = color2.green = color2.blue  = 0.64;
243   color2.alpha = 1.00;
244   color2.position = 11.5/23;
245   
246   CTGradientElement color3;
247   color3.red = color3.green = color3.blue  = 0.80;
248   color3.alpha = 1.00;
249   color3.position = 11.5/23;
250   
251   CTGradientElement color4;
252   color4.red = color4.green = color4.blue  = 0.77;
253   color4.alpha = 1.00;
254   color4.position = 1;
255   
256   [newInstance addElement:&color1];
257   [newInstance addElement:&color2];
258   [newInstance addElement:&color3];
259   [newInstance addElement:&color4];
260   
261   return [newInstance autorelease];
262   }
264 + (id)unifiedSelectedGradient
265   {
266   id newInstance = [[[self class] alloc] init];
267   
268   CTGradientElement color1;
269   color1.red = color1.green = color1.blue  = 0.85;
270   color1.alpha = 1.00;
271   color1.position = 0;
272   
273   CTGradientElement color2;
274   color2.red = color2.green = color2.blue  = 0.95;
275   color2.alpha = 1.00;
276   color2.position = 1;
277   
278   [newInstance addElement:&color1];
279   [newInstance addElement:&color2];
280   
281   return [newInstance autorelease];
282   }
284 + (id)unifiedNormalGradient
285   {
286   id newInstance = [[[self class] alloc] init];
287   
288   CTGradientElement color1;
289   color1.red = color1.green = color1.blue  = 0.75;
290   color1.alpha = 1.00;
291   color1.position = 0;
292   
293   CTGradientElement color2;
294   color2.red = color2.green = color2.blue  = 0.90;
295   color2.alpha = 1.00;
296   color2.position = 1;
297   
298   [newInstance addElement:&color1];
299   [newInstance addElement:&color2];
300   
301   return [newInstance autorelease];
302   }
304 + (id)unifiedPressedGradient
305   {
306   id newInstance = [[[self class] alloc] init];
307   
308   CTGradientElement color1;
309   color1.red = color1.green = color1.blue  = 0.60;
310   color1.alpha = 1.00;
311   color1.position = 0;
312   
313   CTGradientElement color2;
314   color2.red = color2.green = color2.blue  = 0.75;
315   color2.alpha = 1.00;
316   color2.position = 1;
317   
318   [newInstance addElement:&color1];
319   [newInstance addElement:&color2];
320   
321   return [newInstance autorelease];
322   }
324 + (id)unifiedDarkGradient
325   {
326   id newInstance = [[[self class] alloc] init];
327   
328   CTGradientElement color1;
329   color1.red = color1.green = color1.blue  = 0.68;
330   color1.alpha = 1.00;
331   color1.position = 0;
332   
333   CTGradientElement color2;
334   color2.red = color2.green = color2.blue  = 0.83;
335   color2.alpha = 1.00;
336   color2.position = 1;
337   
338   [newInstance addElement:&color1];
339   [newInstance addElement:&color2];
340   
341   return [newInstance autorelease];
342   }
344 + (id)sourceListSelectedGradient
345   {
346   id newInstance = [[[self class] alloc] init];
347   
348   CTGradientElement color1;
349   color1.red   = 0.06;
350   color1.green = 0.37;
351   color1.blue  = 0.85;
352   color1.alpha = 1.00;
353   color1.position = 0;
354   
355   CTGradientElement color2;
356   color2.red   = 0.30;
357   color2.green = 0.60;
358   color2.blue  = 0.92;
359   color2.alpha = 1.00;
360   color2.position = 1;
361   
362   [newInstance addElement:&color1];
363   [newInstance addElement:&color2];
364   
365   return [newInstance autorelease];
366   }
368 + (id)sourceListUnselectedGradient
369   {
370   id newInstance = [[[self class] alloc] init];
371   
372   CTGradientElement color1;
373   color1.red   = 0.43;
374   color1.green = 0.43;
375   color1.blue  = 0.43;
376   color1.alpha = 1.00;
377   color1.position = 0;
378   
379   CTGradientElement color2;
380   color2.red   = 0.60;
381   color2.green = 0.60;
382   color2.blue  = 0.60;
383   color2.alpha = 1.00;
384   color2.position = 1;
385   
386   [newInstance addElement:&color1];
387   [newInstance addElement:&color2];
388   
389   return [newInstance autorelease];
390   }
392 + (id)rainbowGradient
393   {
394   id newInstance = [[[self class] alloc] init];
395   
396   CTGradientElement color1;
397   color1.red   = 1.00;
398   color1.green = 0.00;
399   color1.blue  = 0.00;
400   color1.alpha = 1.00;
401   color1.position = 0.0;
402   
403   CTGradientElement color2;
404   color2.red   = 0.54;
405   color2.green = 0.00;
406   color2.blue  = 1.00;
407   color2.alpha = 1.00;
408   color2.position = 1.0;
409     
410   [newInstance addElement:&color1];
411   [newInstance addElement:&color2];
412   
413   [newInstance setBlendingMode:CTChromaticBlendingMode];
414   
415   return [newInstance autorelease];
416   }
418 + (id)hydrogenSpectrumGradient
419   {
420   id newInstance = [[[self class] alloc] init];
421   
422   struct {float hue; float position; float width;} colorBands[4];
423   
424   colorBands[0].hue = 22;
425   colorBands[0].position = .145;
426   colorBands[0].width = .01;
427   
428   colorBands[1].hue = 200;
429   colorBands[1].position = .71;
430   colorBands[1].width = .008;
431   
432   colorBands[2].hue = 253;
433   colorBands[2].position = .885;
434   colorBands[2].width = .005;
435   
436   colorBands[3].hue = 275;
437   colorBands[3].position = .965;
438   colorBands[3].width = .003;
439   
440   int i;
441   /////////////////////////////
442   for(i = 0; i < 4; i++)
443         {       
444         float color[4];
445         color[0] = colorBands[i].hue - 180*colorBands[i].width;
446         color[1] = 1;
447         color[2] = 0.001;
448         color[3] = 1;
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;
456         
457         
458         color[0] = colorBands[i].hue;
459         color[1] = 1;
460         color[2] = 1;
461         color[3] = 1;
462         transformHSV_RGB(color);
463         CTGradientElement band;
464         band.red   = color[0];
465         band.green = color[1];
466         band.blue  = color[2];
467         band.alpha = color[3];
468         band.position = colorBands[i].position;
469         
470         color[0] = colorBands[i].hue + 180*colorBands[i].width;
471         color[1] = 1;
472         color[2] = 0.001;
473         color[3] = 1;
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;
481         
482         
483         [newInstance addElement:&fadeIn];
484         [newInstance addElement:&band];
485         [newInstance addElement:&fadeOut];
486         }
487   
488   [newInstance setBlendingMode:CTChromaticBlendingMode];
489   
490   return [newInstance autorelease];
491   }
493 #pragma mark -
497 #pragma mark Modification
498 - (CTGradient *)gradientWithAlphaComponent:(float)alpha
499   {
500   id newInstance = [[[self class] alloc] init];
501   
502   CTGradientElement *curElement = elementList;
503   CTGradientElement tempElement;
505   while(curElement != nil)
506         {
507         tempElement = *curElement;
508         tempElement.alpha = alpha;
509         [newInstance addElement:&tempElement];
510         
511         curElement = curElement->nextElement;
512         }
513   
514   return [newInstance autorelease];
515   }
517 - (CTGradient *)gradientWithBlendingMode:(CTGradientBlendingMode)mode
518   {
519   CTGradient *newGradient = [self copy];  
520   
521   [newGradient setBlendingMode:mode];
522   
523   return [newGradient autorelease];
524   }
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
530   {
531   CTGradient *newGradient = [self copy];
532   CTGradientElement newGradientElement;
533   
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;
540   
541   //Pass it off to addElement to take care of adding it to the elementList
542   [newGradient addElement:&newGradientElement];
543   
544   return [newGradient autorelease];
545   }
548 //Removes the color stop at <position> from elementList
549 - (CTGradient *)removeColorStopAtPosition:(float)position
550   {
551   CTGradient *newGradient = [self copy];
552   CTGradientElement removedElement = [newGradient removeElementAtPosition:position];
553   
554   if(isnan(removedElement.position))
555         [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtPosition:]: no such colorStop at position (%f)", [self class], position];
556   
557   return [newGradient autorelease];
558   }
560 - (CTGradient *)removeColorStopAtIndex:(unsigned)index
561   {
562   CTGradient *newGradient = [self copy];
563   CTGradientElement removedElement = [newGradient removeElementAtIndex:index];
564   
565   if(isnan(removedElement.position))
566         [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtIndex:]: index (%i) beyond bounds", [self class], index];
567   
568   return [newGradient autorelease];
569   }
570 #pragma mark -
574 #pragma mark Information
575 - (CTGradientBlendingMode)blendingMode
576   {
577   return blendingMode;
578   }
580 //Returns color at <position> in gradient
581 - (NSColor *)colorStopAtIndex:(unsigned)index
582   {
583   CTGradientElement *element = [self elementAtIndex:index];
584   
585   if(element != nil)
586         return [NSColor colorWithCalibratedRed:element->red 
587                                                                          green:element->green
588                                                                           blue:element->blue
589                                                                          alpha:element->alpha];
590   
591   [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtIndex:]: index (%i) beyond bounds", [self class], index];
592   
593   return nil;
594   }
596 - (NSColor *)colorAtPosition:(float)position
597   {
598   float components[4];
599   
600   switch(blendingMode)
601         {
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;
608         }
609   
610   
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]];
615   }
616 #pragma mark -
620 #pragma mark Drawing
621 - (void)drawSwatchInRect:(NSRect)rect
622   {
623   [self fillRect:rect angle:45];
624   }
626 - (void)fillRect:(NSRect)rect angle:(float)angle
627   {
628   //First Calculate where the beginning and ending points should be
629   CGPoint startPoint;
630   CGPoint endPoint;
631   
632   if(angle == 0)                //screw the calculations - we know the answer
633         {
634         startPoint = CGPointMake(NSMinX(rect), NSMinY(rect));   //right of rect
635         endPoint   = CGPointMake(NSMaxX(rect), NSMinY(rect));   //left  of rect
636         }
637   else if(angle == 90)  //same as above
638         {
639         startPoint = CGPointMake(NSMinX(rect), NSMinY(rect));   //bottom of rect
640         endPoint   = CGPointMake(NSMinX(rect), NSMaxY(rect));   //top    of rect
641         }
642   else                                          //ok, we'll do the calculations now 
643         {
644         float x,y;
645         float sina, cosa, tana;
646         
647         float length;
648         float deltax,
649                   deltay;
650         
651         float rangle = angle * pi/180;  //convert the angle to radians
652         
653         if(fabsf(tan(rangle))<=1)       //for range [-45,45], [135,225]
654                 {
655                 x = NSWidth(rect);
656                 y = NSHeight(rect);
657                 
658                 sina = sin(rangle);
659                 cosa = cos(rangle);
660                 tana = tan(rangle);
661                 
662                 length = x/fabsf(cosa)+(y-x*fabsf(tana))*fabsf(sina);
663                 
664                 deltax = length*cosa/2;
665                 deltay = length*sina/2;
666                 }
667         else                                            //for range [45,135], [225,315]
668                 {
669                 x = NSHeight(rect);
670                 y = NSWidth(rect);
671                 
672                 sina = sin(rangle - 90*pi/180);
673                 cosa = cos(rangle - 90*pi/180);
674                 tana = tan(rangle - 90*pi/180);
675                 
676                 length = x/fabsf(cosa)+(y-x*fabsf(tana))*fabsf(sina);
677                 
678                 deltax =-length*sina/2;
679                 deltay = length*cosa/2;
680                 }
681   
682         startPoint = CGPointMake(NSMidX(rect)-deltax, NSMidY(rect)-deltay);
683         endPoint   = CGPointMake(NSMidX(rect)+deltax, NSMidY(rect)+deltay);
684         }
685   
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);
691           #else
692                 CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
693           #endif
694           CGShadingRef myCGShading = CGShadingCreateAxial(colorspace, startPoint, endPoint, gradientFunction, false, false);
695           
696           CGContextClipToRect (currentContext, *(CGRect *)&rect);       //This is where the action happens
697           CGContextDrawShading(currentContext, myCGShading);
698           
699           CGShadingRelease(myCGShading);
700           CGColorSpaceRelease(colorspace );
701   CGContextRestoreGState(currentContext);
702   }
704 - (void)radialFillRect:(NSRect)rect
705   {
706   CGPoint startPoint, endPoint;
707   float startRadius, endRadius;
708   float scalex, scaley, transx, transy;
709   
710   startPoint = endPoint = CGPointMake(NSMidX(rect), NSMidY(rect));
711   
712   startRadius = -1;
713   if(NSHeight(rect)>NSWidth(rect))
714         {
715         scalex = NSWidth(rect)/NSHeight(rect);
716         transx = (NSHeight(rect)-NSWidth(rect))/2;
717         scaley = 1;
718         transy = 1;
719         endRadius = NSHeight(rect)/2;
720         }
721   else
722         {
723         scalex = 1;
724         transx = 1;
725         scaley = NSHeight(rect)/NSWidth(rect);
726         transy = (NSWidth(rect)-NSHeight(rect))/2;
727         endRadius = NSWidth(rect)/2;
728         }
729   
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);
735           #else
736                 CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
737           #endif
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
744           
745           CGShadingRelease(myCGShading);
746           CGColorSpaceRelease(colorspace);
747   CGContextRestoreGState(currentContext);
748   }
750 - (void)fillBezierPath:(NSBezierPath *)path angle:(float)angle
751   {
752   NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
753   [currentContext saveGraphicsState];
754         NSAffineTransform *transform = [[NSAffineTransform alloc] init];
755         
756         [transform rotateByDegrees:-angle];
757         [path transformUsingAffineTransform:transform];
758         [transform invert];
759         [transform concat];
760         
761         [path addClip];
762         [self fillRect:[path bounds] angle:0];
763         [path transformUsingAffineTransform:transform];
764         [transform release];
765   [currentContext restoreGraphicsState];
766   }
767 - (void)radialFillBezierPath:(NSBezierPath *)path
768   {
769   NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
770   [currentContext saveGraphicsState];
771         [path addClip];
772         [self radialFillRect:[path bounds]];
773   [currentContext restoreGraphicsState];
774   }
775 #pragma mark -
779 #pragma mark Private Methods
780 - (void)setBlendingMode:(CTGradientBlendingMode)mode;
781   {
782   blendingMode = mode;
783   
784   //Choose what blending function to use
785   void *evaluationFunction;
786   switch(blendingMode)
787         {
788         case CTLinearBlendingMode:
789                  evaluationFunction = &linearEvaluation;                        break;
790         case CTChromaticBlendingMode:
791                  evaluationFunction = &chromaticEvaluation;                     break;
792         case CTInverseChromaticBlendingMode:
793                  evaluationFunction = &inverseChromaticEvaluation;      break;
794         }
795   
796   //replace the current CoreGraphics Function with new one
797   if(gradientFunction != NULL)
798           CGFunctionRelease(gradientFunction);
799     
800   CGFunctionCallbacks evaluationCallbackInfo = {0 , evaluationFunction, NULL};  //Version, evaluator function, cleanup function
801   
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)
804   
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
809   }
811 - (void)addElement:(CTGradientElement *)newElement
812   {
813   if(elementList == nil || newElement->position < elementList->position)        //inserting at beginning of list
814         {
815         CTGradientElement *tmpNext = elementList;
816         elementList = malloc(sizeof(CTGradientElement));
817         *elementList = *newElement;
818         elementList->nextElement = tmpNext;
819         }
820   else                                                                                                                                          //inserting somewhere inside list
821         {
822         CTGradientElement *curElement = elementList;
823         
824         while(curElement->nextElement != nil && !((curElement->position <= newElement->position) && (newElement->position < curElement->nextElement->position)))
825                 {
826                 curElement = curElement->nextElement;
827                 }
828         
829         CTGradientElement *tmpNext = curElement->nextElement;
830         curElement->nextElement = malloc(sizeof(CTGradientElement));
831         *(curElement->nextElement) = *newElement;
832         curElement->nextElement->nextElement = tmpNext;
833         }
834   }
836 - (CTGradientElement)removeElementAtIndex:(unsigned)index
837   {
838   CTGradientElement removedElement;
839   
840   if(elementList != nil)
841         {
842         if(index == 0)
843                 {
844                 CTGradientElement *tmpNext = elementList;
845                 elementList = elementList->nextElement;
846                 
847                 removedElement = *tmpNext;
848                 free(tmpNext);
849                 
850                 return removedElement;
851                 }
852         
853         unsigned count = 1;             //we want to start one ahead
854         CTGradientElement *currentElement = elementList;
855         while(currentElement->nextElement != nil)
856                 {
857                 if(count == index)
858                         {
859                         CTGradientElement *tmpNext  = currentElement->nextElement;
860                         currentElement->nextElement = currentElement->nextElement->nextElement;
861                         
862                         removedElement = *tmpNext;
863                         free(tmpNext);
865                         return removedElement;
866                         }
868                 count++;
869                 currentElement = currentElement->nextElement;
870                 }
871         }
872   
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;
880   
881   return removedElement;
882   }
884 - (CTGradientElement)removeElementAtPosition:(float)position
885   {
886   CTGradientElement removedElement;
887   
888   if(elementList != nil)
889         {
890         if(elementList->position == position)
891                 {
892                 CTGradientElement *tmpNext = elementList;
893                 elementList = elementList->nextElement;
894                 
895                 removedElement = *tmpNext;
896                 free(tmpNext);
897                 
898                 return removedElement;
899                 }
900         else
901                 {
902                 CTGradientElement *curElement = elementList;
903                 while(curElement->nextElement != nil)
904                         {
905                         if(curElement->nextElement->position == position)
906                                 {
907                                 CTGradientElement *tmpNext = curElement->nextElement;
908                                 curElement->nextElement = curElement->nextElement->nextElement;
909                                 
910                                 removedElement = *tmpNext;
911                                 free(tmpNext);
913                                 return removedElement;
914                                 }
915                         }
916                 }
917         }
918   
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;
926   
927   return removedElement;
928   }
931 - (CTGradientElement *)elementAtIndex:(unsigned)index;                  
932   {
933   unsigned count = 0;
934   CTGradientElement *currentElement = elementList;
935   
936   while(currentElement != nil)
937         {
938         if(count == index)
939                 return currentElement;
940         
941         count++;
942         currentElement = currentElement->nextElement;
943         }
944   
945   return nil;
946   }
947 #pragma mark -
951 #pragma mark Core Graphics
952 //////////////////////////////////////Blending Functions/////////////////////////////////////
953 void linearEvaluation (void *info, const float *in, float *out)
954   {
955   float position = *in;
956   
957   if(*(CTGradientElement **)info == nil)        //if elementList is empty return clear color
958         {
959         out[0] = out[1] = out[2] = out[3] = 1;
960         return;
961         }
962   
963   //This grabs the first two colors in the sequence
964   CTGradientElement *color1 = *(CTGradientElement **)info;
965   CTGradientElement *color2 = color1->nextElement;
966   
967   //make sure first color and second color are on other sides of position
968   while(color2 != nil && color2->position < position)
969         {
970         color1 = color2;
971         color2 = color1->nextElement;
972         }
973   //if we don't have another color then make next color the same color
974   if(color2 == nil)
975     {
976         color2 = color1;
977     }
978   
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   //-------------------------------------
987   
988   if(position <= color1->position)                      //Make all below color color1's position equal to color1
989         {
990         out[0] = color1->red; 
991         out[1] = color1->green;
992         out[2] = color1->blue;
993         out[3] = color1->alpha;
994         }
995   else if (position >= color2->position)        //Make all above color color2's position equal to color2
996         {
997         out[0] = color2->red; 
998         out[1] = color2->green;
999         out[2] = color2->blue;
1000         out[3] = color2->alpha;
1001         }
1002   else                                                                          //Interpolate color at postions between color1 and color1
1003         {
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);
1006         
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;
1011         }
1012   
1013   //Premultiply the color by the alpha.
1014   out[0] *= out[3];
1015   out[1] *= out[3];
1016   out[2] *= out[3];
1017   }
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)
1032   {
1033   float position = *in;
1034   
1035   if(*(CTGradientElement **)info == nil)        //if elementList is empty return clear color
1036         {
1037         out[0] = out[1] = out[2] = out[3] = 1;
1038         return;
1039         }
1040   
1041   //This grabs the first two colors in the sequence
1042   CTGradientElement *color1 = *(CTGradientElement **)info;
1043   CTGradientElement *color2 = color1->nextElement;
1044   
1045   float c1[4];
1046   float c2[4];
1047     
1048   //make sure first color and second color are on other sides of position
1049   while(color2 != nil && color2->position < position)
1050         {
1051         color1 = color2;
1052         color2 = color1->nextElement;
1053         }
1054   //if we don't have another color then make next color the same color
1055   if(color2 == nil)
1056     {
1057         color2 = color1;
1058     }
1059   
1060   
1061   c1[0] = color1->red; 
1062   c1[1] = color1->green;
1063   c1[2] = color1->blue;
1064   c1[3] = color1->alpha;
1065   
1066   c2[0] = color2->red; 
1067   c2[1] = color2->green;
1068   c2[2] = color2->blue;
1069   c2[3] = color2->alpha;
1070   
1071   transformRGB_HSV(c1);
1072   transformRGB_HSV(c2);
1073   resolveHSV(c1,c2);
1074   
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
1077   
1078   
1079   if(position <= color1->position)                      //Make all below color color1's position equal to color1
1080         {
1081         out[0] = c1[0]; 
1082         out[1] = c1[1];
1083         out[2] = c1[2];
1084         out[3] = c1[3];
1085         }
1086   else if (position >= color2->position)        //Make all above color color2's position equal to color2
1087         {
1088         out[0] = c2[0]; 
1089         out[1] = c2[1];
1090         out[2] = c2[2];
1091         out[3] = c2[3];
1092         }
1093   else                                                                          //Interpolate color at postions between color1 and color1
1094         {
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);
1097         
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];
1102         }
1103     
1104   transformHSV_RGB(out);
1105   
1106   //Premultiply the color by the alpha.
1107   out[0] *= out[3];
1108   out[1] *= out[3];
1109   out[2] *= out[3];
1110   }
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)
1120   {
1121     float position = *in;
1122   
1123   if(*(CTGradientElement **)info == nil)        //if elementList is empty return clear color
1124         {
1125         out[0] = out[1] = out[2] = out[3] = 1;
1126         return;
1127         }
1128   
1129   //This grabs the first two colors in the sequence
1130   CTGradientElement *color1 = *(CTGradientElement **)info;
1131   CTGradientElement *color2 = color1->nextElement;
1132   
1133   float c1[4];
1134   float c2[4];
1135       
1136   //make sure first color and second color are on other sides of position
1137   while(color2 != nil && color2->position < position)
1138         {
1139         color1 = color2;
1140         color2 = color1->nextElement;
1141         }
1142   //if we don't have another color then make next color the same color
1143   if(color2 == nil)
1144     {
1145         color2 = color1;
1146     }
1148   c1[0] = color1->red; 
1149   c1[1] = color1->green;
1150   c1[2] = color1->blue;
1151   c1[3] = color1->alpha;
1152   
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);
1160   resolveHSV(c1,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
1165   
1166   if(position <= color1->position)                      //Make all below color color1's position equal to color1
1167         {
1168         out[0] = c1[0]; 
1169         out[1] = c1[1];
1170         out[2] = c1[2];
1171         out[3] = c1[3];
1172         }
1173   else if (position >= color2->position)        //Make all above color color2's position equal to color2
1174         {
1175         out[0] = c2[0]; 
1176         out[1] = c2[1];
1177         out[2] = c2[2];
1178         out[3] = c2[3];
1179         }
1180   else                                                                          //Interpolate color at postions between color1 and color1
1181         {
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);
1184         
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];
1189         }
1190     
1191   transformHSV_RGB(out);
1192   
1193   //Premultiply the color by the alpha.
1194   out[0] *= out[3];
1195   out[1] *= out[3];
1196   out[2] *= out[3];
1197   }
1200 void transformRGB_HSV(float *components) //H,S,B -> R,G,B
1201         {
1202         float H, S, V;
1203         float R = components[0],
1204                   G = components[1],
1205                   B = components[2];
1206         
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);
1209         
1210         if(MAX == MIN)
1211                 H = NAN;
1212         else if(MAX == R)
1213                 if(G >= B)
1214                         H = 60*(G-B)/(MAX-MIN)+0;
1215                 else
1216                         H = 60*(G-B)/(MAX-MIN)+360;
1217         else if(MAX == G)
1218                 H = 60*(B-R)/(MAX-MIN)+120;
1219         else if(MAX == B)
1220                 H = 60*(R-G)/(MAX-MIN)+240;
1221         
1222         S = MAX == 0 ? 0 : 1 - MIN/MAX;
1223         V = MAX;
1224         
1225         components[0] = H;
1226         components[1] = S;
1227         components[2] = V;
1228         }
1230 void transformHSV_RGB(float *components) //H,S,B -> R,G,B
1231         {
1232         float R, G, B;
1233         float H = fmodf(components[0],359),     //map to [0,360)
1234                   S = components[1],
1235                   V = components[2];
1236         
1237         int   Hi = (int)floorf(H/60.) % 6;
1238         float f  = H/60-Hi,
1239                   p  = V*(1-S),
1240                   q  = V*(1-f*S),
1241                   t  = V*(1-(1-f)*S);
1242         
1243         switch (Hi)
1244                 {
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;
1251                 }
1252         
1253         components[0] = R;
1254         components[1] = G;
1255         components[2] = B;
1256         }
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];
1266         }
1268 @end