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

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

version.plist: Not needed.

Info.plist: Updated for 1.4njr2.

AUTHORS: Updated Onne Gorter's email address and added mine.

AntiRSI.[hm]: Added dock badge session timer display and auto-reset options. Updated URLs to point to my Web site; should probably just switch to Sparkle.

CTBadge: Added customized version.

English.lproj/InfoPlist.strings: Updated for 1.4njr2; credit myself and Chad; remove Onne Gorter's URL.

English.lproj/MainMenu.nib: Preference changes; add cmd-R for manual reset.

File size: 32.5 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  blendingMode = mode;
782 
783  //Choose what blending function to use
784  void *evaluationFunction;
785  switch(blendingMode)
786        {
787        case CTLinearBlendingMode:
788                 evaluationFunction = &linearEvaluation;                        break;
789        case CTChromaticBlendingMode:
790                 evaluationFunction = &chromaticEvaluation;                     break;
791        case CTInverseChromaticBlendingMode:
792                 evaluationFunction = &inverseChromaticEvaluation;      break;
793        }
794 
795  //replace the current CoreGraphics Function with new one
796  if(gradientFunction != NULL)
797          CGFunctionRelease(gradientFunction);
798   
799  CGFunctionCallbacks evaluationCallbackInfo = {0 , evaluationFunction, NULL};  //Version, evaluator function, cleanup function
800 
801  static const float input_value_range   [2] = { 0, 1 };                                                //range  for the evaluator input
802  static const float output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 };              //ranges for the evaluator output (4 returned values)
803 
804  gradientFunction = CGFunctionCreate(&elementList,                                     //the two transition colors
805                                                                          1, input_value_range  ,               //number of inputs (just fraction of progression)
806                                                                          4, output_value_ranges,               //number of outputs (4 - RGBa)
807                                                                          &evaluationCallbackInfo);             //info for using the evaluator function
808  }
809
810- (void)addElement:(CTGradientElement *)newElement
811{
812  if(elementList == nil || newElement->position < elementList->position)        //inserting at beginning of list
813        {
814        CTGradientElement *tmpNext = elementList;
815        elementList = malloc(sizeof(CTGradientElement));
816        *elementList = *newElement;
817        elementList->nextElement = tmpNext;
818        }
819  else                                                                                                                                          //inserting somewhere inside list
820        {
821        CTGradientElement *curElement = elementList;
822       
823        while(curElement->nextElement != nil && !((curElement->position <= newElement->position) && (newElement->position < curElement->nextElement->position)))
824                {
825                curElement = curElement->nextElement;
826                }
827       
828        CTGradientElement *tmpNext = curElement->nextElement;
829        curElement->nextElement = malloc(sizeof(CTGradientElement));
830        *(curElement->nextElement) = *newElement;
831        curElement->nextElement->nextElement = tmpNext;
832        }
833  }
834
835- (CTGradientElement)removeElementAtIndex:(unsigned)index
836  {
837  CTGradientElement removedElement;
838 
839  if(elementList != nil)
840        {
841        if(index == 0)
842                {
843                CTGradientElement *tmpNext = elementList;
844                elementList = elementList->nextElement;
845               
846                removedElement = *tmpNext;
847                free(tmpNext);
848               
849                return removedElement;
850                }
851       
852        unsigned count = 1;             //we want to start one ahead
853        CTGradientElement *currentElement = elementList;
854        while(currentElement->nextElement != nil)
855                {
856                if(count == index)
857                        {
858                        CTGradientElement *tmpNext  = currentElement->nextElement;
859                        currentElement->nextElement = currentElement->nextElement->nextElement;
860                       
861                        removedElement = *tmpNext;
862                        free(tmpNext);
863
864                        return removedElement;
865                        }
866
867                count++;
868                currentElement = currentElement->nextElement;
869                }
870        }
871 
872  //element is not found, return empty element
873  removedElement.red   = 0.0;
874  removedElement.green = 0.0;
875  removedElement.blue  = 0.0;
876  removedElement.alpha = 0.0;
877  removedElement.position = NAN;
878  removedElement.nextElement = nil;
879 
880  return removedElement;
881  }
882
883- (CTGradientElement)removeElementAtPosition:(float)position
884  {
885  CTGradientElement removedElement;
886 
887  if(elementList != nil)
888        {
889        if(elementList->position == position)
890                {
891                CTGradientElement *tmpNext = elementList;
892                elementList = elementList->nextElement;
893               
894                removedElement = *tmpNext;
895                free(tmpNext);
896               
897                return removedElement;
898                }
899        else
900                {
901                CTGradientElement *curElement = elementList;
902                while(curElement->nextElement != nil)
903                        {
904                        if(curElement->nextElement->position == position)
905                                {
906                                CTGradientElement *tmpNext = curElement->nextElement;
907                                curElement->nextElement = curElement->nextElement->nextElement;
908                               
909                                removedElement = *tmpNext;
910                                free(tmpNext);
911
912                                return removedElement;
913                                }
914                        }
915                }
916        }
917 
918  //element is not found, return empty element
919  removedElement.red   = 0.0;
920  removedElement.green = 0.0;
921  removedElement.blue  = 0.0;
922  removedElement.alpha = 0.0;
923  removedElement.position = NAN;
924  removedElement.nextElement = nil;
925 
926  return removedElement;
927  }
928
929
930- (CTGradientElement *)elementAtIndex:(unsigned)index;                 
931  {
932  unsigned count = 0;
933  CTGradientElement *currentElement = elementList;
934 
935  while(currentElement != nil)
936        {
937        if(count == index)
938                return currentElement;
939       
940        count++;
941        currentElement = currentElement->nextElement;
942        }
943 
944  return nil;
945  }
946#pragma mark -
947
948
949
950#pragma mark Core Graphics
951//////////////////////////////////////Blending Functions/////////////////////////////////////
952void linearEvaluation (void *info, const float *in, float *out)
953  {
954  float position = *in;
955 
956  if(*(CTGradientElement **)info == nil)        //if elementList is empty return clear color
957        {
958        out[0] = out[1] = out[2] = out[3] = 1;
959        return;
960        }
961 
962  //This grabs the first two colors in the sequence
963  CTGradientElement *color1 = *(CTGradientElement **)info;
964  CTGradientElement *color2 = color1->nextElement;
965 
966  //make sure first color and second color are on other sides of position
967  while(color2 != nil && color2->position < position)
968        {
969        color1 = color2;
970        color2 = color1->nextElement;
971        }
972  //if we don't have another color then make next color the same color
973  if(color2 == nil)
974    {
975        color2 = color1;
976    }
977 
978  //----------FailSafe settings----------
979  //color1->red   = 1; color2->red   = 0;
980  //color1->green = 1; color2->green = 0;
981  //color1->blue  = 1; color2->blue  = 0;
982  //color1->alpha = 1; color2->alpha = 1;
983  //color1->position = .5;
984  //color2->position = .5;
985  //-------------------------------------
986 
987  if(position <= color1->position)                      //Make all below color color1's position equal to color1
988        {
989        out[0] = color1->red;
990        out[1] = color1->green;
991        out[2] = color1->blue;
992        out[3] = color1->alpha;
993        }
994  else if (position >= color2->position)        //Make all above color color2's position equal to color2
995        {
996        out[0] = color2->red;
997        out[1] = color2->green;
998        out[2] = color2->blue;
999        out[3] = color2->alpha;
1000        }
1001  else                                                                          //Interpolate color at postions between color1 and color1
1002        {
1003        //adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position
1004        position = (position-color1->position)/(color2->position - color1->position);
1005       
1006        out[0] = (color2->red   - color1->red  )*position + color1->red;
1007        out[1] = (color2->green - color1->green)*position + color1->green;
1008        out[2] = (color2->blue  - color1->blue )*position + color1->blue;
1009        out[3] = (color2->alpha - color1->alpha)*position + color1->alpha;
1010        }
1011  }
1012
1013
1014
1015
1016//Chromatic Evaluation -
1017//      This blends colors by their Hue, Saturation, and Value(Brightness) right now I just
1018//      transform the RGB values stored in the CTGradientElements to HSB, in the future I may
1019//      streamline it to avoid transforming in and out of HSB colorspace *for later*
1020//
1021//      For the chromatic blend we shift the hue of color1 to meet the hue of color2. To do
1022//      this we will add to the hue's angle (if we subtract we'll be doing the inverse
1023//      chromatic...scroll down more for that). All we need to do is keep adding to the hue
1024//  until we wrap around the colorwheel and get to color2.
1025void chromaticEvaluation(void *info, const float *in, float *out)
1026  {
1027  float position = *in;
1028 
1029  if(*(CTGradientElement **)info == nil)        //if elementList is empty return clear color
1030        {
1031        out[0] = out[1] = out[2] = out[3] = 1;
1032        return;
1033        }
1034 
1035  //This grabs the first two colors in the sequence
1036  CTGradientElement *color1 = *(CTGradientElement **)info;
1037  CTGradientElement *color2 = color1->nextElement;
1038 
1039  float c1[4];
1040  float c2[4];
1041   
1042  //make sure first color and second color are on other sides of position
1043  while(color2 != nil && color2->position < position)
1044        {
1045        color1 = color2;
1046        color2 = color1->nextElement;
1047        }
1048  //if we don't have another color then make next color the same color
1049  if(color2 == nil)
1050    {
1051        color2 = color1;
1052    }
1053 
1054 
1055  c1[0] = color1->red;
1056  c1[1] = color1->green;
1057  c1[2] = color1->blue;
1058  c1[3] = color1->alpha;
1059 
1060  c2[0] = color2->red;
1061  c2[1] = color2->green;
1062  c2[2] = color2->blue;
1063  c2[3] = color2->alpha;
1064 
1065  transformRGB_HSV(c1);
1066  transformRGB_HSV(c2);
1067  resolveHSV(c1,c2);
1068 
1069  if(c1[0] > c2[0]) //if color1's hue is higher than color2's hue then
1070         c2[0] += 360;  //      we need to move c2 one revolution around the wheel
1071 
1072 
1073  if(position <= color1->position)                      //Make all below color color1's position equal to color1
1074        {
1075        out[0] = c1[0];
1076        out[1] = c1[1];
1077        out[2] = c1[2];
1078        out[3] = c1[3];
1079        }
1080  else if (position >= color2->position)        //Make all above color color2's position equal to color2
1081        {
1082        out[0] = c2[0];
1083        out[1] = c2[1];
1084        out[2] = c2[2];
1085        out[3] = c2[3];
1086        }
1087  else                                                                          //Interpolate color at postions between color1 and color1
1088        {
1089        //adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position
1090        position = (position-color1->position)/(color2->position - color1->position);
1091       
1092        out[0] = (c2[0] - c1[0])*position + c1[0];
1093        out[1] = (c2[1] - c1[1])*position + c1[1];
1094        out[2] = (c2[2] - c1[2])*position + c1[2];
1095        out[3] = (c2[3] - c1[3])*position + c1[3];
1096        }
1097   
1098  transformHSV_RGB(out);
1099  }
1100
1101
1102
1103//Inverse Chromatic Evaluation -
1104//      Inverse Chromatic is about the same story as Chromatic Blend, but here the Hue
1105//      is strictly decreasing, that is we need to get from color1 to color2 by decreasing
1106//      the 'angle' (i.e. 90¼ -> 180¼ would be done by subtracting 270¼ and getting -180¼...
1107//      which is equivalent to 180¼ mod 360¼
1108void inverseChromaticEvaluation(void *info, const float *in, float *out)
1109  {
1110    float position = *in;
1111 
1112  if(*(CTGradientElement **)info == nil)        //if elementList is empty return clear color
1113        {
1114        out[0] = out[1] = out[2] = out[3] = 1;
1115        return;
1116        }
1117 
1118  //This grabs the first two colors in the sequence
1119  CTGradientElement *color1 = *(CTGradientElement **)info;
1120  CTGradientElement *color2 = color1->nextElement;
1121 
1122  float c1[4];
1123  float c2[4];
1124     
1125  //make sure first color and second color are on other sides of position
1126  while(color2 != nil && color2->position < position)
1127        {
1128        color1 = color2;
1129        color2 = color1->nextElement;
1130        }
1131  //if we don't have another color then make next color the same color
1132  if(color2 == nil)
1133    {
1134        color2 = color1;
1135    }
1136
1137  c1[0] = color1->red;
1138  c1[1] = color1->green;
1139  c1[2] = color1->blue;
1140  c1[3] = color1->alpha;
1141 
1142  c2[0] = color2->red;
1143  c2[1] = color2->green;
1144  c2[2] = color2->blue;
1145  c2[3] = color2->alpha;
1146
1147  transformRGB_HSV(c1);
1148  transformRGB_HSV(c2);
1149  resolveHSV(c1,c2);
1150
1151  if(c1[0] < c2[0]) //if color1's hue is higher than color2's hue then
1152         c1[0] += 360;  //      we need to move c2 one revolution back on the wheel
1153
1154 
1155  if(position <= color1->position)                      //Make all below color color1's position equal to color1
1156        {
1157        out[0] = c1[0];
1158        out[1] = c1[1];
1159        out[2] = c1[2];
1160        out[3] = c1[3];
1161        }
1162  else if (position >= color2->position)        //Make all above color color2's position equal to color2
1163        {
1164        out[0] = c2[0];
1165        out[1] = c2[1];
1166        out[2] = c2[2];
1167        out[3] = c2[3];
1168        }
1169  else                                                                          //Interpolate color at postions between color1 and color1
1170        {
1171        //adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position
1172        position = (position-color1->position)/(color2->position - color1->position);
1173       
1174        out[0] = (c2[0] - c1[0])*position + c1[0];
1175        out[1] = (c2[1] - c1[1])*position + c1[1];
1176        out[2] = (c2[2] - c1[2])*position + c1[2];
1177        out[3] = (c2[3] - c1[3])*position + c1[3];
1178        }
1179   
1180  transformHSV_RGB(out);
1181  }
1182
1183
1184
1185void transformRGB_HSV(float *components) //H,S,B -> R,G,B
1186        {
1187        float H, S, V;
1188        float R = components[0],
1189                  G = components[1],
1190                  B = components[2];
1191       
1192        float MAX = R > G ? (R > B ? R : B) : (G > B ? G : B),
1193              MIN = R < G ? (R < B ? R : B) : (G < B ? G : B);
1194       
1195        if(MAX == MIN)
1196                H = NAN;
1197        else if(MAX == R)
1198                if(G >= B)
1199                        H = 60*(G-B)/(MAX-MIN)+0;
1200                else
1201                        H = 60*(G-B)/(MAX-MIN)+360;
1202        else if(MAX == G)
1203                H = 60*(B-R)/(MAX-MIN)+120;
1204        else if(MAX == B)
1205                H = 60*(R-G)/(MAX-MIN)+240;
1206       
1207        S = MAX == 0 ? 0 : 1 - MIN/MAX;
1208        V = MAX;
1209       
1210        components[0] = H;
1211        components[1] = S;
1212        components[2] = V;
1213        }
1214
1215void transformHSV_RGB(float *components) //H,S,B -> R,G,B
1216        {
1217        float R, G, B;
1218        float H = fmodf(components[0],359),     //map to [0,360)
1219                  S = components[1],
1220                  V = components[2];
1221       
1222        int   Hi = (int)floorf(H/60.) % 6;
1223        float f  = H/60-Hi,
1224                  p  = V*(1-S),
1225                  q  = V*(1-f*S),
1226                  t  = V*(1-(1-f)*S);
1227       
1228        switch (Hi)
1229                {
1230                case 0: R=V;G=t;B=p;    break;
1231                case 1: R=q;G=V;B=p;    break;
1232                case 2: R=p;G=V;B=t;    break;
1233                case 3: R=p;G=q;B=V;    break;
1234                case 4: R=t;G=p;B=V;    break;
1235                case 5: R=V;G=p;B=q;    break;
1236                }
1237       
1238        components[0] = R;
1239        components[1] = G;
1240        components[2] = B;
1241        }
1242
1243void resolveHSV(float *color1, float *color2)   //H value may be undefined (i.e. graycale color)
1244        {                                                                                       //      we want to fill it with a sensible value
1245        if(isnan(color1[0]) && isnan(color2[0]))
1246                color1[0] = color2[0] = 0;
1247        else if(isnan(color1[0]))
1248                color1[0] = color2[0];
1249        else if(isnan(color2[0]))
1250                color2[0] = color1[0];
1251        }
1252
1253@end
Note: See TracBrowser for help on using the repository browser.