diff --git a/src/zutil/ColorUtil.java b/src/zutil/ColorUtil.java new file mode 100644 index 0000000..134d259 --- /dev/null +++ b/src/zutil/ColorUtil.java @@ -0,0 +1,114 @@ +package zutil; + +import zutil.converter.Converter; + +/** + * A utility class for color calculations and functions. + */ +public class ColorUtil { + + /** + * Generates a Hex String in the format '#RRGGBB' + * + * @param red the red color value between 0-255 + * @param green the green color value between 0-255 + * @param blue the blue color value between 0-255 + * @return a String hex + */ + public static String getHexString(int red, int green, int blue) { + byte redByte = Converter.toByte(red); + byte greenByte = Converter.toByte(green); + byte blueByte = Converter.toByte(blue); + return "#" + Converter.toHexString(new byte[]{redByte, greenByte, blueByte}); + } + + /** + * Converts a hex string with the format '#RRGGBB' to a int array of length 3 containing the red, green, blue integer values. + * + * @param hex a String hex. + * @return a int array representing RGB. + */ + public static int[] getRgbFromHexString(String hex) { + if (hex.length() != 7) + throw new IllegalArgumentException("Color hex length is not 7 but was " + hex.length()); + else if (hex.charAt(0) != '#') + throw new IllegalArgumentException("Color hex is required to start with the char #"); + + byte[] rgbByte = Converter.hexToByte(hex.substring(1)); + return new int[]{ + Converter.toInt(rgbByte[0]), + Converter.toInt(rgbByte[1]), + Converter.toInt(rgbByte[2])}; + } + + /** + * Calculates a hue value based on RGB values. + * + * @param red the red color value between 0-255 + * @param green the green color value between 0-255 + * @param blue the blue color value between 0-255 + * @return a hue grade in the range of 0-360 degrees. + */ + public static double getHue(int red, int green, int blue) { + double red_percent = red / 255.0; + double green_percent = green / 255.0; + double blue_percent = blue / 255.0; + + double cMax = Math.max(red_percent, Math.max(green_percent, blue_percent)); + double cMin = Math.min(red_percent, Math.min(green_percent, blue_percent)); + double delta = cMax - cMin; + + if (delta == 0) + return 0; + else if (cMax == red_percent) + return 60 * (((green_percent - blue_percent) / delta) % 6); + else if (cMax == green_percent) + return 60 * (((blue_percent - red_percent) / delta) + 2); + else if (cMax == blue_percent) + return 60 * (((red_percent - green_percent) / delta) + 4); + + throw new IllegalStateException(); + } + + /** + * Calculates a saturation percentage based on RGB values. + * + * @param red the red color value between 0-255 + * @param green the green color value between 0-255 + * @param blue the blue color value between 0-255 + * @return a percentage of saturation in the range of 0.0 to 1.0. + */ + public static double getSaturation(int red, int green, int blue) { + double red_percent = red / 255.0; + double green_percent = green / 255.0; + double blue_percent = blue / 255.0; + + double cMax = Math.max(red_percent, Math.max(green_percent, blue_percent)); + double cMin = Math.min(red_percent, Math.min(green_percent, blue_percent)); + double delta = cMax - cMin; + + if (delta == 0) + return 0; + else + return delta / (1 - Math.abs(2 * getLightness(red, green, blue) - 1)); + } + + /** + * Calculates a lightness percentage based on RGB values. + * + * @param red the red color value between 0-255 + * @param green the green color value between 0-255 + * @param blue the blue color value between 0-255 + * @return a percentage of lightness in the range of 0.0 to 1.0. + */ + public static double getLightness(int red, int green, int blue) { + double red_percent = red / 255.0; + double green_percent = green / 255.0; + double blue_percent = blue / 255.0; + + double cMax = Math.max(red_percent, Math.max(green_percent, blue_percent)); + double cMin = Math.min(red_percent, Math.min(green_percent, blue_percent)); + + return (cMax + cMin) / 2.0; + } +} diff --git a/test/zutil/ColorUtilTest.java b/test/zutil/ColorUtilTest.java new file mode 100644 index 0000000..e990fc5 --- /dev/null +++ b/test/zutil/ColorUtilTest.java @@ -0,0 +1,64 @@ +package zutil; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class ColorUtilTest { + + @Test + public void getHexString() { + assertEquals("#000000", ColorUtil.getHexString(0, 0, 0)); + assertEquals("#090909", ColorUtil.getHexString(9, 9, 9)); + assertEquals("#ffffff", ColorUtil.getHexString(255, 255, 255)); + assertEquals("#6496c8", ColorUtil.getHexString(100, 150, 200)); + } + + @Test + public void getRgbFromHexString() { + assertArrayEquals(new int[]{0, 0, 0}, ColorUtil.getRgbFromHexString("#000000")); + assertArrayEquals(new int[]{9, 9, 9}, ColorUtil.getRgbFromHexString("#090909")); + assertArrayEquals(new int[]{255, 255, 255}, ColorUtil.getRgbFromHexString("#ffffff")); + assertArrayEquals(new int[]{100, 150, 200}, ColorUtil.getRgbFromHexString("#6496c8")); + } + + @Test + public void getHueFromRGB() { + assertEquals(0.0, ColorUtil.getHue(0, 0, 0), 0); + assertEquals(0.0, ColorUtil.getHue(255, 255, 255), 0); + assertEquals(0.0, ColorUtil.getHue(255, 0, 0), 0); + assertEquals(120.0, ColorUtil.getHue(0, 255, 0), 0); + assertEquals(240.0, ColorUtil.getHue(0, 0, 255), 0); + assertEquals(0.0, ColorUtil.getHue(128, 128, 128), 0); + assertEquals(12.0, ColorUtil.getHue(100, 60, 50), 0); + assertEquals(108.0, ColorUtil.getHue(60, 100, 50), 0); + assertEquals(228.0, ColorUtil.getHue(50, 60, 100), 0); + } + + @Test + public void getSaturationFromRGB() { + assertEquals(0.0, ColorUtil.getSaturation(0, 0, 0), 0); + assertEquals(0.0, ColorUtil.getSaturation(255, 255, 255), 0); + assertEquals(1.0, ColorUtil.getSaturation(255, 0, 0), 0); + assertEquals(1.0, ColorUtil.getSaturation(0, 255, 0), 0); + assertEquals(1.0, ColorUtil.getSaturation(0, 0, 255), 0); + assertEquals(0.0, ColorUtil.getSaturation(128, 128, 128), 0); + assertEquals(0.333, ColorUtil.getSaturation(100, 60, 50), 0.001); + assertEquals(0.333, ColorUtil.getSaturation(60, 100, 50), 0.001); + assertEquals(0.333, ColorUtil.getSaturation(50, 60, 100), 0.001); + } + + @Test + public void getLightnessFromRGB() { + assertEquals(0.0, ColorUtil.getLightness(0, 0, 0), 0); + assertEquals(1.0, ColorUtil.getLightness(255, 255, 255), 0); + assertEquals(0.5, ColorUtil.getLightness(255, 0, 0), 0); + assertEquals(0.5, ColorUtil.getLightness(0, 255, 0), 0); + assertEquals(0.5, ColorUtil.getLightness(0, 0, 255), 0); + assertEquals(0.75, ColorUtil.getLightness(191, 191, 191), 0.01); + assertEquals(0.5, ColorUtil.getLightness(128, 128, 128), 0.01); + assertEquals(0.294, ColorUtil.getLightness(100, 60, 50), 0.01); + assertEquals(0.294, ColorUtil.getLightness(60, 100, 50), 0.01); + assertEquals(0.294, ColorUtil.getLightness(50, 60, 100), 0.01); + } +} \ No newline at end of file