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