source: trunk/Cocoa/AntiRSI/CTBadge/CTGradient.m @ 370

Last change on this file since 370 was 370, checked in by Nicholas Riley, 12 years ago

Apparently in Xcode, 'Commit Entire Project' doesn't. See the log message from [369].

File size: 32.7 KB
Line 
1//
2//  CTGradient.m
3//
4//  Created by Chad Weider on 2/14/07.
5//  Copyright (c) 2007 Chad Weider.
6//  Some rights reserved: <http://creativecommons.org/licenses/by/2.5/>
7//
8//  Version: 1.6
9
10#import "CTGradient.h"
11
12@interface CTGradient (Private)
13- (void)_commonInit;
14- (void)setBlendingMode:(CTGradientBlendingMode)mode;
15- (void)addElement:(CTGradientElement*)newElement;
16
17- (CTGradientElement *)elementAtIndex:(unsigned)index;
18
19- (CTGradientElement)removeElementAtIndex:(unsigned)index;
20- (CTGradientElement)removeElementAtPosition:(float)position;
21@end
22
23//C Fuctions for color blending
24static void linearEvaluation   (void *info, const float *in, float *out);
25static void chromaticEvaluation(void *info, const float *in, float *out);
26static void inverseChromaticEvaluation(void *info, const float *in, float *out);
27static void transformRGB_HSV(float *components);
28static void transformHSV_RGB(float *components);
29static void resolveHSV(float *color1, float *color2);
30
31
32@implementation CTGradient
33/////////////////////////////////////Initialization Type Stuff
34- (id)init
35  {
36  self = [super init];
37 
38  if (self != nil)
39        {
40        [self _commonInit];
41        [self setBlendingMode:CTLinearBlendingMode];
42        }
43  return self;
44  }
45
46- (void)_commonInit
47  {
48  elementList = nil;
49  }
50
51- (void)dealloc
52  {
53  CGFunctionRelease(gradientFunction);
54 
55  CTGradientElement *elementToRemove = elementList;
56  while(elementList != nil)
57        {
58        elementToRemove = elementList;
59        elementList = elementList->nextElement;
60        free(elementToRemove);
61        }
62 
63  [super dealloc];
64  }
65
66- (id)copyWithZone:(NSZone *)zone
67  {
68  CTGradient *copy = [[[self class] allocWithZone:zone] init];
69 
70  //now just copy my elementlist
71  CTGradientElement *currentElement = elementList;
72  while(currentElement != nil)
73        {
74        [copy addElement:currentElement];
75        currentElement = currentElement->nextElement;
76        }
77 
78  [copy setBlendingMode:blendingMode];
79 
80  return copy;
81  }
82
83- (void)encodeWithCoder:(NSCoder *)coder
84  {
85  if([coder allowsKeyedCoding])
86        {
87        unsigned count = 0;
88        CTGradientElement *currentElement = elementList;
89        while(currentElement != nil)
90                {
91                [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->red)];
92                [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->green)];
93                [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->blue)];
94                [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->alpha)];
95                [coder encodeValueOfObjCType:@encode(float) at:&(currentElement->position)];
96               
97                count++;
98                currentElement = currentElement->nextElement;
99                }
100        [coder encodeInt:count forKey:@"CTGradientElementCount"];
101        [coder encodeInt:blendingMode forKey:@"CTGradientBlendingMode"];
102        }
103  else
104        [NSException raise:NSInvalidArchiveOperationException format:@"Only supports NSKeyedArchiver coders"];
105  }
106
107- (id)initWithCoder:(NSCoder *)coder
108  {
109  [self _commonInit];
110 
111  [self setBlendingMode:[coder decodeIntForKey:@"CTGradientBlendingMode"]];
112  unsigned count = [coder decodeIntForKey:@"CTGradientElementCount"];
113 
114  while(count != 0)
115        {
116    CTGradientElement newElement;
117       
118        [coder decodeValueOfObjCType:@encode(float) at:&(newElement.red)];
119        [coder decodeValueOfObjCType:@encode(float) at:&(newElement.green)];
120        [coder decodeValueOfObjCType:@encode(float) at:&(newElement.blue)];
121        [coder decodeValueOfObjCType:@encode(float) at:&(newElement.alpha)];
122        [coder decodeValueOfObjCType:@encode(float) at:&(newElement.position)];
123       
124        count--;
125        [self addElement:&newElement];
126        }
127  return self;
128  }
129#pragma mark -
130
131
132
133#pragma mark Creation
134+ (id)gradientWithBeginningColor:(NSColor *)begin endingColor:(NSColor *)end
135  {
136  id newInstance = [[[self class] alloc] init];
137 
138  CTGradientElement color1;
139  CTGradientElement color2;
140 
141  [[begin colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&color1.red
142                                                                                                                           green:&color1.green
143                                                                                                                                blue:&color1.blue
144                                                                                                                           alpha:&color1.alpha];
145 
146  [[end   colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&color2.red
147                                                                                                                           green:&color2.green
148                                                                                                                                blue:&color2.blue
149                                                                                                                           alpha:&color2.alpha]; 
150  color1.position = 0;
151  color2.position = 1;
152 
153  [newInstance addElement:&color1];
154  [newInstance addElement:&color2];
155 
156  return [newInstance autorelease];
157  }
158
159+ (id)aquaSelectedGradient
160  {
161  id newInstance = [[[self class] alloc] init];
162 
163  CTGradientElement color1;
164  color1.red   = 0.58;
165  color1.green = 0.86;
166  color1.blue  = 0.98;
167  color1.alpha = 1.00;
168  color1.position = 0;
169 
170  CTGradientElement color2;
171  color2.red   = 0.42;
172  color2.green = 0.68;
173  color2.blue  = 0.90;
174  color2.alpha = 1.00;
175  color2.position = 11.5/23;
176 
177  CTGradientElement color3;
178  color3.red   = 0.64;
179  color3.green = 0.80;
180  color3.blue  = 0.94;
181  color3.alpha = 1.00;
182  color3.position = 11.5/23;
183 
184  CTGradientElement color4;
185  color4.red   = 0.56;
186  color4.green = 0.70;
187  color4.blue  = 0.90;
188  color4.alpha = 1.00;
189  color4.position = 1;
190 
191  [newInstance addElement:&color1];
192  [newInstance addElement:&color2];
193  [newInstance addElement:&color3];
194  [newInstance addElement:&color4];
195 
196  return [newInstance autorelease];
197  }
198
199+ (id)aquaNormalGradient
200  {
201  id newInstance = [[[self class] alloc] init];
202 
203  CTGradientElement color1;
204  color1.red = color1.green = color1.blue  = 0.95;
205  color1.alpha = 1.00;
206  color1.position = 0;
207 
208  CTGradientElement color2;
209  color2.red = color2.green = color2.blue  = 0.83;
210  color2.alpha = 1.00;
211  color2.position = 11.5/23;
212 
213  CTGradientElement color3;
214  color3.red = color3.green = color3.blue  = 0.95;
215  color3.alpha = 1.00;
216  color3.position = 11.5/23;
217 
218  CTGradientElement color4;
219  color4.red = color4.green = color4.blue  = 0.92;
220  color4.alpha = 1.00;
221  color4.position = 1;
222 
223  [newInstance addElement:&color1];
224  [newInstance addElement:&color2];
225  [newInstance addElement:&color3];
226  [newInstance addElement:&color4];
227 
228  return [newInstance autorelease];
229  }
230
231+ (id)aquaPressedGradient
232  {
233  id newInstance = [[[self class] alloc] init];
234 
235  CTGradientElement color1;
236  color1.red = color1.green = color1.blue  = 0.80;
237  color1.alpha = 1.00;
238  color1.position = 0;
239 
240  CTGradientElement color2;
241  color2.red = color2.green = color2.blue  = 0.64;
242  color2.alpha = 1.00;
243  color2.position = 11.5/23;
244 
245  CTGradientElement color3;
246  color3.red = color3.green = color3.blue  = 0.80;
247  color3.alpha = 1.00;
248  color3.position = 11.5/23;
249 
250  CTGradientElement color4;
251  color4.red = color4.green = color4.blue  = 0.77;
252  color4.alpha = 1.00;
253  color4.position = 1;
254 
255  [newInstance addElement:&color1];
256  [newInstance addElement:&color2];
257  [newInstance addElement:&color3];
258  [newInstance addElement:&color4];
259 
260  return [newInstance autorelease];
261  }
262
263+ (id)unifiedSelectedGradient
264  {
265  id newInstance = [[[self class] alloc] init];
266 
267  CTGradientElement color1;
268  color1.red = color1.green = color1.blue  = 0.85;
269  color1.alpha = 1.00;
270  color1.position = 0;
271 
272  CTGradientElement color2;
273  color2.red = color2.green = color2.blue  = 0.95;
274  color2.alpha = 1.00;
275  color2.position = 1;
276 
277  [newInstance addElement:&color1];
278  [newInstance addElement:&color2];
279 
280  return [newInstance autorelease];
281  }
282
283+ (id)unifiedNormalGradient
284  {
285  id newInstance = [[[self class] alloc] init];
286 
287  CTGradientElement color1;
288  color1.red = color1.green = color1.blue  = 0.75;
289  color1.alpha = 1.00;
290  color1.position = 0;
291 
292  CTGradientElement color2;
293  color2.red = color2.green = color2.blue  = 0.90;
294  color2.alpha = 1.00;
295  color2.position = 1;
296 
297  [newInstance addElement:&color1];
298  [newInstance addElement:&color2];
299 
300  return [newInstance autorelease];
301  }
302
303+ (id)unifiedPressedGradient
304  {
305  id newInstance = [[[self class] alloc] init];
306 
307  CTGradientElement color1;
308  color1.red = color1.green = color1.blue  = 0.60;
309  color1.alpha = 1.00;
310  color1.position = 0;
311 
312  CTGradientElement color2;
313  color2.red = color2.green = color2.blue  = 0.75;
314  color2.alpha = 1.00;
315  color2.position = 1;
316 
317  [newInstance addElement:&color1];
318  [newInstance addElement:&color2];
319 
320  return [newInstance autorelease];
321  }
322
323+ (id)unifiedDarkGradient
324  {
325  id newInstance = [[[self class] alloc] init];
326 
327  CTGradientElement color1;
328  color1.red = color1.green = color1.blue  = 0.68;
329  color1.alpha = 1.00;
330  color1.position = 0;
331 
332  CTGradientElement color2;
333  color2.red = color2.green = color2.blue  = 0.83;
334  color2.alpha = 1.00;
335  color2.position = 1;
336 
337  [newInstance addElement:&color1];
338  [newInstance addElement:&color2];
339 
340  return [newInstance autorelease];
341  }
342
343+ (id)sourceListSelectedGradient
344  {
345  id newInstance = [[[self class] alloc] init];
346 
347  CTGradientElement color1;
348  color1.red   = 0.06;
349  color1.green = 0.37;
350  color1.blue  = 0.85;
351  color1.alpha = 1.00;
352  color1.position = 0;
353 
354  CTGradientElement color2;
355  color2.red   = 0.30;
356  color2.green = 0.60;
357  color2.blue  = 0.92;
358  color2.alpha = 1.00;
359  color2.position = 1;
360 
361  [newInstance addElement:&color1];
362  [newInstance addElement:&color2];
363 
364  return [newInstance autorelease];
365  }
366
367+ (id)sourceListUnselectedGradient
368  {
369  id newInstance = [[[self class] alloc] init];
370 
371  CTGradientElement color1;
372  color1.red   = 0.43;
373  color1.green = 0.43;
374  color1.blue  = 0.43;
375  color1.alpha = 1.00;
376  color1.position = 0;
377 
378  CTGradientElement color2;
379  color2.red   = 0.60;
380  color2.green = 0.60;
381  color2.blue  = 0.60;
382  color2.alpha = 1.00;
383  color2.position = 1;
384 
385  [newInstance addElement:&color1];
386  [newInstance addElement:&color2];
387 
388  return [newInstance autorelease];
389  }
390
391+ (id)rainbowGradient
392  {
393  id newInstance = [[[self class] alloc] init];
394 
395  CTGradientElement color1;
396  color1.red   = 1.00;
397  color1.green = 0.00;
398  color1.blue  = 0.00;
399  color1.alpha = 1.00;
400  color1.position = 0.0;
401 
402  CTGradientElement color2;
403  color2.red   = 0.54;
404  color2.green = 0.00;
405  color2.blue  = 1.00;
406  color2.alpha = 1.00;
407  color2.position = 1.0;
408   
409  [newInstance addElement:&color1];
410  [newInstance addElement:&color2];
411 
412  [newInstance setBlendingMode:CTChromaticBlendingMode];
413 
414  return [newInstance autorelease];
415  }
416
417+ (id)hydrogenSpectrumGradient
418  {
419  id newInstance = [[[self class] alloc] init];
420 
421  struct {float hue; float position; float width;} colorBands[4];
422 
423  colorBands[0].hue = 22;
424  colorBands[0].position = .145;
425  colorBands[0].width = .01;
426 
427  colorBands[1].hue = 200;
428  colorBands[1].position = .71;
429  colorBands[1].width = .008;
430 
431  colorBands[2].hue = 253;
432  colorBands[2].position = .885;
433  colorBands[2].width = .005;
434 
435  colorBands[3].hue = 275;
436  colorBands[3].position = .965;
437  colorBands[3].width = .003;
438 
439  int i;
440  /////////////////////////////
441  for(i = 0; i < 4; i++)
442        {       
443        float color[4];
444        color[0] = colorBands[i].hue - 180*colorBands[i].width;
445        color[1] = 1;
446        color[2] = 0.001;
447        color[3] = 1;
448        transformHSV_RGB(color);
449        CTGradientElement fadeIn;
450        fadeIn.red   = color[0];
451        fadeIn.green = color[1];
452        fadeIn.blue  = color[2];
453        fadeIn.alpha = color[3];
454        fadeIn.position = colorBands[i].position - colorBands[i].width;
455       
456       
457        color[0] = colorBands[i].hue;
458        color[1] = 1;
459        color[2] = 1;
460        color[3] = 1;
461        transformHSV_RGB(color);
462        CTGradientElement band;
463        band.red   = color[0];
464        band.green = color[1];
465        band.blue  = color[2];
466        band.alpha = color[3];
467        band.position = colorBands[i].position;
468       
469        color[0] = colorBands[i].hue + 180*colorBands[i].width;
470        color[1] = 1;
471        color[2] = 0.001;
472        color[3] = 1;
473        transformHSV_RGB(color);
474        CTGradientElement fadeOut;
475        fadeOut.red   = color[0];
476        fadeOut.green = color[1];
477        fadeOut.blue  = color[2];
478        fadeOut.alpha = color[3];
479        fadeOut.position = colorBands[i].position + colorBands[i].width;
480       
481       
482        [newInstance addElement:&fadeIn];
483        [newInstance addElement:&band];
484        [newInstance addElement:&fadeOut];
485        }
486 
487  [newInstance setBlendingMode:CTChromaticBlendingMode];
488 
489  return [newInstance autorelease];
490  }
491
492#pragma mark -
493
494
495
496#pragma mark Modification
497- (CTGradient *)gradientWithAlphaComponent:(float)alpha
498  {
499  id newInstance = [[[self class] alloc] init];
500 
501  CTGradientElement *curElement = elementList;
502  CTGradientElement tempElement;
503
504  while(curElement != nil)
505        {
506        tempElement = *curElement;
507        tempElement.alpha = alpha;
508        [newInstance addElement:&tempElement];
509       
510        curElement = curElement->nextElement;
511        }
512 
513  return [newInstance autorelease];
514  }
515
516- (CTGradient *)gradientWithBlendingMode:(CTGradientBlendingMode)mode
517  {
518  CTGradient *newGradient = [self copy]; 
519 
520  [newGradient setBlendingMode:mode];
521 
522  return [newGradient autorelease];
523  }
524
525
526//Adds a color stop with <color> at <position> in elementList
527//(if two elements are at the same position then added imediatly after the one that was there already)
528- (CTGradient *)addColorStop:(NSColor *)color atPosition:(float)position
529  {
530  CTGradient *newGradient = [self copy];
531  CTGradientElement newGradientElement;
532 
533  //put the components of color into the newGradientElement - must make sure it is a RGB color (not Gray or CMYK)
534  [[color colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&newGradientElement.red
535                                                                                                                           green:&newGradientElement.green
536                                                                                                                                blue:&newGradientElement.blue
537                                                                                                                           alpha:&newGradientElement.alpha];
538  newGradientElement.position = position;
539 
540  //Pass it off to addElement to take care of adding it to the elementList
541  [newGradient addElement:&newGradientElement];
542 
543  return [newGradient autorelease];
544  }
545
546
547//Removes the color stop at <position> from elementList
548- (CTGradient *)removeColorStopAtPosition:(float)position
549  {
550  CTGradient *newGradient = [self copy];
551  CTGradientElement removedElement = [newGradient removeElementAtPosition:position];
552 
553  if(isnan(removedElement.position))
554        [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtPosition:]: no such colorStop at position (%f)", [self class], position];
555 
556  return [newGradient autorelease];
557  }
558
559- (CTGradient *)removeColorStopAtIndex:(unsigned)index
560  {
561  CTGradient *newGradient = [self copy];
562  CTGradientElement removedElement = [newGradient removeElementAtIndex:index];
563 
564  if(isnan(removedElement.position))
565        [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtIndex:]: index (%i) beyond bounds", [self class], index];
566 
567  return [newGradient autorelease];
568  }
569#pragma mark -
570
571
572
573#pragma mark Information
574- (CTGradientBlendingMode)blendingMode
575  {
576  return blendingMode;
577  }
578
579//Returns color at <position> in gradient
580- (NSColor *)colorStopAtIndex:(unsigned)index
581  {
582  CTGradientElement *element = [self elementAtIndex:index];
583 
584  if(element != nil)
585        return [NSColor colorWithCalibratedRed:element->red
586                                                                         green:element->green
587                                                                          blue:element->blue
588                                                                         alpha:element->alpha];
589 
590  [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtIndex:]: index (%i) beyond bounds", [self class], index];
591 
592  return nil;
593  }
594
595- (NSColor *)colorAtPosition:(float)position
596  {
597  float components[4];
598 
599  switch(blendingMode)
600        {
601        case CTLinearBlendingMode:
602                 linearEvaluation(&elementList, &position, components);                         break;
603        case CTChromaticBlendingMode:
604                 chromaticEvaluation(&elementList, &position, components);                      break;
605        case CTInverseChromaticBlendingMode:
606                 inverseChromaticEvaluation(&elementList, &position, components);       break;
607        }
608 
609 
610  return [NSColor colorWithCalibratedRed:components[0]
611                                                                   green:components[1]
612                                                                    blue:components[2]
613                                                                   alpha:components[3]];
614  }
615#pragma mark -
616
617
618
619#pragma mark Drawing
620- (void)drawSwatchInRect:(NSRect)rect
621  {
622  [self fillRect:rect angle:45];
623  }
624
625- (void)fillRect:(NSRect)rect angle:(float)angle
626  {
627  //First Calculate where the beginning and ending points should be
628  CGPoint startPoint;
629  CGPoint endPoint;
630 
631  if(angle == 0)                //screw the calculations - we know the answer
632        {
633        startPoint = CGPointMake(NSMinX(rect), NSMinY(rect));   //right of rect
634        endPoint   = CGPointMake(NSMaxX(rect), NSMinY(rect));   //left  of rect
635        }
636  else if(angle == 90)  //same as above
637        {
638        startPoint = CGPointMake(NSMinX(rect), NSMinY(rect));   //bottom of rect
639        endPoint   = CGPointMake(NSMinX(rect), NSMaxY(rect));   //top    of rect
640        }
641  else                                          //ok, we'll do the calculations now
642        {
643        float x,y;
644        float sina, cosa, tana;
645       
646        float length;
647        float deltax,
648                  deltay;
649       
650        float rangle = angle * pi/180;  //convert the angle to radians
651       
652        if(fabsf(tan(rangle))<=1)       //for range [-45,45], [135,225]
653                {
654                x = NSWidth(rect);
655                y = NSHeight(rect);
656               
657                sina = sin(rangle);
658                cosa = cos(rangle);
659                tana = tan(rangle);
660               
661                length = x/fabsf(cosa)+(y-x*fabsf(tana))*fabsf(sina);
662               
663                deltax = length*cosa/2;
664                deltay = length*sina/2;
665                }
666        else                                            //for range [45,135], [225,315]
667                {
668                x = NSHeight(rect);
669                y = NSWidth(rect);
670               
671                sina = sin(rangle - 90*pi/180);
672                cosa = cos(rangle - 90*pi/180);
673                tana = tan(rangle - 90*pi/180);
674               
675                length = x/fabsf(cosa)+(y-x*fabsf(tana))*fabsf(sina);
676               
677                deltax =-length*sina/2;
678                deltay = length*cosa/2;
679                }
680 
681        startPoint = CGPointMake(NSMidX(rect)-deltax, NSMidY(rect)-deltay);
682        endPoint   = CGPointMake(NSMidX(rect)+deltax, NSMidY(rect)+deltay);
683        }
684 
685  //Calls to CoreGraphics
686  CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
687  CGContextSaveGState(currentContext);
688          #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
689                CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
690          #else
691                CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
692          #endif
693          CGShadingRef myCGShading = CGShadingCreateAxial(colorspace, startPoint, endPoint, gradientFunction, false, false);
694         
695          CGContextClipToRect (currentContext, *(CGRect *)&rect);       //This is where the action happens
696          CGContextDrawShading(currentContext, myCGShading);
697         
698          CGShadingRelease(myCGShading);
699          CGColorSpaceRelease(colorspace );
700  CGContextRestoreGState(currentContext);
701  }
702
703- (void)radialFillRect:(NSRect)rect
704  {
705  CGPoint startPoint , endPoint;
706  float startRadius, endRadius;
707  float scalex, scaley, transx, transy;
708 
709  startPoint = endPoint = CGPointMake(NSMidX(rect), NSMidY(rect));
710 
711  startRadius = -1;
712  if(NSHeight(rect)>NSWidth(rect))
713        {
714        scalex = NSWidth(rect)/NSHeight(rect);
715        transx = (NSHeight(rect)-NSWidth(rect))/2;
716        scaley = 1;
717        transy = 1;
718        endRadius = NSHeight(rect)/2;
719        }
720  else
721        {
722        scalex = 1;
723        transx = 1;
724        scaley = NSHeight(rect)/NSWidth(rect);
725        transy = (NSWidth(rect)-NSHeight(rect))/2;
726        endRadius = NSWidth(rect)/2;
727        }
728 
729  //Calls to CoreGraphics
730  CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
731  CGContextSaveGState(currentContext);
732          #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
733                CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
734          #else
735                CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
736          #endif
737          CGShadingRef myCGShading = CGShadingCreateRadial(colorspace, startPoint, startRadius, endPoint, endRadius, gradientFunction, true, true);
738
739          CGContextClipToRect  (currentContext, *(CGRect *)&rect);
740          CGContextScaleCTM    (currentContext, scalex, scaley);
741          CGContextTranslateCTM(currentContext, transx, transy);
742          CGContextDrawShading (currentContext, myCGShading);           //This is where the action happens
743         
744          CGShadingRelease(myCGShading);
745          CGColorSpaceRelease(colorspace);
746  CGContextRestoreGState(currentContext);
747  }
748
749- (void)fillBezierPath:(NSBezierPath *)path angle:(float)angle
750  {
751  NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
752  [currentContext saveGraphicsState];
753        NSAffineTransform *transform = [[NSAffineTransform alloc] init];
754       
755        [transform rotateByDegrees:-angle];
756        [path transformUsingAffineTransform:transform];
757        [transform invert];
758        [transform concat];
759       
760        [path addClip];
761        [self fillRect:[path bounds] angle:0];
762        [path transformUsingAffineTransform:transform];
763        [transform release];
764  [currentContext restoreGraphicsState];
765  }
766- (void)radialFillBezierPath:(NSBezierPath *)path
767  {
768  NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
769  [currentContext saveGraphicsState];
770        [path addClip];
771        [self radialFillRect:[path bounds]];
772  [currentContext restoreGraphicsState];
773  }
774#pragma mark -
775
776
777
778#pragma mark Private Methods
779- (void)setBlendingMode:(CTGradientBlendingMode)mode;
780  {
781  //Choose what blending function to use
782  void *evaluationFunction;
783  switch(mode)
784        {
785        case CTLinearBlendingMode:
786                 evaluationFunction = &linearEvaluation;                        break;
787        case CTChromaticBlendingMode:
788                 evaluationFunction = &chromaticEvaluation;                     break;
789        case CTInverseChromaticBlendingMode:
790                 evaluationFunction = &inverseChromaticEvaluation;      break;
791        default:
792                 NSParameterAssert("blending mode not supported" && NO);
793                 return; // unreachable, satisfy compiler
794        }
795
796  blendingMode = mode;
797
798  //replace the current CoreGraphics Function with new one
799  if(gradientFunction != NULL)
800          CGFunctionRelease(gradientFunction);
801   
802  CGFunctionCallbacks evaluationCallbackInfo = {0 , evaluationFunction, NULL};  //Version, evaluator function, cleanup function
803 
804  static const float input_value_range   [2] = { 0, 1 };                                                //range  for the evaluator input
805  static const float output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 };              //ranges for the evaluator output (4 returned values)
806 
807  gradientFunction = CGFunctionCreate(&elementList,                                     //the two transition colors
808                                                                          1, input_value_range  ,               //number of inputs (just fraction of progression)
809                                                                          4, output_value_ranges,               //number of outputs (4 - RGBa)
810                                                                          &evaluationCallbackInfo);             //info for using the evaluator function
811  }
812
813- (void)addElement:(CTGradientElement *)newElement
814{
815  if(elementList == nil || newElement->position < elementList->position)        //inserting at beginning of list
816        {
817        CTGradientElement *tmpNext = elementList;
818        elementList = malloc(sizeof(CTGradientElement));
819        *elementList = *newElement;
820        elementList->nextElement = tmpNext;
821        }
822  else                                                                                                                                          //inserting somewhere inside list
823        {
824        CTGradientElement *curElement = elementList;
825       
826        while(curElement->nextElement != nil && !((curElement->position <= newElement->position) && (newElement->position < curElement->nextElement->position)))
827                {
828                curElement = curElement->nextElement;
829                }
830       
831        CTGradientElement *tmpNext = curElement->nextElement;
832        curElement->nextElement = malloc(sizeof(CTGradientElement));
833        *(curElement->nextElement) = *newElement;
834        curElement->nextElement->nextElement = tmpNext;
835        }
836  }
837
838- (CTGradientElement)removeElementAtIndex:(unsigned)index
839  {
840  CTGradientElement removedElement;
841 
842  if(elementList != nil)
843        {
844        if(index == 0)
845                {
846                CTGradientElement *tmpNext = elementList;
847                elementList = elementList->nextElement;
848               
849                removedElement = *tmpNext;
850                free(tmpNext);
851               
852                return removedElement;
853                }
854       
855        unsigned count = 1;             //we want to start one ahead
856        CTGradientElement *currentElement = elementList;
857        while(currentElement->nextElement != nil)
858                {
859                if(count == index)
860                        {
861                        CTGradientElement *tmpNext  = currentElement->nextElement;
862                        currentElement->nextElement = currentElement->nextElement->nextElement;
863                       
864                        removedElement = *tmpNext;
865                        free(tmpNext);
866
867                        return removedElement;
868                        }
869
870                count++;
871                currentElement = currentElement->nextElement;
872                }
873        }
874 
875  //element is not found, return empty element
876  removedElement.red   = 0.0;
877  removedElement.green = 0.0;
878  removedElement.blue  = 0.0;
879  removedElement.alpha = 0.0;
880  removedElement.position = NAN;
881  removedElement.nextElement = nil;
882 
883  return removedElement;
884  }
885
886- (CTGradientElement)removeElementAtPosition:(float)position
887  {
888  CTGradientElement removedElement;
889 
890  if(elementList != nil)
891        {
892        if(elementList->position == position)
893                {
894                CTGradientElement *tmpNext = elementList;
895                elementList = elementList->nextElement;
896               
897                removedElement = *tmpNext;
898                free(tmpNext);
899               
900                return removedElement;
901                }
902        else
903                {
904                CTGradientElement *curElement = elementList;
905                while(curElement->nextElement != nil)
906                        {
907                        if(curElement->nextElement->position == position)
908                                {
909                                CTGradientElement *tmpNext = curElement->nextElement;
910                                curElement->nextElement = curElement->nextElement->nextElement;
911                               
912                                removedElement = *tmpNext;
913                                free(tmpNext);
914
915                                return removedElement;
916                                }
917                        }
918                }
919        }
920 
921  //element is not found, return empty element
922  removedElement.red   = 0.0;
923  removedElement.green = 0.0;
924  removedElement.blue  = 0.0;
925  removedElement.alpha = 0.0;
926  removedElement.position = NAN;
927  removedElement.nextElement = nil;
928 
929  return removedElement;
930  }
931
932
933- (CTGradientElement *)elementAtIndex:(unsigned)index;                 
934  {
935  unsigned count = 0;
936  CTGradientElement *currentElement = elementList;
937 
938  while(currentElement != nil)
939        {
940        if(count == index)
941                return currentElement;
942       
943        count++;
944        currentElement = currentElement->nextElement;
945        }
946 
947  return nil;
948  }
949#pragma mark -
950
951
952
953#pragma mark Core Graphics
954//////////////////////////////////////Blending Functions/////////////////////////////////////
955void linearEvaluation (void *info, const float *in, float *out)
956  {
957  float position = *in;
958 
959  if(*(CTGradientElement **)info == nil)        //if elementList is empty return clear color
960        {
961        out[0] = out[1] = out[2] = out[3] = 1;
962        return;
963        }
964 
965  //This grabs the first two colors in the sequence
966  CTGradientElement *color1 = *(CTGradientElement **)info;
967  CTGradientElement *color2 = color1->nextElement;
968 
969  //make sure first color and second color are on other sides of position
970  while(color2 != nil && color2->position < position)
971        {
972        color1 = color2;
973        color2 = color1->nextElement;
974        }
975  //if we don't have another color then make next color the same color
976  if(color2 == nil)
977    {
978        color2 = color1;
979    }
980 
981  //----------FailSafe settings----------
982  //color1->red   = 1; color2->red   = 0;
983  //color1->green = 1; color2->green = 0;
984  //color1->blue  = 1; color2->blue  = 0;
985  //color1->alpha = 1; color2->alpha = 1;
986  //color1->position = .5;
987  //color2->position = .5;
988  //-------------------------------------
989 
990  if(position <= color1->position)                      //Make all below color color1's position equal to color1
991        {
992        out[0] = color1->red;
993        out[1] = color1->green;
994        out[2] = color1->blue;
995        out[3] = color1->alpha;
996        }
997  else if (position >= color2->position)        //Make all above color color2's position equal to color2
998        {
999        out[0] = color2->red;
1000        out[1] = color2->green;
1001        out[2] = color2->blue;
1002        out[3] = color2->alpha;
1003        }
1004  else                                                                          //Interpolate color at postions between color1 and color1
1005        {
1006        //adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position
1007        position = (position-color1->position)/(color2->position - color1->position);
1008       
1009        out[0] = (color2->red   - color1->red  )*position + color1->red;
1010        out[1] = (color2->green - color1->green)*position + color1->green;
1011        out[2] = (color2->blue  - color1->blue )*position + color1->blue;
1012        out[3] = (color2->alpha - color1->alpha)*position + color1->alpha;
1013        }
1014  }
1015
1016
1017
1018
1019//Chromatic Evaluation -
1020//      This blends colors by their Hue, Saturation, and Value(Brightness) right now I just
1021//      transform the RGB values stored in the CTGradientElements to HSB, in the future I may
1022//      streamline it to avoid transforming in and out of HSB colorspace *for later*
1023//
1024//      For the chromatic blend we shift the hue of color1 to meet the hue of color2. To do
1025//      this we will add to the hue's angle (if we subtract we'll be doing the inverse
1026//      chromatic...scroll down more for that). All we need to do is keep adding to the hue
1027//  until we wrap around the colorwheel and get to color2.
1028void chromaticEvaluation(void *info, const float *in, float *out)
1029  {
1030  float position = *in;
1031 
1032  if(*(CTGradientElement **)info == nil)        //if elementList is empty return clear color
1033        {
1034        out[0] = out[1] = out[2] = out[3] = 1;
1035        return;
1036        }
1037 
1038  //This grabs the first two colors in the sequence
1039  CTGradientElement *color1 = *(CTGradientElement **)info;
1040  CTGradientElement *color2 = color1->nextElement;
1041 
1042  float c1[4];
1043  float c2[4];
1044   
1045  //make sure first color and second color are on other sides of position
1046  while(color2 != nil && color2->position < position)
1047        {
1048        color1 = color2;
1049        color2 = color1->nextElement;
1050        }
1051  //if we don't have another color then make next color the same color
1052  if(color2 == nil)
1053    {
1054        color2 = color1;
1055    }
1056 
1057 
1058  c1[0] = color1->red;
1059  c1[1] = color1->green;
1060  c1[2] = color1->blue;
1061  c1[3] = color1->alpha;
1062 
1063  c2[0] = color2->red;
1064  c2[1] = color2->green;
1065  c2[2] = color2->blue;
1066  c2[3] = color2->alpha;
1067 
1068  transformRGB_HSV(c1);
1069  transformRGB_HSV(c2);
1070  resolveHSV(c1,c2);
1071 
1072  if(c1[0] > c2[0]) //if color1's hue is higher than color2's hue then
1073         c2[0] += 360;  //      we need to move c2 one revolution around the wheel
1074 
1075 
1076  if(position <= color1->position)                      //Make all below color color1's position equal to color1
1077        {
1078        out[0] = c1[0];
1079        out[1] = c1[1];
1080        out[2] = c1[2];
1081        out[3] = c1[3];
1082        }
1083  else if (position >= color2->position)        //Make all above color color2's position equal to color2
1084        {
1085        out[0] = c2[0];
1086        out[1] = c2[1];
1087        out[2] = c2[2];
1088        out[3] = c2[3];
1089        }
1090  else                                                                          //Interpolate color at postions between color1 and color1
1091        {
1092        //adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position
1093        position = (position-color1->position)/(color2->position - color1->position);
1094       
1095        out[0] = (c2[0] - c1[0])*position + c1[0];
1096        out[1] = (c2[1] - c1[1])*position + c1[1];
1097        out[2] = (c2[2] - c1[2])*position + c1[2];
1098        out[3] = (c2[3] - c1[3])*position + c1[3];
1099        }
1100   
1101  transformHSV_RGB(out);
1102  }
1103
1104
1105
1106//Inverse Chromatic Evaluation -
1107//      Inverse Chromatic is about the same story as Chromatic Blend, but here the Hue
1108//      is strictly decreasing, that is we need to get from color1 to color2 by decreasing
1109//      the 'angle' (i.e. 90¼ -> 180¼ would be done by subtracting 270¼ and getting -180¼...
1110//      which is equivalent to 180¼ mod 360¼
1111void inverseChromaticEvaluation(void *info, const float *in, float *out)
1112  {
1113    float position = *in;
1114 
1115  if(*(CTGradientElement **)info == nil)        //if elementList is empty return clear color
1116        {
1117        out[0] = out[1] = out[2] = out[3] = 1;
1118        return;
1119        }
1120 
1121  //This grabs the first two colors in the sequence
1122  CTGradientElement *color1 = *(CTGradientElement **)info;
1123  CTGradientElement *color2 = color1->nextElement;
1124 
1125  float c1[4];
1126  float c2[4];
1127     
1128  //make sure first color and second color are on other sides of position
1129  while(color2 != nil && color2->position < position)
1130        {
1131        color1 = color2;
1132        color2 = color1->nextElement;
1133        }
1134  //if we don't have another color then make next color the same color
1135  if(color2 == nil)
1136    {
1137        color2 = color1;
1138    }
1139
1140  c1[0] = color1->red;
1141  c1[1] = color1->green;
1142  c1[2] = color1->blue;
1143  c1[3] = color1->alpha;
1144 
1145  c2[0] = color2->red;
1146  c2[1] = color2->green;
1147  c2[2] = color2->blue;
1148  c2[3] = color2->alpha;
1149
1150  transformRGB_HSV(c1);
1151  transformRGB_HSV(c2);
1152  resolveHSV(c1,c2);
1153
1154  if(c1[0] < c2[0]) //if color1's hue is higher than color2's hue then
1155         c1[0] += 360;  //      we need to move c2 one revolution back on the wheel
1156
1157 
1158  if(position <= color1->position)                      //Make all below color color1's position equal to color1
1159        {
1160        out[0] = c1[0];
1161        out[1] = c1[1];
1162        out[2] = c1[2];
1163        out[3] = c1[3];
1164        }
1165  else if (position >= color2->position)        //Make all above color color2's position equal to color2
1166        {
1167        out[0] = c2[0];
1168        out[1] = c2[1];
1169        out[2] = c2[2];
1170        out[3] = c2[3];
1171        }
1172  else                                                                          //Interpolate color at postions between color1 and color1
1173        {
1174        //adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position
1175        position = (position-color1->position)/(color2->position - color1->position);
1176       
1177        out[0] = (c2[0] - c1[0])*position + c1[0];
1178        out[1] = (c2[1] - c1[1])*position + c1[1];
1179        out[2] = (c2[2] - c1[2])*position + c1[2];
1180        out[3] = (c2[3] - c1[3])*position + c1[3];
1181        }
1182   
1183  transformHSV_RGB(out);
1184  }
1185
1186
1187
1188void transformRGB_HSV(float *components) //H,S,B <- R,G,B
1189        {
1190        float R = components[0],
1191                  G = components[1],
1192                  B = components[2];
1193       
1194        float MAX = R > G ? (R > B ? R : B) : (G > B ? G : B),
1195              MIN = R < G ? (R < B ? R : B) : (G < B ? G : B);
1196       
1197        float H = NAN;
1198
1199        if(MAX != MIN)
1200          {
1201          if(MAX == R)
1202                  if(G >= B)
1203                          H = 60*(G-B)/(MAX-MIN)+0;
1204                  else
1205                          H = 60*(G-B)/(MAX-MIN)+360;
1206          else if(MAX == G)
1207                  H = 60*(B-R)/(MAX-MIN)+120;
1208          else if(MAX == B)
1209                  H = 60*(R-G)/(MAX-MIN)+240;
1210          }
1211       
1212        float S = MAX == 0 ? 0 : 1 - MIN/MAX;
1213        float V = MAX;
1214       
1215        components[0] = H;
1216        components[1] = S;
1217        components[2] = V;
1218        }
1219
1220void transformHSV_RGB(float *components) //H,S,B -> R,G,B
1221        {
1222        float R, G, B;
1223        float H = fmodf(components[0],359),     //map to [0,360)
1224                  S = components[1],
1225                  V = components[2];
1226       
1227        int   Hi = (int)floorf(H/60.) % 6;
1228        float f  = H/60-Hi,
1229                  p  = V*(1-S),
1230                  q  = V*(1-f*S),
1231                  t  = V*(1-(1-f)*S);
1232       
1233        switch (Hi)
1234                {
1235                case 0: R=V;G=t;B=p;    break;
1236                case 1: R=q;G=V;B=p;    break;
1237                case 2: R=p;G=V;B=t;    break;
1238                case 3: R=p;G=q;B=V;    break;
1239                case 4: R=t;G=p;B=V;    break;
1240                case 5: R=V;G=p;B=q;    break;
1241                default: R=0;G=0;B=0;   // unreachable, satisfy compiler
1242                }
1243       
1244        components[0] = R;
1245        components[1] = G;
1246        components[2] = B;
1247        }
1248
1249void resolveHSV(float *color1, float *color2)   //H value may be undefined (i.e. graycale color)
1250        {                                                                                       //      we want to fill it with a sensible value
1251        if(isnan(color1[0]) && isnan(color2[0]))
1252                color1[0] = color2[0] = 0;
1253        else if(isnan(color1[0]))
1254                color1[0] = color2[0];
1255        else if(isnan(color2[0]))
1256                color2[0] = color1[0];
1257        }
1258
1259@end
Note: See TracBrowser for help on using the repository browser.