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