Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion smack-experimental/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dependencies {
api project(':smack-core')
api project(':smack-extensions')

implementation "org.hsluv:hsluv:0.2"
implementation "org.hsluv:hsluv:1.0"

testFixturesApi(testFixtures(project(":smack-extensions")))
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
*/
package org.jivesoftware.smackx.colors;

import static java.lang.Byte.toUnsignedInt;

import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.SHA1;

import org.hsluv.HUSLColorConverter;
import org.hsluv.HsluvColorConverter;

/**
* Smack API for Consistent Color Generation (XEP-0392).
Expand Down Expand Up @@ -68,7 +70,7 @@ public enum Deficiency {
*/
private static double createAngle(CharSequence input) {
byte[] h = SHA1.bytes(input.toString());
double v = u(h[0]) + (256 * u(h[1]));
double v = toUnsignedInt(h[0]) + (256 * toUnsignedInt(h[1]));
double d = v / 65536;
return d * 360;
}
Expand Down Expand Up @@ -107,41 +109,23 @@ private static double applyColorDeficiencyCorrection(double angle, Deficiency de
*
* @see <a href="https://xmpp.org/extensions/xep-0392.html#algorithm-rgb">XEP-0392 §5.4: RGB generation</a>
*/
private static double[] hsluvToRgb(double hue) {
return hsluvToRgb(hue, 100, 50);
}

/**
* Converting a HSLuv angle to RGB.
*
* @param hue angle 0 <= hue < 360
* @param saturation saturation 0 <= saturation <= 100
* @param lightness lightness 0 <= lightness <= 100
* @return rbg array with values 0 <= (r,g,b) <= 1
*
* @see <a href="https://www.rapidtables.com/convert/color/hsl-to-rgb.html">HSL to RGB conversion</a>
*/
private static double[] hsluvToRgb(double hue, double saturation, double lightness) {
return HUSLColorConverter.hsluvToRgb(new double[] {hue, saturation, lightness});
}

private static double[] mixWithBackground(double[] rgbi, float[] rgbb) {
return new double[] {
0.2 * (1 - rgbb[0]) + 0.8 * rgbi[0],
0.2 * (1 - rgbb[1]) + 0.8 * rgbi[1],
0.2 * (1 - rgbb[2]) + 0.8 * rgbi[2]
};
private static float[] hsluvToRgb(double hue) {
HsluvColorConverter conv = new HsluvColorConverter();
conv.hsluv_h = hue;
conv.hsluv_s = 100;
conv.hsluv_l = 50;
conv.hsluvToRgb();
// avoid negative values
conv.rgb_r = Math.max(0, conv.rgb_r);
conv.rgb_g = Math.max(0, conv.rgb_g);
conv.rgb_b = Math.max(0, conv.rgb_b);
return new float[] {(float) conv.rgb_r, (float) conv.rgb_g, (float) conv.rgb_b};
}

/**
* Treat a signed java byte as unsigned to get its numerical value.
*
* @param b signed java byte
* @return integer value of its unsigned representation
*/
private static int u(byte b) {
// Get unsigned value of signed byte as an integer.
return b & 0xFF;
private static void mixWithBackground(float[] rgbi, float[] rgbb) {
rgbi[0] = 0.2f * (1 - rgbb[0]) + 0.8f * rgbi[0];
rgbi[1] = 0.2f * (1 - rgbb[1]) + 0.8f * rgbi[1];
rgbi[2] = 0.2f * (1 - rgbb[2]) + 0.8f * rgbi[2];
}

/**
Expand All @@ -167,12 +151,12 @@ public static float[] RGBFrom(CharSequence input) {
public static float[] RGBFrom(CharSequence input, ConsistentColorSettings settings) {
double angle = createAngle(input);
double correctedAngle = applyColorDeficiencyCorrection(angle, settings.getDeficiency());
double[] rgb = hsluvToRgb(correctedAngle);
float[] rgb = hsluvToRgb(correctedAngle);
if (settings.backgroundRGB != null) {
rgb = mixWithBackground(rgb, settings.backgroundRGB);
mixWithBackground(rgb, settings.backgroundRGB);
}

return new float[] {(float) rgb[0], (float) rgb[1], (float) rgb[2]};
return rgb;
}

public static int[] floatRgbToInts(float[] floats) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,99 +45,106 @@ public class ConsistentColorsTest extends SmackTestSuite {
@Test
public void romeoNoDeficiencyTest() {
float[] rgb = new float[] {0.865f, 0.000f, 0.686f};
assertRGBEquals(rgb, ConsistentColor.RGBFrom(romeo), EPS);
assertRGBEquals(rgb, ConsistentColor.RGBFrom(romeo));
}

@Test
public void romeoRedGreenBlindnessTest() {
float[] expected = new float[] {0.865f, 0.000f, 0.686f};
float[] expected = new float[] {0.865f, 0.000f, 0.68620354f};
float[] actual = ConsistentColor.RGBFrom(romeo, redGreenDeficiency);
assertRGBEquals(expected, actual, EPS);
assertRGBEquals(expected, actual);
}

@Test
public void romeoBlueBlindnessTest() {
float[] expected = new float[] {0.000f, 0.535f, 0.350f};
float[] actual = ConsistentColor.RGBFrom(romeo, blueBlindnessDeficiency);
assertRGBEquals(expected, actual, EPS);
assertRGBEquals(expected, actual);
}

@Test
public void julietNickTest() {
// may have a negative Red
float[] expected = new float[] {0f, 0.5226505f, 0.50349814f};
float[] actual = ConsistentColor.RGBFrom("juliet");
assertRGBEquals(expected, actual);
}

@Test
public void julietNoDeficiencyTest() {
float[] expected = new float[] {0.000f, 0.515f, 0.573f};
float[] actual = ConsistentColor.RGBFrom(juliet, noDeficiency);
assertRGBEquals(expected, actual, EPS);
assertRGBEquals(expected, actual);
}

@Test
public void julietRedGreenBlindnessTest() {
float[] expected = new float[] {0.742f, 0.359f, 0.000f};
float[] actual = ConsistentColor.RGBFrom(juliet, redGreenDeficiency);
assertRGBEquals(expected, actual, EPS);
assertRGBEquals(expected, actual);
}

@Test
public void julietBlueBlindnessTest() {
float[] expected = new float[] {0.742f, 0.359f, 0.000f};
float[] actual = ConsistentColor.RGBFrom(juliet, blueBlindnessDeficiency);
assertRGBEquals(expected, actual, EPS);
assertRGBEquals(expected, actual);
}

@Test
public void emojiNoDeficiencyTest() {
float[] expected = new float[] {0.872f, 0.000f, 0.659f};
float[] actual = ConsistentColor.RGBFrom(emoji, noDeficiency);
assertRGBEquals(expected, actual, EPS);
assertRGBEquals(expected, actual);
}

@Test
public void emojiRedGreenBlindnessTest() {
float[] expected = new float[] {0.872f, 0.000f, 0.659f};
float[] actual = ConsistentColor.RGBFrom(emoji, redGreenDeficiency);
assertRGBEquals(expected, actual, EPS);
assertRGBEquals(expected, actual);
}

@Test
public void emojiBlueBlindnessTest() {
float[] expected = new float[] {0.000f, 0.533f, 0.373f};
float[] actual = ConsistentColor.RGBFrom(emoji, blueBlindnessDeficiency);
assertRGBEquals(expected, actual, EPS);
assertRGBEquals(expected, actual);
}

@Test
public void councilNoDeficiencyTest() {
float[] expected = new float[] {0.918f, 0.000f, 0.394f};
float[] actual = ConsistentColor.RGBFrom(council, noDeficiency);
assertRGBEquals(expected, actual, EPS);
assertRGBEquals(expected, actual);
}

@Test
public void councilRedGreenBlindnessTest() {
float[] expected = new float[] {0.918f, 0.000f, 0.394f};
float[] actual = ConsistentColor.RGBFrom(council, redGreenDeficiency);
assertRGBEquals(expected, actual, EPS);
assertRGBEquals(expected, actual);
}

@Test
public void councilBlueBlindnessTest() {
float[] expected = new float[] {0.000f, 0.524f, 0.485f};
float[] actual = ConsistentColor.RGBFrom(council, blueBlindnessDeficiency);
assertRGBEquals(expected, actual, EPS);
assertRGBEquals(expected, actual);
}

/**
* Check, whether the values of two float arrays of size 3 are pairwise equal with an allowed error of eps.
*
* @param expected expected values
* @param actual actual values
* @param eps allowed error
*/
private static void assertRGBEquals(float[] expected, float[] actual, float eps) {
private static void assertRGBEquals(float[] expected, float[] actual) {
assertEquals(3, expected.length);
assertEquals(3, actual.length);

for (int i = 0; i < actual.length; i++) {
assertEquals(expected[i], actual[i], eps);
}
assertEquals(expected[0], actual[0], EPS);
assertEquals(expected[1], actual[1], EPS);
assertEquals(expected[2], actual[2], EPS);
}
}