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