source: trunk/macosx/CTGradient/CTGradient.m @ 5972

Last change on this file since 5972 was 5972, checked in by livings124, 14 years ago

pieces bar color adjustment; dead code removal

File size: 25.1 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#pragma mark -
159
160
161#pragma mark Modification
162- (CTGradient *)gradientWithAlphaComponent:(float)alpha
163  {
164  id newInstance = [[[self class] alloc] init];
165 
166  CTGradientElement *curElement = elementList;
167  CTGradientElement tempElement;
168
169  while(curElement != nil)
170        {
171        tempElement = *curElement;
172        tempElement.alpha = alpha;
173        [newInstance addElement:&tempElement];
174       
175        curElement = curElement->nextElement;
176        }
177 
178  return [newInstance autorelease];
179  }
180
181- (CTGradient *)gradientWithBlendingMode:(CTGradientBlendingMode)mode
182  {
183  CTGradient *newGradient = [self copy]; 
184 
185  [newGradient setBlendingMode:mode];
186 
187  return [newGradient autorelease];
188  }
189
190
191//Adds a color stop with <color> at <position> in elementList
192//(if two elements are at the same position then added imediatly after the one that was there already)
193- (CTGradient *)addColorStop:(NSColor *)color atPosition:(float)position
194  {
195  CTGradient *newGradient = [self copy];
196  CTGradientElement newGradientElement;
197 
198  //put the components of color into the newGradientElement - must make sure it is a RGB color (not Gray or CMYK)
199  [[color colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&newGradientElement.red
200                                                                                                                           green:&newGradientElement.green
201                                                                                                                                blue:&newGradientElement.blue
202                                                                                                                           alpha:&newGradientElement.alpha];
203  newGradientElement.position = position;
204 
205  //Pass it off to addElement to take care of adding it to the elementList
206  [newGradient addElement:&newGradientElement];
207 
208  return [newGradient autorelease];
209  }
210
211
212//Removes the color stop at <position> from elementList
213- (CTGradient *)removeColorStopAtPosition:(float)position
214  {
215  CTGradient *newGradient = [self copy];
216  CTGradientElement removedElement = [newGradient removeElementAtPosition:position];
217 
218  if(isnan(removedElement.position))
219        [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtPosition:]: no such colorStop at position (%f)", [self class], position];
220 
221  return [newGradient autorelease];
222  }
223
224- (CTGradient *)removeColorStopAtIndex:(unsigned)index
225  {
226  CTGradient *newGradient = [self copy];
227  CTGradientElement removedElement = [newGradient removeElementAtIndex:index];
228 
229  if(isnan(removedElement.position))
230        [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtIndex:]: index (%i) beyond bounds", [self class], index];
231 
232  return [newGradient autorelease];
233  }
234#pragma mark -
235
236
237
238#pragma mark Information
239- (CTGradientBlendingMode)blendingMode
240  {
241  return blendingMode;
242  }
243
244//Returns color at <position> in gradient
245- (NSColor *)colorStopAtIndex:(unsigned)index
246  {
247  CTGradientElement *element = [self elementAtIndex:index];
248 
249  if(element != nil)
250        return [NSColor colorWithCalibratedRed:element->red
251                                                                         green:element->green
252                                                                          blue:element->blue
253                                                                         alpha:element->alpha];
254 
255  [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtIndex:]: index (%i) beyond bounds", [self class], index];
256 
257  return nil;
258  }
259
260- (NSColor *)colorAtPosition:(float)position
261  {
262  float components[4];
263 
264  switch(blendingMode)
265        {
266        case CTLinearBlendingMode:
267                 linearEvaluation(&elementList, &position, components);                         break;
268        case CTChromaticBlendingMode:
269                 chromaticEvaluation(&elementList, &position, components);                      break;
270        case CTInverseChromaticBlendingMode:
271                 inverseChromaticEvaluation(&elementList, &position, components);       break;
272        }
273 
274 
275  return [NSColor colorWithCalibratedRed:components[0]
276                                                                   green:components[1]
277                                                                    blue:components[2]
278                                                                   alpha:components[3]];
279  }
280#pragma mark -
281
282
283
284#pragma mark Drawing
285- (void)drawSwatchInRect:(NSRect)rect
286  {
287  [self fillRect:rect angle:45];
288  }
289
290- (void)fillRect:(NSRect)rect angle:(float)angle
291  {
292  //First Calculate where the beginning and ending points should be
293  CGPoint startPoint;
294  CGPoint endPoint;
295 
296  if(angle == 0)                //screw the calculations - we know the answer
297        {
298        startPoint = CGPointMake(NSMinX(rect), NSMinY(rect));   //right of rect
299        endPoint   = CGPointMake(NSMaxX(rect), NSMinY(rect));   //left  of rect
300        }
301  else if(angle == 90)  //same as above
302        {
303        startPoint = CGPointMake(NSMinX(rect), NSMinY(rect));   //bottom of rect
304        endPoint   = CGPointMake(NSMinX(rect), NSMaxY(rect));   //top    of rect
305        }
306  else                                          //ok, we'll do the calculations now
307        {
308        float x,y;
309        float sina, cosa, tana;
310       
311        float length;
312        float deltax,
313                  deltay;
314       
315        float rangle = angle * pi/180;  //convert the angle to radians
316       
317        if(fabsf(tan(rangle))<=1)       //for range [-45,45], [135,225]
318                {
319                x = NSWidth(rect);
320                y = NSHeight(rect);
321               
322                sina = sin(rangle);
323                cosa = cos(rangle);
324                tana = tan(rangle);
325               
326                length = x/fabsf(cosa)+(y-x*fabsf(tana))*fabsf(sina);
327               
328                deltax = length*cosa/2;
329                deltay = length*sina/2;
330                }
331        else                                            //for range [45,135], [225,315]
332                {
333                x = NSHeight(rect);
334                y = NSWidth(rect);
335               
336                sina = sin(rangle - 90*pi/180);
337                cosa = cos(rangle - 90*pi/180);
338                tana = tan(rangle - 90*pi/180);
339               
340                length = x/fabsf(cosa)+(y-x*fabsf(tana))*fabsf(sina);
341               
342                deltax =-length*sina/2;
343                deltay = length*cosa/2;
344                }
345 
346        startPoint = CGPointMake(NSMidX(rect)-deltax, NSMidY(rect)-deltay);
347        endPoint   = CGPointMake(NSMidX(rect)+deltax, NSMidY(rect)+deltay);
348        }
349 
350  //Calls to CoreGraphics
351  CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
352  CGContextSaveGState(currentContext);
353          #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
354                CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
355          #else
356                CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
357          #endif
358          CGShadingRef myCGShading = CGShadingCreateAxial(colorspace, startPoint, endPoint, gradientFunction, false, false);
359         
360          CGContextClipToRect (currentContext, *(CGRect *)&rect);       //This is where the action happens
361          CGContextDrawShading(currentContext, myCGShading);
362         
363          CGShadingRelease(myCGShading);
364          CGColorSpaceRelease(colorspace );
365  CGContextRestoreGState(currentContext);
366  }
367
368- (void)radialFillRect:(NSRect)rect
369  {
370  CGPoint startPoint , endPoint;
371  float startRadius, endRadius;
372  float scalex, scaley, transx, transy;
373 
374  startPoint = endPoint = CGPointMake(NSMidX(rect), NSMidY(rect));
375 
376  startRadius = -1;
377  if(NSHeight(rect)>NSWidth(rect))
378        {
379        scalex = NSWidth(rect)/NSHeight(rect);
380        transx = (NSHeight(rect)-NSWidth(rect))/2;
381        scaley = 1;
382        transy = 1;
383        endRadius = NSHeight(rect)/2;
384        }
385  else
386        {
387        scalex = 1;
388        transx = 1;
389        scaley = NSHeight(rect)/NSWidth(rect);
390        transy = (NSWidth(rect)-NSHeight(rect))/2;
391        endRadius = NSWidth(rect)/2;
392        }
393 
394  //Calls to CoreGraphics
395  CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
396  CGContextSaveGState(currentContext);
397          #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
398                CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
399          #else
400                CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
401          #endif
402          CGShadingRef myCGShading = CGShadingCreateRadial(colorspace, startPoint, startRadius, endPoint, endRadius, gradientFunction, true, true);
403
404          CGContextClipToRect  (currentContext, *(CGRect *)&rect);
405          CGContextScaleCTM    (currentContext, scalex, scaley);
406          CGContextTranslateCTM(currentContext, transx, transy);
407          CGContextDrawShading (currentContext, myCGShading);           //This is where the action happens
408         
409          CGShadingRelease(myCGShading);
410          CGColorSpaceRelease(colorspace);
411  CGContextRestoreGState(currentContext);
412  }
413
414- (void)fillBezierPath:(NSBezierPath *)path angle:(float)angle
415  {
416  NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
417  [currentContext saveGraphicsState];
418        NSAffineTransform *transform = [[NSAffineTransform alloc] init];
419       
420        [transform rotateByDegrees:-angle];
421        [path transformUsingAffineTransform:transform];
422        [transform invert];
423        [transform concat];
424       
425        [path addClip];
426        [self fillRect:[path bounds] angle:0];
427        [path transformUsingAffineTransform:transform];
428        [transform release];
429  [currentContext restoreGraphicsState];
430  }
431- (void)radialFillBezierPath:(NSBezierPath *)path
432  {
433  NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
434  [currentContext saveGraphicsState];
435        [path addClip];
436        [self radialFillRect:[path bounds]];
437  [currentContext restoreGraphicsState];
438  }
439#pragma mark -
440
441
442
443#pragma mark Private Methods
444- (void)setBlendingMode:(CTGradientBlendingMode)mode;
445  {
446  blendingMode = mode;
447 
448  //Choose what blending function to use
449  void *evaluationFunction;
450  switch(blendingMode)
451        {
452        case CTLinearBlendingMode:
453                 evaluationFunction = &linearEvaluation;                        break;
454        case CTChromaticBlendingMode:
455                 evaluationFunction = &chromaticEvaluation;                     break;
456        case CTInverseChromaticBlendingMode:
457                 evaluationFunction = &inverseChromaticEvaluation;      break;
458        }
459 
460  //replace the current CoreGraphics Function with new one
461  if(gradientFunction != NULL)
462          CGFunctionRelease(gradientFunction);
463   
464  CGFunctionCallbacks evaluationCallbackInfo = {0 , evaluationFunction, NULL};  //Version, evaluator function, cleanup function
465 
466  static const float input_value_range   [2] = { 0, 1 };                                                //range  for the evaluator input
467  static const float output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 };              //ranges for the evaluator output (4 returned values)
468 
469  gradientFunction = CGFunctionCreate(&elementList,                                     //the two transition colors
470                                                                          1, input_value_range  ,               //number of inputs (just fraction of progression)
471                                                                          4, output_value_ranges,               //number of outputs (4 - RGBa)
472                                                                          &evaluationCallbackInfo);             //info for using the evaluator function
473  }
474
475- (void)addElement:(CTGradientElement *)newElement
476{
477  if(elementList == nil || newElement->position < elementList->position)        //inserting at beginning of list
478        {
479        CTGradientElement *tmpNext = elementList;
480        elementList = malloc(sizeof(CTGradientElement));
481        *elementList = *newElement;
482        elementList->nextElement = tmpNext;
483        }
484  else                                                                                                                                          //inserting somewhere inside list
485        {
486        CTGradientElement *curElement = elementList;
487       
488        while(curElement->nextElement != nil && !((curElement->position <= newElement->position) && (newElement->position < curElement->nextElement->position)))
489                {
490                curElement = curElement->nextElement;
491                }
492       
493        CTGradientElement *tmpNext = curElement->nextElement;
494        curElement->nextElement = malloc(sizeof(CTGradientElement));
495        *(curElement->nextElement) = *newElement;
496        curElement->nextElement->nextElement = tmpNext;
497        }
498  }
499
500- (CTGradientElement)removeElementAtIndex:(unsigned)index
501  {
502  CTGradientElement removedElement;
503 
504  if(elementList != nil)
505        {
506        if(index == 0)
507                {
508                CTGradientElement *tmpNext = elementList;
509                elementList = elementList->nextElement;
510               
511                removedElement = *tmpNext;
512                free(tmpNext);
513               
514                return removedElement;
515                }
516       
517        unsigned count = 1;             //we want to start one ahead
518        CTGradientElement *currentElement = elementList;
519        while(currentElement->nextElement != nil)
520                {
521                if(count == index)
522                        {
523                        CTGradientElement *tmpNext  = currentElement->nextElement;
524                        currentElement->nextElement = currentElement->nextElement->nextElement;
525                       
526                        removedElement = *tmpNext;
527                        free(tmpNext);
528
529                        return removedElement;
530                        }
531
532                count++;
533                currentElement = currentElement->nextElement;
534                }
535        }
536 
537  //element is not found, return empty element
538  removedElement.red   = 0.0;
539  removedElement.green = 0.0;
540  removedElement.blue  = 0.0;
541  removedElement.alpha = 0.0;
542  removedElement.position = NAN;
543  removedElement.nextElement = nil;
544 
545  return removedElement;
546  }
547
548- (CTGradientElement)removeElementAtPosition:(float)position
549  {
550  CTGradientElement removedElement;
551 
552  if(elementList != nil)
553        {
554        if(elementList->position == position)
555                {
556                CTGradientElement *tmpNext = elementList;
557                elementList = elementList->nextElement;
558               
559                removedElement = *tmpNext;
560                free(tmpNext);
561               
562                return removedElement;
563                }
564        else
565                {
566                CTGradientElement *curElement = elementList;
567                while(curElement->nextElement != nil)
568                        {
569                        if(curElement->nextElement->position == position)
570                                {
571                                CTGradientElement *tmpNext = curElement->nextElement;
572                                curElement->nextElement = curElement->nextElement->nextElement;
573                               
574                                removedElement = *tmpNext;
575                                free(tmpNext);
576
577                                return removedElement;
578                                }
579                        }
580                }
581        }
582 
583  //element is not found, return empty element
584  removedElement.red   = 0.0;
585  removedElement.green = 0.0;
586  removedElement.blue  = 0.0;
587  removedElement.alpha = 0.0;
588  removedElement.position = NAN;
589  removedElement.nextElement = nil;
590 
591  return removedElement;
592  }
593
594
595- (CTGradientElement *)elementAtIndex:(unsigned)index;                 
596  {
597  unsigned count = 0;
598  CTGradientElement *currentElement = elementList;
599 
600  while(currentElement != nil)
601        {
602        if(count == index)
603                return currentElement;
604       
605        count++;
606        currentElement = currentElement->nextElement;
607        }
608 
609  return nil;
610  }
611#pragma mark -
612
613
614
615#pragma mark Core Graphics
616//////////////////////////////////////Blending Functions/////////////////////////////////////
617void linearEvaluation (void *info, const float *in, float *out)
618  {
619  float position = *in;
620 
621  if(*(CTGradientElement **)info == nil)        //if elementList is empty return clear color
622        {
623        out[0] = out[1] = out[2] = out[3] = 1;
624        return;
625        }
626 
627  //This grabs the first two colors in the sequence
628  CTGradientElement *color1 = *(CTGradientElement **)info;
629  CTGradientElement *color2 = color1->nextElement;
630 
631  //make sure first color and second color are on other sides of position
632  while(color2 != nil && color2->position < position)
633        {
634        color1 = color2;
635        color2 = color1->nextElement;
636        }
637  //if we don't have another color then make next color the same color
638  if(color2 == nil)
639    {
640        color2 = color1;
641    }
642 
643  //----------FailSafe settings----------
644  //color1->red   = 1; color2->red   = 0;
645  //color1->green = 1; color2->green = 0;
646  //color1->blue  = 1; color2->blue  = 0;
647  //color1->alpha = 1; color2->alpha = 1;
648  //color1->position = .5;
649  //color2->position = .5;
650  //-------------------------------------
651 
652  if(position <= color1->position)                      //Make all below color color1's position equal to color1
653        {
654        out[0] = color1->red;
655        out[1] = color1->green;
656        out[2] = color1->blue;
657        out[3] = color1->alpha;
658        }
659  else if (position >= color2->position)        //Make all above color color2's position equal to color2
660        {
661        out[0] = color2->red;
662        out[1] = color2->green;
663        out[2] = color2->blue;
664        out[3] = color2->alpha;
665        }
666  else                                                                          //Interpolate color at postions between color1 and color1
667        {
668        //adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position
669        position = (position-color1->position)/(color2->position - color1->position);
670       
671        out[0] = (color2->red   - color1->red  )*position + color1->red;
672        out[1] = (color2->green - color1->green)*position + color1->green;
673        out[2] = (color2->blue  - color1->blue )*position + color1->blue;
674        out[3] = (color2->alpha - color1->alpha)*position + color1->alpha;
675        }
676  }
677
678
679
680
681//Chromatic Evaluation -
682//      This blends colors by their Hue, Saturation, and Value(Brightness) right now I just
683//      transform the RGB values stored in the CTGradientElements to HSB, in the future I may
684//      streamline it to avoid transforming in and out of HSB colorspace *for later*
685//
686//      For the chromatic blend we shift the hue of color1 to meet the hue of color2. To do
687//      this we will add to the hue's angle (if we subtract we'll be doing the inverse
688//      chromatic...scroll down more for that). All we need to do is keep adding to the hue
689//  until we wrap around the colorwheel and get to color2.
690void chromaticEvaluation(void *info, const float *in, float *out)
691  {
692  float position = *in;
693 
694  if(*(CTGradientElement **)info == nil)        //if elementList is empty return clear color
695        {
696        out[0] = out[1] = out[2] = out[3] = 1;
697        return;
698        }
699 
700  //This grabs the first two colors in the sequence
701  CTGradientElement *color1 = *(CTGradientElement **)info;
702  CTGradientElement *color2 = color1->nextElement;
703 
704  float c1[4];
705  float c2[4];
706   
707  //make sure first color and second color are on other sides of position
708  while(color2 != nil && color2->position < position)
709        {
710        color1 = color2;
711        color2 = color1->nextElement;
712        }
713  //if we don't have another color then make next color the same color
714  if(color2 == nil)
715    {
716        color2 = color1;
717    }
718 
719 
720  c1[0] = color1->red;
721  c1[1] = color1->green;
722  c1[2] = color1->blue;
723  c1[3] = color1->alpha;
724 
725  c2[0] = color2->red;
726  c2[1] = color2->green;
727  c2[2] = color2->blue;
728  c2[3] = color2->alpha;
729 
730  transformRGB_HSV(c1);
731  transformRGB_HSV(c2);
732  resolveHSV(c1,c2);
733 
734  if(c1[0] > c2[0]) //if color1's hue is higher than color2's hue then
735         c2[0] += 360;  //      we need to move c2 one revolution around the wheel
736 
737 
738  if(position <= color1->position)                      //Make all below color color1's position equal to color1
739        {
740        out[0] = c1[0];
741        out[1] = c1[1];
742        out[2] = c1[2];
743        out[3] = c1[3];
744        }
745  else if (position >= color2->position)        //Make all above color color2's position equal to color2
746        {
747        out[0] = c2[0];
748        out[1] = c2[1];
749        out[2] = c2[2];
750        out[3] = c2[3];
751        }
752  else                                                                          //Interpolate color at postions between color1 and color1
753        {
754        //adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position
755        position = (position-color1->position)/(color2->position - color1->position);
756       
757        out[0] = (c2[0] - c1[0])*position + c1[0];
758        out[1] = (c2[1] - c1[1])*position + c1[1];
759        out[2] = (c2[2] - c1[2])*position + c1[2];
760        out[3] = (c2[3] - c1[3])*position + c1[3];
761        }
762   
763  transformHSV_RGB(out);
764  }
765
766
767
768//Inverse Chromatic Evaluation -
769//      Inverse Chromatic is about the same story as Chromatic Blend, but here the Hue
770//      is strictly decreasing, that is we need to get from color1 to color2 by decreasing
771//      the 'angle' (i.e. 90Œ -> 180Œ would be done by subtracting 270Œ and getting -180Œ...
772//      which is equivalent to 180Œ mod 360Œ
773void inverseChromaticEvaluation(void *info, const float *in, float *out)
774  {
775    float position = *in;
776 
777  if(*(CTGradientElement **)info == nil)        //if elementList is empty return clear color
778        {
779        out[0] = out[1] = out[2] = out[3] = 1;
780        return;
781        }
782 
783  //This grabs the first two colors in the sequence
784  CTGradientElement *color1 = *(CTGradientElement **)info;
785  CTGradientElement *color2 = color1->nextElement;
786 
787  float c1[4];
788  float c2[4];
789     
790  //make sure first color and second color are on other sides of position
791  while(color2 != nil && color2->position < position)
792        {
793        color1 = color2;
794        color2 = color1->nextElement;
795        }
796  //if we don't have another color then make next color the same color
797  if(color2 == nil)
798    {
799        color2 = color1;
800    }
801
802  c1[0] = color1->red;
803  c1[1] = color1->green;
804  c1[2] = color1->blue;
805  c1[3] = color1->alpha;
806 
807  c2[0] = color2->red;
808  c2[1] = color2->green;
809  c2[2] = color2->blue;
810  c2[3] = color2->alpha;
811
812  transformRGB_HSV(c1);
813  transformRGB_HSV(c2);
814  resolveHSV(c1,c2);
815
816  if(c1[0] < c2[0]) //if color1's hue is higher than color2's hue then
817         c1[0] += 360;  //      we need to move c2 one revolution back on the wheel
818
819 
820  if(position <= color1->position)                      //Make all below color color1's position equal to color1
821        {
822        out[0] = c1[0];
823        out[1] = c1[1];
824        out[2] = c1[2];
825        out[3] = c1[3];
826        }
827  else if (position >= color2->position)        //Make all above color color2's position equal to color2
828        {
829        out[0] = c2[0];
830        out[1] = c2[1];
831        out[2] = c2[2];
832        out[3] = c2[3];
833        }
834  else                                                                          //Interpolate color at postions between color1 and color1
835        {
836        //adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position
837        position = (position-color1->position)/(color2->position - color1->position);
838       
839        out[0] = (c2[0] - c1[0])*position + c1[0];
840        out[1] = (c2[1] - c1[1])*position + c1[1];
841        out[2] = (c2[2] - c1[2])*position + c1[2];
842        out[3] = (c2[3] - c1[3])*position + c1[3];
843        }
844   
845  transformHSV_RGB(out);
846  }
847
848
849
850void transformRGB_HSV(float *components) //H,S,B -> R,G,B
851        {
852        float H, S, V;
853        float R = components[0],
854                  G = components[1],
855                  B = components[2];
856       
857        float MAX = R > G ? (R > B ? R : B) : (G > B ? G : B),
858              MIN = R < G ? (R < B ? R : B) : (G < B ? G : B);
859       
860        if(MAX == MIN)
861                H = NAN;
862        else if(MAX == R)
863                if(G >= B)
864                        H = 60*(G-B)/(MAX-MIN)+0;
865                else
866                        H = 60*(G-B)/(MAX-MIN)+360;
867        else if(MAX == G)
868                H = 60*(B-R)/(MAX-MIN)+120;
869        else if(MAX == B)
870                H = 60*(R-G)/(MAX-MIN)+240;
871       
872        S = MAX == 0 ? 0 : 1 - MIN/MAX;
873        V = MAX;
874       
875        components[0] = H;
876        components[1] = S;
877        components[2] = V;
878        }
879
880void transformHSV_RGB(float *components) //H,S,B -> R,G,B
881        {
882        float R, G, B;
883        float H = fmodf(components[0],359),     //map to [0,360)
884                  S = components[1],
885                  V = components[2];
886       
887        int   Hi = (int)floorf(H/60.) % 6;
888        float f  = H/60-Hi,
889                  p  = V*(1-S),
890                  q  = V*(1-f*S),
891                  t  = V*(1-(1-f)*S);
892       
893        switch (Hi)
894                {
895                case 0: R=V;G=t;B=p;    break;
896                case 1: R=q;G=V;B=p;    break;
897                case 2: R=p;G=V;B=t;    break;
898                case 3: R=p;G=q;B=V;    break;
899                case 4: R=t;G=p;B=V;    break;
900                case 5: R=V;G=p;B=q;    break;
901                }
902       
903        components[0] = R;
904        components[1] = G;
905        components[2] = B;
906        }
907
908void resolveHSV(float *color1, float *color2)   //H value may be undefined (i.e. graycale color)
909        {                                                                                       //      we want to fill it with a sensible value
910        if(isnan(color1[0]) && isnan(color2[0]))
911                color1[0] = color2[0] = 0;
912        else if(isnan(color1[0]))
913                color1[0] = color2[0];
914        else if(isnan(color2[0]))
915                color2[0] = color1[0];
916        }
917
918@end
Note: See TracBrowser for help on using the repository browser.