00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 @import <Foundation/CPObject.j>
00024
00025 @import "CGColor.j"
00026
00027 @import "CPCompatibility.j"
00028 @import "CPImage.j"
00029
00030
00031 var _redComponent = 0,
00032 _greenComponent = 1,
00033 _blueComponent = 2,
00034 _alphaCompnent = 3;
00035
00036 var _hueComponent = 0,
00037 _saturationComponent = 1,
00038 _brightnessComponent = 2;
00039
00040 var cachedBlackColor,
00041 cachedRedColor,
00042 cachedGreenColor,
00043 cachedBlueColor,
00044 cachedYellowColor,
00045 cachedGrayColor,
00046 cachedLightGrayColor,
00047 cachedDarkGrayColor,
00048 cachedWhiteColor,
00049 cachedShadowColor,
00050 cachedClearColor;
00051
00067 @implementation CPColor : CPObject
00068 {
00069 CPArray _components;
00070
00071 CPImage _patternImage;
00072 CPString _cssString;
00073 }
00074
00088 + (CPColor)colorWithRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
00089 {
00090 return [[CPColor alloc] _initWithRGBA:[red, green, blue, alpha]];
00091 }
00092
00108 + (CPColor)colorWithCalibratedRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
00109 {
00110 return [self colorWithRed:red green:green blue:blue alpha:alpha];
00111 }
00112
00113
00123 + (CPColor)colorWithWhite:(float)white alpha:(float)alpha
00124 {
00125 return [[CPColor alloc] _initWithRGBA:[white, white, white, alpha]];
00126 }
00127
00139 + (CPColor)colorWithCalibratedWhite:(float)white alpha:(float)alpha
00140 {
00141 return [self colorWithWhite:white alpha:alpha];
00142 }
00143
00153 + (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness
00154 {
00155 return [self colorWithHue:hue saturation:saturation brightness:brightness alpha:1.0];
00156 }
00157
00158 + (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness alpha:(float)alpha
00159 {
00160 if(saturation === 0.0)
00161 return [CPColor colorWithCalibratedWhite:brightness / 100.0 alpha:alpha];
00162
00163 var f = hue % 60,
00164 p = (brightness * (100 - saturation)) / 10000,
00165 q = (brightness * (6000 - saturation * f)) / 600000,
00166 t = (brightness * (6000 - saturation * (60 -f))) / 600000,
00167 b = brightness / 100.0;
00168
00169 switch(FLOOR(hue / 60))
00170 {
00171 case 0: return [CPColor colorWithCalibratedRed: b green: t blue: p alpha: alpha];
00172 case 1: return [CPColor colorWithCalibratedRed: q green: b blue: p alpha: alpha];
00173 case 2: return [CPColor colorWithCalibratedRed: p green: b blue: t alpha: alpha];
00174 case 3: return [CPColor colorWithCalibratedRed: p green: q blue: b alpha: alpha];
00175 case 4: return [CPColor colorWithCalibratedRed: t green: p blue: b alpha: alpha];
00176 case 5: return [CPColor colorWithCalibratedRed: b green: p blue: q alpha: alpha];
00177 }
00178 }
00179
00190 + (CPColor)colorWithHexString:(string)hex
00191 {
00192 return [[CPColor alloc] _initWithRGBA: hexToRGB(hex)];
00193 }
00194
00198 + (CPColor)blackColor
00199 {
00200 if (!cachedBlackColor)
00201 cachedBlackColor = [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 0.0, 1.0]];
00202
00203 return cachedBlackColor;
00204 }
00205
00209 + (CPColor)blueColor
00210 {
00211 if (!cachedBlueColor)
00212 cachedBlueColor = [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 1.0, 1.0]];
00213
00214 return cachedBlueColor;
00215 }
00216
00220 + (CPColor)darkGrayColor
00221 {
00222 if (!cachedDarkGrayColor)
00223 cachedDarkGrayColor = [CPColor colorWithCalibratedWhite:1.0 / 3.0 alpha:1.0];
00224
00225 return cachedDarkGrayColor;
00226 }
00227
00231 + (CPColor)grayColor
00232 {
00233 if (!cachedGrayColor)
00234 cachedGrayColor = [CPColor colorWithCalibratedWhite:0.5 alpha: 1.0];
00235
00236 return cachedGrayColor;
00237 }
00238
00242 + (CPColor)greenColor
00243 {
00244 if (!cachedGreenColor)
00245 cachedGreenColor = [[CPColor alloc] _initWithRGBA:[0.0, 1.0, 0.0, 1.0]];
00246
00247 return cachedGreenColor;
00248 }
00249
00253 + (CPColor)lightGrayColor
00254 {
00255 if (!cachedLightGrayColor)
00256 cachedLightGrayColor = [CPColor colorWithCalibratedWhite:2.0 / 3.0 alpha:1.0];
00257
00258 return cachedLightGrayColor;
00259 }
00260
00264 + (CPColor)redColor
00265 {
00266 if (!cachedRedColor)
00267 cachedRedColor = [[CPColor alloc] _initWithRGBA:[1.0, 0.0, 0.0, 1.0]];
00268
00269 return cachedRedColor;
00270 }
00271
00275 + (CPColor)whiteColor
00276 {
00277 if (!cachedWhiteColor)
00278 cachedWhiteColor = [[CPColor alloc] _initWithRGBA:[1.0, 1.0, 1.0, 1.0]];
00279
00280 return cachedWhiteColor;
00281 }
00282
00286 + (CPColor)yellowColor
00287 {
00288 if (!cachedYellowColor)
00289 cachedYellowColor = [[CPColor alloc] _initWithRGBA:[1.0, 1.0, 0.0, 1.0]];
00290
00291 return cachedYellowColor;
00292 }
00293
00298 + (CPColor)shadowColor
00299 {
00300 if (!cachedShadowColor)
00301 cachedShadowColor = [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 0.0, 1.0 / 3.0]];
00302
00303 return cachedShadowColor;
00304 }
00305
00310 + (CPColor)clearColor
00311 {
00312 if (!cachedClearColor)
00313 cachedClearColor = [self colorWithCalibratedWhite:0.0 alpha:0.0];
00314
00315 return cachedClearColor;
00316 }
00317
00323 + (CPColor)colorWithPatternImage:(CPImage)anImage
00324 {
00325 return [[CPColor alloc] _initWithPatternImage:anImage];
00326 }
00327
00334 + (CPColor)colorWithCSSString:(CPString)aString
00335 {
00336 return [[CPColor alloc] _initWithCSSString: aString];
00337 }
00338
00339
00340 - (id)_initWithCSSString:(CPString)aString
00341 {
00342 if(aString.indexOf("rgb") == CPNotFound)
00343 return nil;
00344
00345 self = [super init];
00346
00347 var startingIndex = aString.indexOf("(");
00348 var parts = aString.substring(startingIndex+1).split(',');
00349
00350 _components = [
00351 parseInt(parts[0], 10) / 255.0,
00352 parseInt(parts[1], 10) / 255.0,
00353 parseInt(parts[2], 10) / 255.0,
00354 parts[3] ? parseInt(parts[3], 10) / 255.0 : 1.0
00355 ]
00356
00357 _cssString = aString;
00358
00359 return self;
00360 }
00361
00362
00363 - (id)_initWithRGBA:(CPArray)components
00364 {
00365 self = [super init];
00366
00367 if (self)
00368 {
00369 _components = components;
00370
00371 if (!CPFeatureIsCompatible(CPCSSRGBAFeature) && _components[3] != 1.0 && window.Base64 && window.CRC32)
00372 {
00373 var bytes = [0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x8,0x3,0x0,0x0,0x0,0x28,0xcb,0x34,0xbb,0x0,0x0,0x3,0x0,0x50,0x4c,0x54,0x45,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x89,0x99,0x55,0x0,0x0,0x0,0x1,0x74,0x52,0x4e,0x53,0x0,0x40,0xe6,0xd8,0x66,0x0,0x0,0x0,0x10,0x49,0x44,0x41,0x54,0x78,0xda,0x62,0x60,0x0,0x0,0x0,0x0,0xff,0xff,0x3,0x0,0x0,0x2,0x0,0x1,0x24,0x7f,0x24,0xf1,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82,0xff];
00374 var r_off = 41;
00375 var g_off = 42;
00376 var b_off = 43;
00377 var a_off = 821;
00378 var plte_crc_off = 809;
00379 var trns_crc_off = 822;
00380 var plte_type_off = 37;
00381 var trns_type_off = 817;
00382
00383 bytes[r_off] = Math.round(_components[0]*255);
00384 bytes[g_off] = Math.round(_components[1]*255);
00385 bytes[b_off] = Math.round(_components[2]*255);
00386 bytes[a_off] = Math.round(_components[3]*255);
00387
00388
00389 var new_plte_crc = integerToBytes(CRC32.getCRC(bytes, plte_type_off, 4+768), 4);
00390 var new_trns_crc = integerToBytes(CRC32.getCRC(bytes, trns_type_off, 4+1), 4);
00391
00392
00393 for (var i = 0; i < 4; i++)
00394 {
00395 bytes[plte_crc_off+i] = new_plte_crc[i];
00396 bytes[trns_crc_off+i] = new_trns_crc[i];
00397 }
00398
00399
00400 var base64image = Base64.encode(bytes);
00401
00402 _cssString = "url(\"data:image/png;base64," + base64image + "\")";
00403 }
00404 else
00405 {
00406 var hasAlpha = CPFeatureIsCompatible(CPCSSRGBAFeature) && _components[3] != 1.0;
00407
00408 _cssString = (hasAlpha ? "rgba(" : "rgb(") +
00409 parseInt(_components[0] * 255.0) + ", " +
00410 parseInt(_components[1] * 255.0) + ", " +
00411 parseInt(_components[2] * 255.0) +
00412 (hasAlpha ? (", " + _components[3]) : "") + ")";
00413 }
00414 }
00415 return self;
00416 }
00417
00418
00419 - (id)_initWithPatternImage:(CPImage)anImage
00420 {
00421 self = [super init];
00422
00423 if (self)
00424 {
00425 _patternImage = anImage;
00426 _cssString = "url(\"" + [_patternImage filename] + "\")";
00427 }
00428
00429 return self;
00430 }
00431
00435 - (CPImage)patternImage
00436 {
00437 return _patternImage;
00438 }
00439
00443 - (float)alphaComponent
00444 {
00445 return _components[3];
00446 }
00447
00451 - (float)blueComponent
00452 {
00453 return _components[2];
00454 }
00455
00459 - (float)greenComponent
00460 {
00461 return _components[1];
00462 }
00463
00467 - (float)redComponent
00468 {
00469 return _components[0];
00470 }
00471
00483 - (CPArray)components
00484 {
00485 return _components;
00486 }
00487
00495 - (CPColor)colorWithAlphaComponent:(float)anAlphaComponent
00496 {
00497 var components = _components.slice();
00498
00499 components[components.length - 1] = anAlphaComponent;
00500
00501 return [[[self class] alloc] _initWithRGBA:components];
00502 }
00503
00514 - (CPArray)hsbComponents
00515 {
00516 var red = ROUND(_components[_redComponent] * 255.0),
00517 green = ROUND(_components[_greenComponent] * 255.0),
00518 blue = ROUND(_components[_blueComponent] * 255.0);
00519
00520 var max = MAX(red, green, blue),
00521 min = MIN(red, green, blue),
00522 delta = max - min;
00523
00524 var brightness = max / 255.0,
00525 saturation = (max != 0) ? delta / max : 0;
00526
00527 var hue;
00528 if(saturation == 0)
00529 hue = 0;
00530 else
00531 {
00532 var rr = (max - red) / delta;
00533 var gr = (max - green) / delta;
00534 var br = (max - blue) / delta;
00535
00536 if (red == max)
00537 hue = br - gr;
00538 else if (green == max)
00539 hue = 2 + rr - br;
00540 else
00541 hue = 4 + gr - rr;
00542
00543 hue /= 6;
00544 if (hue < 0)
00545 hue++;
00546 }
00547
00548 return [
00549 ROUND(hue * 360.0),
00550 ROUND(saturation * 100.0),
00551 ROUND(brightness * 100.0)
00552 ];
00553 }
00554
00564 - (CPString)cssString
00565 {
00566 return _cssString;
00567 }
00568
00572 - (CPString)hexString
00573 {
00574 return rgbToHex([self redComponent], [self greenComponent], [self blueComponent])
00575 }
00576
00577 - (BOOL)isEqual:(CPColor)aColor
00578 {
00579 if (!aColor)
00580 return NO;
00581
00582 if (aColor === self)
00583 return YES;
00584
00585 return [aColor isKindOfClass:CPColor] && [aColor cssString] === [self cssString];
00586 }
00587
00588 - (CPString)description
00589 {
00590 return [super description]+" "+[self cssString];
00591 }
00592
00593 @end
00594
00595 @implementation CPColor (CoreGraphicsExtensions)
00596
00600 - (void)set
00601 {
00602 [self setFill];
00603 [self setStroke];
00604 }
00605
00609 - (void)setFill
00610 {
00611 var ctx = [[CPGraphicsContext currentContext] graphicsPort];
00612 CGContextSetFillColor(ctx, self);
00613 }
00614
00618 - (void)setStroke
00619 {
00620 var ctx = [[CPGraphicsContext currentContext] graphicsPort];
00621 CGContextSetStrokeColor(ctx, self);
00622 }
00623
00624 @end
00625
00626 var CPColorComponentsKey = @"CPColorComponentsKey",
00627 CPColorPatternImageKey = @"CPColorPatternImageKey";
00628
00629 @implementation CPColor (CPCoding)
00630
00635 - (id)initWithCoder:(CPCoder)aCoder
00636 {
00637 if ([aCoder containsValueForKey:CPColorPatternImageKey])
00638 return [self _initWithPatternImage:[aCoder decodeObjectForKey:CPColorPatternImageKey]];
00639
00640 return [self _initWithRGBA:[aCoder decodeObjectForKey:CPColorComponentsKey]];
00641 }
00642
00647 - (void)encodeWithCoder:(CPCoder)aCoder
00648 {
00649 if (_patternImage)
00650 [aCoder encodeObject:_patternImage forKey:CPColorPatternImageKey];
00651 else
00652 [aCoder encodeObject:_components forKey:CPColorComponentsKey];
00653 }
00654
00655 @end
00656
00657 var hexCharacters = "0123456789ABCDEF";
00658
00665 function hexToRGB(hex)
00666 {
00667 if ( hex.length == 3 )
00668 hex = hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2);
00669 if(hex.length != 6)
00670 return null;
00671
00672 hex = hex.toUpperCase();
00673
00674 for(var i=0; i<hex.length; i++)
00675 if(hexCharacters.indexOf(hex.charAt(i)) == -1)
00676 return null;
00677
00678 var red = (hexCharacters.indexOf(hex.charAt(0)) * 16 + hexCharacters.indexOf(hex.charAt(1))) / 255.0;
00679 var green = (hexCharacters.indexOf(hex.charAt(2)) * 16 + hexCharacters.indexOf(hex.charAt(3))) / 255.0;
00680 var blue = (hexCharacters.indexOf(hex.charAt(4)) * 16 + hexCharacters.indexOf(hex.charAt(5))) / 255.0;
00681
00682 return [red, green, blue, 1.0];
00683 }
00684
00685 function integerToBytes(integer, length) {
00686 if (!length)
00687 length = (integer == 0) ? 1 : Math.round((Math.log(integer)/Math.log(2))/8+0.5);
00688
00689 var bytes = new Array(length);
00690 for (var i = length-1; i >= 0; i--) {
00691 bytes[i] = integer & 255;
00692 integer = integer >> 8
00693 }
00694 return bytes;
00695 }
00696
00697 function rgbToHex(r,g,b) {
00698 return byteToHex(r) + byteToHex(g) + byteToHex(b);
00699 }
00700
00701 function byteToHex(n) {
00702 if (!n || isNaN(n)) return "00";
00703 n = ROUND(MIN(255,MAX(0,256*n)));
00704 return hexCharacters.charAt((n - n % 16) / 16) +
00705 hexCharacters.charAt(n % 16);
00706 }
00707
00708
00709
00710
00711
00712