diff --git a/AppKit/CPColor.j b/AppKit/CPColor.j index 6f302cefd..e3fdd083e 100644 --- a/AppKit/CPColor.j +++ b/AppKit/CPColor.j @@ -156,11 +156,15 @@ var cachedBlackColor, } /*! - Creates a new color in HSB space. + Creates a new color based on the given HSB components. - @param hue the hue value - @param saturation the saturation value - @param brightness the brightness value + Note: earlier versions of this method took a hue component as degrees between 0-360, + and saturation and brightness components as percent between 0-100. This method has + now been corrected to take all components in the 0-1 range as in Cocoa. + + @param hue the hue component (0.0-1.0) + @param saturation the saturation component (0.0-1.0) + @param brightness the brightness component (0.0-1.0) @return the initialized color */ @@ -169,25 +173,61 @@ var cachedBlackColor, return [self colorWithHue:hue saturation:saturation brightness:brightness alpha:1.0]; } +/*! + Calibrated colors are not supported in Cappuccino. + + This method has the same result as [CPColor colorWithHue:saturation:brightness:alpha:]. +*/ ++ (CPColor)colorWithCalibratedHue:(float)hue saturation:(float)saturation brightness:(float)brightness alpha:(float)alpha +{ + return [self colorWithHue:hue saturation:saturation brightness:brightness alpha:alpha]; +} + +/*! + Creates a new color based on the given HSB components. + + Note: earlier versions of this method took a hue component as degrees between 0-360, + and saturation and brightness components as percent between 0-100. This method has + now been corrected to take all components in the 0-1 range as in Cocoa. + + @param hue the hue component (0.0-1.0) + @param saturation the saturation component (0.0-1.0) + @param brightness the brightness component (0.0-1.0) + @param alpha the opacity component (0.0-1.0) + + @return the initialized color +*/ + (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness alpha:(float)alpha { + // Clamp values. + hue = MAX(MIN(hue, 1.0), 0.0); + saturation = MAX(MIN(saturation, 1.0), 0.0); + brightness = MAX(MIN(brightness, 1.0), 0.0); + if (saturation === 0.0) - return [CPColor colorWithCalibratedWhite:brightness / 100.0 alpha:alpha]; + return [CPColor colorWithCalibratedWhite:brightness alpha:alpha]; - var f = hue % 60, - p = (brightness * (100 - saturation)) / 10000, - q = (brightness * (6000 - saturation * f)) / 600000, - t = (brightness * (6000 - saturation * (60 -f))) / 600000, - b = brightness / 100.0; + var f = (hue * 360) % 60, + p = (brightness * (1 - saturation)), + q = (brightness * (60 - saturation * f)) / 60, + t = (brightness * (60 - saturation * (60 - f))) / 60, + b = brightness; - switch (FLOOR(hue / 60)) + switch (FLOOR(hue * 6)) { - case 0: return [CPColor colorWithCalibratedRed:b green:t blue:p alpha:alpha]; - case 1: return [CPColor colorWithCalibratedRed:q green:b blue:p alpha:alpha]; - case 2: return [CPColor colorWithCalibratedRed:p green:b blue:t alpha:alpha]; - case 3: return [CPColor colorWithCalibratedRed:p green:q blue:b alpha:alpha]; - case 4: return [CPColor colorWithCalibratedRed:t green:p blue:b alpha:alpha]; - case 5: return [CPColor colorWithCalibratedRed:b green:p blue:q alpha:alpha]; + case 0: + case 6: + return [CPColor colorWithCalibratedRed:b green:t blue:p alpha:alpha]; + case 1: + return [CPColor colorWithCalibratedRed:q green:b blue:p alpha:alpha]; + case 2: + return [CPColor colorWithCalibratedRed:p green:b blue:t alpha:alpha]; + case 3: + return [CPColor colorWithCalibratedRed:p green:q blue:b alpha:alpha]; + case 4: + return [CPColor colorWithCalibratedRed:t green:p blue:b alpha:alpha]; + case 5: + return [CPColor colorWithCalibratedRed:b green:p blue:q alpha:alpha]; } } @@ -575,6 +615,9 @@ var cachedBlackColor, /*! Returns an array with the HSB values for this color. + + The values are expressed as fractions between 0.0-1.0. + The index values are ordered as:
Index Component
@@ -621,12 +664,36 @@ var cachedBlackColor,
}
return [
- ROUND(hue * 360.0),
- ROUND(saturation * 100.0),
- ROUND(brightness * 100.0)
+ hue,
+ saturation,
+ brightness
];
}
+/*!
+ Returns the hue component, the H in HSB, of the receiver.
+*/
+- (float)hueComponent
+{
+ return [self hsbComponents][0];
+}
+
+/*!
+ Returns the saturation component, the S in HSB, of the receiver.
+*/
+- (float)saturationComponent
+{
+ return [self hsbComponents][1];
+}
+
+/*!
+ Returns the brightness component, the B in HSB, of the receiver.
+*/
+- (float)brightnessComponent
+{
+ return [self hsbComponents][2];
+}
+
/*!
Returns the CSS representation of this color. The color will
be in one of the following forms:
diff --git a/AppKit/CPColorPicker.j b/AppKit/CPColorPicker.j
index 7a1d43961..14aed7793 100644
--- a/AppKit/CPColorPicker.j
+++ b/AppKit/CPColorPicker.j
@@ -158,12 +158,12 @@
brightness = [_brightnessSlider floatValue];
[_hueSaturationView setWheelBrightness:brightness / 100.0];
- [_brightnessSlider setBackgroundColor:[CPColor colorWithHue:hue saturation:saturation brightness:100]];
+ [_brightnessSlider setBackgroundColor:[CPColor colorWithHue:hue / 360.0 saturation:saturation / 100.0 brightness:1]];
var colorPanel = [self colorPanel],
opacity = [colorPanel opacity];
- _cachedColor = [CPColor colorWithHue:hue saturation:saturation brightness:brightness alpha:opacity];
+ _cachedColor = [CPColor colorWithHue:hue / 360.0 saturation:saturation / 100.0 brightness:brightness / 100.0 alpha:opacity];
[[self colorPanel] setColor:_cachedColor];
}
@@ -194,10 +194,10 @@
var hsb = [newColor hsbComponents];
[_hueSaturationView setPositionToColor:newColor];
- [_brightnessSlider setFloatValue:hsb[2]];
- [_hueSaturationView setWheelBrightness:hsb[2] / 100.0];
+ [_brightnessSlider setFloatValue:hsb[2] * 100.0];
+ [_hueSaturationView setWheelBrightness:hsb[2]];
- [_brightnessSlider setBackgroundColor:[CPColor colorWithHue:hsb[0] saturation:hsb[1] brightness:100]];
+ [_brightnessSlider setBackgroundColor:[CPColor colorWithHue:hsb[0] saturation:hsb[1] brightness:1]];
}
- (CPImage)provideNewButtonImage
@@ -366,8 +366,8 @@
{
var hsb = [aColor hsbComponents],
bounds = [self bounds],
- angle = [self degreesToRadians:hsb[0]],
- distance = (hsb[1] / 100.0) * _radius;
+ angle = [self degreesToRadians:hsb[0] * 360.0],
+ distance = hsb[1] * _radius;
[self setAngle:angle distance:distance];
}
diff --git a/AppKit/CPSliderColorPicker.j b/AppKit/CPSliderColorPicker.j
index 38aa8427c..7e7f997b4 100644
--- a/AppKit/CPSliderColorPicker.j
+++ b/AppKit/CPSliderColorPicker.j
@@ -146,7 +146,7 @@
[_hueLabel setTextColor:[CPColor blackColor]];
_hueSlider = [[CPSlider alloc] initWithFrame:CGRectMake(15, 143, aFrame.size.width - 70, 20)];
- [_hueSlider setMaxValue:359.0];
+ [_hueSlider setMaxValue:0.999];
[_hueSlider setMinValue:0.0];
[_hueSlider setTarget:self];
[_hueSlider setAction:@selector(sliderChanged:)];
@@ -165,7 +165,7 @@
[_saturationLabel setTextColor:[CPColor blackColor]];
_saturationSlider = [[CPSlider alloc] initWithFrame:CGRectMake(15, 168, aFrame.size.width - 70, 20)];
- [_saturationSlider setMaxValue:100.0];
+ [_saturationSlider setMaxValue:1.0];
[_saturationSlider setMinValue:0.0];
[_saturationSlider setTarget:self];
[_saturationSlider setAction:@selector(sliderChanged:)];
@@ -184,7 +184,7 @@
[_brightnessLabel setTextColor:[CPColor blackColor]];
_brightnessSlider = [[CPSlider alloc] initWithFrame:CGRectMake(15, 194, aFrame.size.width - 70, 20)];
- [_brightnessSlider setMaxValue:100.0];
+ [_brightnessSlider setMaxValue:1.0];
[_brightnessSlider setMinValue:0.0];
[_brightnessSlider setTarget:self];
[_brightnessSlider setAction:@selector(sliderChanged:)];
@@ -282,6 +282,9 @@
- (void)setColor:(CPColor)aColor
{
+ if (!aColor)
+ [CPException raise:CPInvalidArgumentException reason:"aColor can't be nil"];
+
[self updateRGBSliders:aColor];
[self updateHSBSliders:aColor];
[self updateHex:aColor];
@@ -313,9 +316,9 @@
- (void)updateLabels
{
- [_hueValue setStringValue:ROUND([_hueSlider floatValue])];
- [_saturationValue setStringValue:ROUND([_saturationSlider floatValue])];
- [_brightnessValue setStringValue:ROUND([_brightnessSlider floatValue])];
+ [_hueValue setStringValue:ROUND([_hueSlider floatValue] * 360.0)];
+ [_saturationValue setStringValue:ROUND([_saturationSlider floatValue] * 100.0)];
+ [_brightnessValue setStringValue:ROUND([_brightnessSlider floatValue] * 100.0)];
[_redValue setStringValue:ROUND([_redSlider floatValue] * 255)];
[_greenValue setStringValue:ROUND([_greenSlider floatValue] * 255)];
@@ -363,15 +366,15 @@
[self sliderChanged:_blueSlider];
break;
- case _hueValue: [_hueSlider setFloatValue:MAX(MIN(ROUND(value), 360), 0)];
+ case _hueValue: [_hueSlider setFloatValue:MAX(MIN(ROUND(value), 360) / 360.0, 0)];
[self sliderChanged:_hueSlider];
break;
- case _saturationValue: [_saturationSlider setFloatValue:MAX(MIN(ROUND(value), 100), 0)];
+ case _saturationValue: [_saturationSlider setFloatValue:MAX(MIN(ROUND(value), 100) / 100.0, 0)];
[self sliderChanged:_saturationSlider];
break;
- case _brightnessValue: [_brightnessSlider setFloatValue:MAX(MIN(ROUND(value), 100), 0)];
+ case _brightnessValue: [_brightnessSlider setFloatValue:MAX(MIN(ROUND(value), 100) / 100.0, 0)];
[self sliderChanged:_brightnessSlider];
break;
}
diff --git a/Tests/AppKit/CPColorTest.j b/Tests/AppKit/CPColorTest.j
index a0ee065f1..b357a6805 100644
--- a/Tests/AppKit/CPColorTest.j
+++ b/Tests/AppKit/CPColorTest.j
@@ -8,7 +8,7 @@
{
var colors = ['000000', '0099CC', '7E8EAB', 'FFFFFF'];
for (var i = 0; i < colors.length; ++i)
- [self assert: colors[i] equals: [[CPColor colorWithHexString: colors[i]] hexString]];
+ [self assert:colors[i] equals:[[CPColor colorWithHexString:colors[i]] hexString]];
}
- (void)testColorWithCSSString
@@ -21,6 +21,33 @@
[self assert:128 equals:ROUND([rgbaColour alphaComponent] * 255) message:"alpha component"];
}
+- (void)testColorWithHue_saturation_brightness_
+{
+ var tests = [
+ [[0, 0, 0], [0, 0, 0]],
+ [[0, 0, 1], [1, 1, 1]],
+ [[0, 1, 1], [1, 0, 0]],
+ [[0.75, 1, 1], [0.5, 0, 1]],
+ [[0.5, 0.5, 0.5], [0.25, 0.5, 0.5]],
+ [[0.9, 0.8, 0.7], [0.7, 0.14, 0.476]]
+ ];
+
+ for (var i = 0; i < tests.length; i++)
+ {
+ var test = tests[i],
+ input = test[0],
+ expected = test[1],
+ c = [CPColor colorWithCalibratedHue:input[0] saturation:input[1] brightness:input[2] alpha:0.5];
+ [self assert:expected equals:[Math.round([c redComponent] * 1000) / 1000, Math.round([c greenComponent] * 1000) / 1000, Math.round([c blueComponent] * 1000) / 1000] message:@"hue: " + input[0] + " saturation: " + input[1] + " brightness: " + input[2]];
+ }
+
+ var hsb = [[CPColor colorWithHue:0.9 saturation:0.8 brightness:0.7] hsbComponents];
+ [self assert:[0.9, 0.8, 0.7] equals:[Math.round(hsb[0] * 10) / 10, Math.round(hsb[1] * 10) / 10, Math.round(hsb[2] * 10) / 10]];
+
+ hsb = [[CPColor colorWithHue:0.999 saturation:0.8 brightness:0.7] hsbComponents];
+ [self assert:[0.999, 0.8, 0.7] equals:[Math.round(hsb[0] * 1000) / 1000, Math.round(hsb[1] * 10) / 10, Math.round(hsb[2] * 10) / 10]];
+}
+
- (void)testIsEqual_
{
// Based on https://gist.github.com/e06f749362cb1166439f by spakanati.