Chameleon Applications

Chameleon Applications Svn Source Tree

Root/branches/diebuche/ChameleonPrefPane/Sources/CTGradient.m

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

Archive Download this file

Revision: 80