diff --git a/pom.xml b/pom.xml index d36f4fb..d32eb8a 100644 --- a/pom.xml +++ b/pom.xml @@ -52,18 +52,6 @@ 1.0.1 - - org.openjfx - javafx-graphics - 11.0.2 - - - - org.openjfx - javafx-swing - 11.0.2 - - org.junit.jupiter diff --git a/src/main/java/dev/brachtendorf/clustering/ClusterResult.java b/src/main/java/dev/brachtendorf/clustering/ClusterResult.java index 8ff4cb9..c4dc8ef 100644 --- a/src/main/java/dev/brachtendorf/clustering/ClusterResult.java +++ b/src/main/java/dev/brachtendorf/clustering/ClusterResult.java @@ -1,6 +1,6 @@ package dev.brachtendorf.clustering; -import java.awt.Graphics; +import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; @@ -19,7 +19,6 @@ import dev.brachtendorf.clustering.distance.EuclideanDistance; import dev.brachtendorf.graphics.ColorUtil; import dev.brachtendorf.mutable.MutableDouble; -import javafx.scene.paint.Color; /** * @author Kilian @@ -45,11 +44,11 @@ public class ClusterResult { private HashMap silhouetteCoef = new HashMap<>(); //Cohesion ... - + //Radius ... diameter //density volume/points - - + + public ClusterResult(int[] clusterIndex, double[][] data) { int dimensions = data[0].length; @@ -163,7 +162,7 @@ public void printInformation() { StringBuilder sb = new StringBuilder(); sb.append("Observations: ").append(clusterIndex.length).append("\n").append("Number of Clusters: ") - .append(numberOfClusters).append("\n"); + .append(numberOfClusters).append("\n"); int clusterLength = StringUtil.charsNeeded(numberOfClusters); int obsLength = StringUtil.charsNeeded(clusterIndex.length); @@ -190,7 +189,7 @@ public void printInformation() { } silouetteCoeffificient += silhouetteCoef.get(i).getValue(); sb.append("] Silhouette Coef: ").append(df.format(silhouetteCoef.get(i).getValue())).append(" SSE:") - .append(sseDf.format(sse.get(i).doubleValue())).append("\n"); + .append(sseDf.format(sse.get(i).doubleValue())).append("\n"); } sb.append("SSE: " + df.format(sseSum)).append("\n"); @@ -221,7 +220,7 @@ public void toImage(File outputFile) { } } - javafx.scene.paint.Color[] c = ColorUtil.ColorPalette.getPaletteHue(numberOfClusters, Color.BLUE, Color.RED); + Color[] c = ColorUtil.ColorPalette.getPaletteHue(numberOfClusters, Color.BLUE, Color.RED); // Scale data g.fillRect(0, 0, 700, 700); @@ -259,11 +258,11 @@ public HashMap> getClusters() { public List getCluster(int cluster) { return clusters.get(cluster); } - + public DoubleSummaryStatistics[] getStats(int cluster) { return stats.get(cluster); } - + public int[] getClusterData() { return clusterIndex; diff --git a/src/main/java/dev/brachtendorf/graphics/ColorUtil.java b/src/main/java/dev/brachtendorf/graphics/ColorUtil.java index 2224770..51dca60 100644 --- a/src/main/java/dev/brachtendorf/graphics/ColorUtil.java +++ b/src/main/java/dev/brachtendorf/graphics/ColorUtil.java @@ -1,7 +1,8 @@ package dev.brachtendorf.graphics; import dev.brachtendorf.MathUtil; -import javafx.scene.paint.Color; + +import java.awt.*; /** * @author Kilian @@ -36,10 +37,7 @@ public class ColorUtil { * @since 1.0.0 com.github.kilianB */ public static java.awt.Color fxToAwtColor(Color fxColor){ - return new java.awt.Color((float)fxColor.getRed(), - (float)fxColor.getGreen(), - (float)fxColor.getBlue(), - (float)fxColor.getOpacity()); + return fxColor; } //@formatter:on @@ -51,29 +49,26 @@ public static java.awt.Color fxToAwtColor(Color fxColor){ * @since 1.0.0 com.github.kilianB */ public static Color awtToFxColor(java.awt.Color awtColor) { - return new Color(awtColor.getRed()/255d, - awtColor.getGreen()/255d, - awtColor.getBlue()/255d, - awtColor.getAlpha()/255d); + return awtColor; } //@formatter:on /** * Convert an argb value to it's individual components in range of 0 - 255 - * + * * @param argb values as int * @return [0] Alpha, [1] Red, [2] Green, [3] Blue * @since 1.0.0 com.github.kilianB - * + * */ public static int[] argbToComponents(int argb) { - return new int[] { argb >> 24 & 0xFF, argb >> 16 & 0xFF, argb >> 8 & 0xFF, argb & 0xFF }; + return new int[]{argb >> 24 & 0xFF, argb >> 16 & 0xFF, argb >> 8 & 0xFF, argb & 0xFF}; } /** * Converts the components to a single int argb representation. The individual * values are not range checked - * + * * @param alpha in range of 0 - 255 * @param red in range of 0 - 255 * @param green in range of 0 - 255 @@ -88,63 +83,38 @@ public static int componentsToARGB(int alpha, int red, int green, int blue) { /** * Convert an argb value (alpha 24,red 16, green 8, blue 0) into a java fx * color. - * + * * @param argb the argb color as an int * @return The JavaFX Color * @since 1.0.0 com.github.kilianB */ - public static javafx.scene.paint.Color argbToFXColor(int argb) { + public static Color argbToFXColor(int argb) { int[] components = argbToComponents(argb); - return new javafx.scene.paint.Color(components[1] / 255d, components[2] / 255d, components[3] / 255d, - components[0] / 255d); + return new Color(components[1], components[2], components[3], components[0]); } /** * Return the hexcode of a color - * + * * @param color the color to convert * @return a hex representation of the color * @since 1.0.0 com.github.kilianB */ public static String fxToHex(Color color) { return String.format("#%02X%02X%02X", (int) (color.getRed() * 255), (int) (color.getGreen() * 255), - (int) (color.getBlue() * 255)); + (int) (color.getBlue() * 255)); } // https://stackoverflow.com/a/2103608/3244464 // https://www.compuphase.com/cmetric.htm - /** - * Compute a distance metric of 2 colors. The distance of a color is greater the - * further away two colors are. - *

- * - * Identical colors will return a distance of 0. - * - * @param c1 The first color - * @param c2 The second color - * @return a double value indicating the distance of two colors - * @since 1.0.0 com.github.kilianB - */ - public static double distance(Color c1, Color c2) { - double rmean = (c1.getRed() * 255 + c2.getRed() * 255) / 2; - int r = (int) (c1.getRed() * 255 - c2.getRed() * 255); - int g = (int) (c1.getGreen() * 255 - c2.getGreen() * 255); - int b = (int) (c1.getBlue() * 255 - c2.getBlue() * 255); - double weightR = 2 + rmean / 256; - double weightG = 4.0; - double weightB = 2 + (255 - rmean) / 256; - return Math.sqrt(weightR * r * r + weightG * g * g + weightB * b * b); - } - // https://stackoverflow.com/a/2103608/3244464 - // https://www.compuphase.com/cmetric.htm /** * Compute a distance metric of 2 colors. The distance of a color is greater the * further away two colors are. *

- * + * * Identical colors will return a distance of 0. - * + * * @param c1 The first color * @param c2 The second color * @return a double value indicating the distance of two colors @@ -163,18 +133,7 @@ public static double distance(java.awt.Color c1, java.awt.Color c2) { /** * Get the Y (luma component) of the YCrCb color model - * - * @param c an javaFX color - * @return the luma component in the tange [0-1] - * @since 1.3.2 com.github.kilianB - */ - public static double getLuma(Color c) { - return LUMA_RED * c.getRed() + LUMA_GREEN * c.getGreen() + LUMA_BLUE * c.getBlue(); - } - - /** - * Get the Y (luma component) of the YCrCb color model - * + * * @param c an awt color * @return the luma component in the tange [0-255] * @since 1.3.2 com.github.kilianB @@ -188,27 +147,7 @@ public static int getLuma(java.awt.Color c) { * Return either white or black depending on the supplied color to guarantee * readability. The contrast color is assumed to be used as text overlay on top * of the input color. - * - * @param input the color of the background - * @return the color (white or black) of the foreground whichever guarantees - * more readability. - * @since 1.0.0 com.github.kilianB - */ - public static Color getContrastColor(Color input) { - // Luminascense - double y = getLuma(input); - if (y > 0.55) { - return Color.BLACK; - } else { - return Color.WHITE; - } - } - - /** - * Return either white or black depending on the supplied color to guarantee - * readability. The contrast color is assumed to be used as text overlay on top - * of the input color. - * + * * @param input the color of the background * @return the color (white or black) of the foreground whichever guarantees * more readability. @@ -228,18 +167,18 @@ public static class ColorPalette { /** * Return a default palette from blue to orange using rgba interpolation - * + * * @param numColors the number of colors present in the returned array * @return A color array with * @since 1.0.0 com.github.kilianB */ public static Color[] getPalette(int numColors) { - return getPalette(numColors, Color.web("#003f5c"), Color.web("#ffa600")); + return getPalette(numColors, Color.decode("#003f5c"), Color.decode("#ffa600")); } /** * Create a color palette using rgba interpolation - * + * * @param numColors The number of colors present in the returned array * @param startColor The color of the first index * @param endColor The color of the last index @@ -251,27 +190,41 @@ public static Color[] getPalette(int numColors, Color startColor, Color endColor Color[] cols = new Color[numColors]; for (int i = 0; i < numColors; i++) { double factor = i / (double) numColors; - cols[i] = startColor.interpolate(endColor, factor); + cols[i] = interpolate(startColor, endColor, factor); } return cols; } + private static Color interpolate(Color startColor, Color endColor, double factor) { + if (factor < 0) return startColor; + if (factor > 1) return endColor; + return new Color( + interpolate(startColor.getRed(), endColor.getRed(), factor), + interpolate(startColor.getGreen(), endColor.getGreen(), factor), + interpolate(startColor.getBlue(), endColor.getBlue(), factor) + ); + } + + private static int interpolate(int start, int end, double factor) { + return (int) (start + (end - start) * factor); + } + /** * Create a color palette with the hue component being altered instead of the * individual rgb components. {@link #getPalette(int)}. - * + * * @param numColors The number of colors present in the returned array * @return An array containing the interpolated colors * @since 1.0.0 com.github.kilianB */ public static Color[] getPaletteHue(int numColors) { - return getPaletteHue(numColors, Color.web("#003f5c"), Color.web("#ffa600")); + return getPaletteHue(numColors, Color.decode("#003f5c"), Color.decode("#ffa600")); } /** * Create a color palette with the hue component being altered instead of the * individual rgb components. {@link #getPalette(int)}. - * + * * @param numColors The number of colors present in the returned array * @param startColor The color of the first index * @param endColor The color of the last index @@ -280,15 +233,17 @@ public static Color[] getPaletteHue(int numColors) { */ public static Color[] getPaletteHue(int numColors, Color startColor, Color endColor) { - double hDelta = (endColor.getHue() - startColor.getHue()) / numColors; - double sDelta = (endColor.getSaturation() - startColor.getSaturation()) / numColors; - double bDelta = (endColor.getBrightness() - startColor.getBrightness()) / numColors; + float[] startHsb = Color.RGBtoHSB(startColor.getRed(), startColor.getGreen(), startColor.getBlue(), null); + float[] endHsb = new float[3]; + double hDelta = (endHsb[0] - startHsb[0]) / numColors; + double sDelta = (endHsb[1] - startHsb[1]) / numColors; + double bDelta = (endHsb[2] - startHsb[2]) / numColors; Color[] cols = new Color[numColors]; for (int i = 0; i < numColors; i++) { - double newSat = startColor.getSaturation() + sDelta * i; - double newBrightness = startColor.getBrightness() + bDelta * i; + double newSat = startHsb[1] + sDelta * i; + double newBrightness = startHsb[2] + bDelta * i; // Wrap around if (newSat > 1) { @@ -302,8 +257,7 @@ public static Color[] getPaletteHue(int numColors, Color startColor, Color endCo } else if (newBrightness < 0) { newBrightness = 1 - newBrightness; } - - cols[i] = Color.hsb(startColor.getHue() + hDelta * i, newSat, newBrightness); + cols[i] = new Color(Color.HSBtoRGB((float) (startHsb[0] + hDelta * i), (float) newSat, (float) newBrightness)); } return cols; } diff --git a/src/main/java/dev/brachtendorf/graphics/FastPixel.java b/src/main/java/dev/brachtendorf/graphics/FastPixel.java index c19ca37..942a492 100644 --- a/src/main/java/dev/brachtendorf/graphics/FastPixel.java +++ b/src/main/java/dev/brachtendorf/graphics/FastPixel.java @@ -1,17 +1,16 @@ package dev.brachtendorf.graphics; +import java.awt.*; import java.awt.image.BufferedImage; import java.util.logging.Logger; -import javafx.scene.paint.Color; - /** * Utility class to access pixel data in a fraction of the time required by the * native JDK methods. - * + * *

* Additionally support different color spaces and bulk operations - * + * * @author Kilian * */ @@ -21,7 +20,7 @@ public interface FastPixel { /** * Return a fast pixel instance mapped to the buffered image type - * + * * @param bufferedImage the buffered image to create a fast pixel instance for * @return an instantiated FastPixelObject */ @@ -38,7 +37,7 @@ public static FastPixel create(BufferedImage bufferedImage) { return new FastPixelInt(bufferedImage); default: LOGGER.finest("No fast implementation available for " + bufferedImage.getType() - + ". Fallback to slow default variant."); + + ". Fallback to slow default variant."); return new FastPixelSlowDefault(bufferedImage); // throw new UnsupportedOperationException( // "The image type is currently not supported: " + bufferedImage.getType()); @@ -52,7 +51,7 @@ public static FastPixel create(BufferedImage bufferedImage) { * call {@link #setReplaceOpaqueColors(int, int, int, int, int)} and set a color * which will be returned in case that the pixel has an alpha value smaller than * the specified threshold - * + * * @since 1.0.0 * @return true if opaque colors are replaced by a user defined color */ @@ -63,7 +62,7 @@ public static FastPixel create(BufferedImage bufferedImage) { * specified threshold. The pixel value is replaced for all get operations, * including luminosity calculation. Set operations are not touched by this * setting. - * + * * @param alphaThreshold replace each pixel which has an alpha value smaller or * equal to the threshold in the range of [0-255]. A value * of will disabled color replacement @@ -84,7 +83,7 @@ public static FastPixel create(BufferedImage bufferedImage) { * specified threshold. The pixel value is replaced for all get operations, * including luminosity calculation. Set operations are not touched by this * setting. - * + * * @param alphaThreshold replace each pixel which has an alpha value smaller * or equal to the threshold in the range of [0-255]. A * value of will disabled color replacement @@ -92,23 +91,7 @@ public static FastPixel create(BufferedImage bufferedImage) { */ default void setReplaceOpaqueColors(int alphaThreshold, java.awt.Color replacementColor) { setReplaceOpaqueColors(alphaThreshold, replacementColor.getRed(), replacementColor.getGreen(), - replacementColor.getBlue(), replacementColor.getAlpha()); - } - - /** - * Replace all pixels values in the image which have an alpha < than the - * specified threshold. The pixel value is replaced for all get operations, - * including luminosity calculation. Set operations are not touched by this - * setting. - * - * @param alphaThreshold replace each pixel which has an alpha value smaller - * or equal to the threshold in the range of [0-255]. A - * value of will disabled color replacement - * @param replacementColor the color which will be returned in case of an opaque - * pixel - */ - default void setReplaceOpaqueColors(int alphaThreshold, Color replacementColor) { - setReplaceOpaqueColors(alphaThreshold, ColorUtil.fxToAwtColor(replacementColor)); + replacementColor.getBlue(), replacementColor.getAlpha()); } int getRGB(int index); @@ -118,7 +101,7 @@ default void setReplaceOpaqueColors(int alphaThreshold, Color replacementColor) * are only 8-bits of precision for each color component in the returned data * when using this method. An ArrayOutOfBoundsException may be thrown if the * coordinates are not in bounds. - * + * * @param x the X coordinate of the pixel from which to get the pixel in the * default RGB color model * @param y the Y coordinate of the pixel from which to get the pixel in the @@ -136,7 +119,7 @@ default int getRGB(int x, int y) { * color model(TYPE_INT_ARGB). There are only 8-bits of precision for each color * component in the returned data when using this method. An * ArrayOutOfBoundsException may be thrown if the coordinates are not in bounds. - * + * * @return a 2d integer array containing the argb values of the image * @since 1.3.0 com.github.kilianB */ @@ -144,7 +127,7 @@ default int getRGB(int x, int y) { /** * Get the alpha value of the specified pixel - * + * * @param index the offset in the underlying array * @return the alpha value in range [0-255] or -1 if alpha is not supported * @since 1.5.0 com.github.kilianB @@ -153,7 +136,7 @@ default int getRGB(int x, int y) { /** * Get the alpha value of the specified pixel - * + * * @param x The x coordinate of the images' pixel * @param y The y coordinate of the images' pixel * @return the alpha value in range [0-255] or -1 if alpha is not supported @@ -166,7 +149,7 @@ default int getAlpha(int x, int y) { /** * Get the alpha component of the entire image mapped to a 2d array representing * the x and y coordinates of the pixel. - * + * * @return the alpha values or null if alpha is not supported * @since 1.3.0 com.github.kilianB */ @@ -175,7 +158,7 @@ default int getAlpha(int x, int y) { /** * Set the alpha value of the specified pixel. This method is a NOP if alpha is * not supported. - * + * * @param x The x coordinate of the images' pixel * @param y The y coordinate of the images' pixel * @param newAlpha the new alpha value in range [0-255] @@ -187,7 +170,7 @@ default void setAlpha(int x, int y, int newAlpha) { /** * Set the alpha value. This method is a NOP if alpha is not supported. - * + * * @param index the offset of the underlying array * @param newAlpha the new alpha value in range [0-255] * @since 1.5.0 com.github.kilianB @@ -196,7 +179,7 @@ default void setAlpha(int x, int y, int newAlpha) { /** * Set new alpha values for the entire picture - * + * * @param newAlpha red values in range [0-255] * @since 1.4.5 com.github.kilianB */ @@ -204,7 +187,7 @@ default void setAlpha(int x, int y, int newAlpha) { /** * Get the red value at the specified offset - * + * * @param index offset of ther underlying array * @return the red value in range [0-255] * @since 1.5.0 com.github.kilianB @@ -213,7 +196,7 @@ default void setAlpha(int x, int y, int newAlpha) { /** * Get the red value of the specified pixel - * + * * @param x The x coordinate of the images' pixel * @param y The y coordinate of the images' pixel * @return the red value in range [0-255] @@ -226,7 +209,7 @@ default int getRed(int x, int y) { /** * Get the red component of the entire image mapped to a 2d array representing * the x and y coordinates of the pixel. - * + * * @return the red values in range [0-255] * @since 1.3.0 com.github.kilianB */ @@ -234,7 +217,7 @@ default int getRed(int x, int y) { /** * Get the red component of the entire image mapped to a 1d array - * + * * @return the red values in range [0-255] * @since 1.5.5 com.github.kilianB */ @@ -242,7 +225,7 @@ default int getRed(int x, int y) { /** * Set the red value of the specified pixel - * + * * @param x The x coordinate of the images' pixel * @param y The y coordinate of the images' pixel * @param newRed the new red value in range [0-255] @@ -254,7 +237,7 @@ default void setRed(int x, int y, int newRed) { /** * Set the red value at the specified offset - * + * * @param index the offset of the underlying array * @param newRed the new red value in range [0-255] * @since 1.5.0 com.github.kilianB @@ -263,7 +246,7 @@ default void setRed(int x, int y, int newRed) { /** * Set new red values for the entire picture - * + * * @param newRed red values in range [0-255] * @since 1.4.5 com.github.kilianB */ @@ -271,7 +254,7 @@ default void setRed(int x, int y, int newRed) { /** * Get the green value of the specified offset - * + * * @param index the offset of the underlying array * @return the green value in range [0-255] * @since 1.5.0 com.github.kilianB @@ -280,7 +263,7 @@ default void setRed(int x, int y, int newRed) { /** * Get the green value of the specified pixel - * + * * @param x The x coordinate of the images' pixel * @param y The y coordinate of the images' pixel * @return the green value in range [0-255] @@ -292,7 +275,7 @@ default int getGreen(int x, int y) { /** * Set the green value of the specified pixel - * + * * @param x The x coordinate of the images' pixel * @param y The y coordinate of the images' pixel * @param newGreen the new green value in range [0-255] @@ -304,7 +287,7 @@ default void setGreen(int x, int y, int newGreen) { /** * Set the green value at the specified offset - * + * * @param index the offset of the underlying array * @param newGreen the new green value in range [0-255] * @since 1.5.0 com.github.kilianB @@ -313,7 +296,7 @@ default void setGreen(int x, int y, int newGreen) { /** * Set new green values for the entire picture - * + * * @param newGreen red values in range [0-255] * @since 1.4.5 com.github.kilianB */ @@ -322,7 +305,7 @@ default void setGreen(int x, int y, int newGreen) { /** * Get the green component of the entire image mapped to a 2d array representing * the x and y coordinates of the pixel. - * + * * @return the green values in range [0-255] * @since 1.3.0 com.github.kilianB */ @@ -330,7 +313,7 @@ default void setGreen(int x, int y, int newGreen) { /** * Get the green component of the entire image mapped to a 1d array - * + * * @return the green values in range [0-255] * @since 1.5.5 com.github.kilianB */ @@ -338,7 +321,7 @@ default void setGreen(int x, int y, int newGreen) { /** * Get the blue value of the specified offset - * + * * @param index the offset of the underlying array * @return the green value in range [0-255] * @since 1.5.0 com.github.kilianB @@ -347,7 +330,7 @@ default void setGreen(int x, int y, int newGreen) { /** * Get the blue value of the specified pixel - * + * * @param x The x coordinate of the images' pixel * @param y The y coordinate of the images' pixel * @return the blue value in range [0-255] @@ -359,7 +342,7 @@ default int getBlue(int x, int y) { /** * Set the blue value of the specified pixel - * + * * @param x The x coordinate of the images' pixel * @param y The y coordinate of the images' pixel * @param newBlue the new blue value in range [0-255] @@ -374,7 +357,7 @@ default void setBlue(int x, int y, int newBlue) { /** * Get the blue component of the entire image mapped to a 2d array representing * the x and y coordinates of the image. - * + * * @return the blue values in range [0-255] * @since 1.3.0 com.github.kilianB */ @@ -382,7 +365,7 @@ default void setBlue(int x, int y, int newBlue) { /** * Get the blue component of the entire image mapped to a 1d array - * + * * @return the red values in range [0-255] * @since 1.5.5 com.github.kilianB */ @@ -390,7 +373,7 @@ default void setBlue(int x, int y, int newBlue) { /** * Set new blue values for the entire picture - * + * * @param newBlue red values in range [0-255] * @since 1.4.5 com.github.kilianB */ @@ -400,12 +383,12 @@ default void setBlue(int x, int y, int newBlue) { /** * Get the average grayscale at the specified offset - * + * *

* Average grayscale: (R+G+B)/3 *

* This - * + * * @param index offset of der underlying array * @return the grayscale values in range [0-255] * @since 1.5.0 com.github.kilianB @@ -417,10 +400,10 @@ default void setBlue(int x, int y, int newBlue) { /** * Get the average grayscale of the specified pixel - * + * *

* Average grayscale: (R+G+B)/3 - * + * * @param x The x coordinate of the images' pixel * @param y The y coordinate of the images' pixel * @return the grayscale values in range [0-255] @@ -433,13 +416,13 @@ default int getAverageGrayscale(int x, int y) { /** * Get the average grayscale of the entire image mapped to a 2d array * representing the x and y coordinates of the pixel. - * + * *

* Average grayscale: (R+G+B)/3 - * + * *

* Average grayscale: (R+G+B)/3 - * + * * @return the grayscale values in range [0 - 255] * @since 1.5.0 com.github.kilianB */ @@ -447,15 +430,15 @@ default int getAverageGrayscale(int x, int y) { /** * Set the gray values at the specified offset - * + * *

* Average grayscale: (R+G+B)/3 - * + * *

* It is up to the inheriting class to decide how the gray value is reflected at * the value level. If the image is still in rgb or argb mode the value of each * individual channel will be set to the gray value - * + * * @param index offset of der underlaying array * @param newGrayValue to set the pixels to range [0 - 255] * @since 1.5.0 com.github.kilianB @@ -468,15 +451,15 @@ default void setAverageGrayscale(int index, int newGrayValue) { /** * Set the gray values of the specified pixel image. - * + * *

* Average grayscale: (R+G+B)/3 - * + * *

* It is up to the inheriting class to decide how the gray value is reflected at * the value level. If the image is still in rgb or argb mode the value of each * individual channel will be set to the gray value - * + * * @param x The x coordinate of the images' pixel * @param y The y coordinate of the images' pixel * @param newGrayValue to set the pixels to range [0 - 255] @@ -488,39 +471,40 @@ default void setAverageGrayscale(int x, int y, int newGrayValue) { /** * Set the gray values of the entire image. - * + * *

* Average grayscale: (R+G+B)/3 - * + * *

* It is up to the inheriting class to decide how the gray value is reflected at * the value level. If the image is still in rgb or argb mode the value of each * individual channel will be set to the gray value - * + * * @param newGrayValue to set the pixels to range [0 - 255] * @since 1.5.0 com.github.kilianB */ void setAverageGrayscale(int[][] newGrayValue); // YCrCb + /** * Return the Y(Luma) component of the YCbCr color model for the specified * offset. - * + * * @param index of the underlying array * @return the luma component in range [0-255] * @since 1.3.0 com.github.kilianB */ default int getLuma(int index) { int lum = (int) ((getRed(index)) * ColorUtil.LUMA_RED + (getGreen(index)) * ColorUtil.LUMA_GREEN - + (getBlue(index)) * ColorUtil.LUMA_BLUE); + + (getBlue(index)) * ColorUtil.LUMA_BLUE); return lum > 255 ? 255 : lum; } /** * Return the Y(Luma) component of the YCbCr color model for the specified * pixel. - * + * * @param x the x coordinate of the image * @param y the y coordinate of the image * @return the luma component in range [0-255] @@ -533,7 +517,7 @@ default int getLuma(int x, int y) { /** * Return the Y(Luma) component of the YCbCr color model for the entire image * mapped to a 2d array representing the x and y coordinates of the pixel. - * + * * @return the luma component in range [0-255] * @since 1.3.1 com.github.kilianB */ @@ -542,21 +526,21 @@ default int getLuma(int x, int y) { /** * Return the Y(Luma) component of the YCbCr color model fof the entire image * mapped to a 1d array - * + * * @return the luma component in range [0-255] */ int[] getLuma1D(); default int getCr(int index) { int cr = (int) (getRed(index) * ColorUtil.CR_RED + getGreen(index) * ColorUtil.CR_GREEN - + getBlue(index) * ColorUtil.CR_BLUE); + + getBlue(index) * ColorUtil.CR_BLUE); return cr > 255 ? 255 : cr; } /** * Return the Cr(red-difference) component of the YCbCr color model for the * specified pixel. - * + * * @param x the x coordinate of the image * @param y the y coordinate of the image * @return the cr component in range [0-255] @@ -569,21 +553,21 @@ default int getCr(int x, int y) { /** * Return the Cb(blue-difference) component of the YCbCr color model for the * specified offset. - * + * * @param index offset of the underlying array * @return the cb component in range [0-255] * @since 1.5.0 com.github.kilianB */ default int getCb(int index) { int cb = (int) (getRed(index) * ColorUtil.CB_RED + getGreen(index) * ColorUtil.CB_GREEN - + getBlue(index) * ColorUtil.CB_BLUE); + + getBlue(index) * ColorUtil.CB_BLUE); return cb > 255 ? 255 : cb; } /** * Return the Cb(blue-difference) component of the YCbCr color model for the * specified pixel. - * + * * @param x the x coordinate of the image * @param y the y coordinate of the image * @return the cb component in range [0-255] @@ -596,7 +580,7 @@ default int getCb(int x, int y) { /** * Return the hue component (angle) of the HSV color model for the specified * offset - * + * * @param index offset of der underlying array * @return the hue component in range [0-360]. As defined the hue is 0 for * undefined colors (e.g. white or black) @@ -636,7 +620,7 @@ default int getHue(int index) { /** * Return the hue component (angle) of the HSV color model for the specified * pixel - * + * * @param x the x coordinate of the image * @param y the y coordinate of the image * @return the hue component in range [0-360]. As defined the hue is 0 for @@ -650,12 +634,12 @@ default int getHue(int x, int y) { /** * Return the saturation component of the HSV color model for the specified * offset - * + * *

* Note: Opposed to all other values for the hsb model saturation is returned as * double in the range of [0-1] instead of [0-255] to allow for a higher * accuracy. - * + * * @param index the offset of the underlying array * @return the sat component in range [0-1]. As defined the sat is 0 for * undefined colors (i.e. black) @@ -676,12 +660,12 @@ default double getSat(int index) { /** * Return the saturation component of the HSV color model for the specified * pixel - * + * *

* Note: Opposed to all other values for the hsb model saturation is returned as * double in the range of [0-1] instead of [0-255] to allow for a higher * accuracy. - * + * * @param x the x coordinate of the image * @param y the y coordinate of the image * @return the sat component in range [0-1]. As defined the sat is 0 for @@ -694,7 +678,7 @@ default double getSat(int x, int y) { /** * Return the value component of the HSV color model for the specified offset - * + * * @param index offset of the udnerlying array * @return the value component in range [0-255]. * @since 1.5.0 com.github.kilianB @@ -709,7 +693,7 @@ default int getVal(int index) { /** * Return the value component of the HSV color model for the specified pixel - * + * * @param x the x coordinate of the image * @param y the y coordinate of the image * @return the value component in range [0-255]. @@ -720,14 +704,14 @@ default int getVal(int x, int y) { /** * Check if an image supports alpha values - * + * * @return true if the image has an alpha channel. false otherwise */ boolean hasAlpha(); /** * Map the x and y values to the underlying one dimensional data array - * + * * @param x the x coordinate * @param y the y coordinate * @return the corresponding 1d array index diff --git a/src/main/java/dev/brachtendorf/graphics/ImageUtil.java b/src/main/java/dev/brachtendorf/graphics/ImageUtil.java index 385df55..b4ad59b 100644 --- a/src/main/java/dev/brachtendorf/graphics/ImageUtil.java +++ b/src/main/java/dev/brachtendorf/graphics/ImageUtil.java @@ -1,8 +1,6 @@ package dev.brachtendorf.graphics; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.RenderingHints; +import java.awt.*; import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.DirectColorModel; @@ -11,12 +9,6 @@ import javax.imageio.ImageTypeSpecifier; -import javafx.embed.swing.SwingFXUtils; -import javafx.scene.image.Image; -import javafx.scene.image.PixelFormat; -import javafx.scene.image.PixelReader; -import javafx.scene.paint.Color; - /** * @author Kilian * @@ -25,12 +17,12 @@ public class ImageUtil { /** * Resize the buffered image to an arbitrary dimension - * + * *

* If a high quality interpolated thumbnail is required * {@link #createThumbnail(BufferedImage, int, int)} may be more suited. - * - * + * + * * @param source the source image * @param width the new width * @param height the new height @@ -54,9 +46,9 @@ public static BufferedImage getScaledInstance(BufferedImage source, int width, i *

* Returns a thumbnail of a source image. *

- * + * * TODO LGPL LICENSE!! - * + * * @param image the source image * @param newWidth the width of the thumbnail * @param newHeight the height of the thumbnail @@ -77,7 +69,7 @@ public static BufferedImage createThumbnail(BufferedImage image, int newWidth, i if (newWidth >= width || newHeight >= height) { throw new IllegalArgumentException( - "newWidth and newHeight cannot" + " be greater than the image" + " dimensions"); + "newWidth and newHeight cannot" + " be greater than the image" + " dimensions"); } else if (newWidth <= 0 || newHeight <= 0) { throw new IllegalArgumentException("newWidth and newHeight must" + " be greater than 0"); } @@ -149,7 +141,7 @@ public static BufferedImage createThumbnail(BufferedImage image, int newWidth, i /** * Convert the image to an image of the new type - * + * * @param original the original image to convert * @param newType the new type * @return a copy of the original image with new pixel type @@ -164,7 +156,7 @@ public static BufferedImage toNewType(BufferedImage original, int newType) { /** * Convert the image to an image of the new type - * + * * @param original the original image to convert * @param newType the new type * @return a copy of the original image with new pixel type @@ -175,19 +167,7 @@ public static BufferedImage toNewType(BufferedImage original, BImageType newType /** * Calculate the interpolated average color of the image - * - * @param image the source image - * @return the average color of the image - * @since 1.0.0 com.github.kilianB - */ - public static Color interpolateColor(Image image) { - BufferedImage bImage = SwingFXUtils.fromFXImage(image, null); - return interpolateColor(bImage); - } - - /** - * Calculate the interpolated average color of the image - * + * * @param bImage the source image * @return the average color of the image * @since 2.0.1 @@ -203,35 +183,16 @@ public static Color interpolateColor(BufferedImage bImage) { * most often occurred in this image. Be aware that calculating the average * color using this approach is a rather expensive operation. *

- * - * @param bImage The source image - * @return the dominant color of this image - * @since 2.0.1 - */ - public static Color dominantColor(BufferedImage bImage) { - return dominantColor(SwingFXUtils.toFXImage(bImage, null)); - } - - /** - * Return the dominant color of this image. The dominant color is the color that - * most often occurred in this image. Be aware that calculating the average - * color using this approach is a rather expensive operation. - *

- * + * * @param image The source image * @return the dominant color of this image - * @since 1.0.0 com.github.kilianB + * @since 2.0.1 */ - public static Color dominantColor(Image image) { + public static Color dominantColor(BufferedImage image) { HashMap colorCount = new HashMap<>(); - PixelReader pr = image.getPixelReader(); - - int[] pixels = new int[(int) (image.getWidth() * image.getHeight())]; - - pr.getPixels(0, 0, (int) image.getWidth(), (int) image.getHeight(), PixelFormat.getIntArgbInstance(), pixels, 0, - (int) image.getWidth()); + int[] pixels = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth()); for (int argb : pixels) { // Do we even need to put it back in? Can't be simply change the Integer object? @@ -240,7 +201,7 @@ public static Color dominantColor(Image image) { } int argb = colorCount.entrySet().stream().max((entry, entry2) -> entry.getValue().compareTo(entry2.getValue())) - .get().getKey().intValue(); + .get().getKey().intValue(); return ColorUtil.argbToFXColor(argb); } @@ -249,31 +210,14 @@ public static Color dominantColor(Image image) { * Calculate the average color of this image. The average color is determined by * summing the squared argb component of each pixel and determining the mean * value of these. - * + * * @param image The source image * @return The average mean color of this image * @since 2.0.1 */ public static Color meanColor(BufferedImage image) { - return meanColor(SwingFXUtils.toFXImage(image, null)); - } - - /** - * Calculate the average color of this image. The average color is determined by - * summing the squared argb component of each pixel and determining the mean - * value of these. - * - * @param image The source image - * @return The average mean color of this image - * @since 1.0.0 com.github.kilianB - */ - public static Color meanColor(Image image) { - - PixelReader pr = image.getPixelReader(); - int[] pixels = new int[(int) (image.getWidth() * image.getHeight())]; - pr.getPixels(0, 0, (int) image.getWidth(), (int) image.getHeight(), PixelFormat.getIntArgbInstance(), pixels, 0, - (int) image.getWidth()); + int[] pixels = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth()); double meanAlpha = 0; double meanRed = 0; @@ -294,14 +238,14 @@ public static Color meanColor(Image image) { } int argbMean = ColorUtil.componentsToARGB((int) Math.sqrt(meanAlpha) * 255, (int) Math.sqrt(meanRed), - (int) Math.sqrt(meanGreen), (int) Math.sqrt(meanBlue)); + (int) Math.sqrt(meanGreen), (int) Math.sqrt(meanBlue)); return ColorUtil.argbToFXColor(argbMean); } /** * Buffered image types mapped to enum values for easier handling - * + * * @author Kilian * */ @@ -436,7 +380,7 @@ public enum BImageType { * Images with 8 bits per pixel should use the image types * TYPE_BYTE_INDEXED or TYPE_BYTE_GRAY depending on * their ColorModel. - * + * *

* When color data is stored in an image of this type, the closest color in the * colormap is determined by the IndexColorModel and the resulting diff --git a/src/test/java/dev/brachtendorf/ArrayUtilTest.java b/src/test/java/dev/brachtendorf/ArrayUtilTest.java index 0403a40..cf464d8 100644 --- a/src/test/java/dev/brachtendorf/ArrayUtilTest.java +++ b/src/test/java/dev/brachtendorf/ArrayUtilTest.java @@ -236,12 +236,12 @@ public void deepArrayCopyElementNested() { Object[] array = new Object[10]; for (int i = 0; i < 10; i++) { if (i % 2 == 0) { - array[i] = new javafx.geometry.Point2D(0, 0); + array[i] = new Point2D.Float(0, 0); } else { Object[] o = new Object[4]; array[i] = o; for (int j = 0; j < 4; j++) { - o[j] = new javafx.geometry.Point2D(0, 0); + o[j] = new Point2D.Float(0, 0); } } } diff --git a/src/test/java/dev/brachtendorf/graphics/ColorUtilTest.java b/src/test/java/dev/brachtendorf/graphics/ColorUtilTest.java index 956f867..a3395fe 100644 --- a/src/test/java/dev/brachtendorf/graphics/ColorUtilTest.java +++ b/src/test/java/dev/brachtendorf/graphics/ColorUtilTest.java @@ -7,140 +7,140 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import javafx.scene.paint.Color; +import java.awt.*; class ColorUtilTest { @Nested - class AwtToFX{ + class AwtToFX { @Test void black() { assertEquals(Color.BLACK, - ColorUtil.awtToFxColor(java.awt.Color.BLACK)); + ColorUtil.awtToFxColor(java.awt.Color.BLACK)); } - + @Test void white() { assertEquals(Color.WHITE, - ColorUtil.awtToFxColor(java.awt.Color.WHITE)); + ColorUtil.awtToFxColor(java.awt.Color.WHITE)); } - + @Test void gray() { assertEquals(Color.GRAY, - ColorUtil.awtToFxColor(java.awt.Color.GRAY)); + ColorUtil.awtToFxColor(java.awt.Color.GRAY)); } - + @Test void test() { - Color fxColor = Color.web("#961e1e"); - + Color fxColor = Color.decode("#961e1e"); + java.awt.Color awtColor = java.awt.Color.decode("#961e1e"); - + Color genfxColor = ColorUtil.awtToFxColor(awtColor); - assertEquals(fxColor,genfxColor); + assertEquals(fxColor, genfxColor); } - + @Test @Disabled void alpha() { //Color.web(colorString, opacity) // java.awt.Color.decode(nm) what is the awt equivalent? //DON'T compare float and doubles! - assertEquals(new Color(0.5d,0.5d,0.5d,0.3d), - ColorUtil.awtToFxColor(new java.awt.Color(0.5f,0.5f,0.5f,0.3f))); + assertEquals(new Color(0.5f, 0.5f, 0.5f, 0.3f), + ColorUtil.awtToFxColor(new java.awt.Color(0.5f, 0.5f, 0.5f, 0.3f))); } - + } - + @Nested - class FXToAwt{ + class FXToAwt { @Test void black() { assertEquals(java.awt.Color.BLACK, - ColorUtil.fxToAwtColor(Color.BLACK)); + ColorUtil.fxToAwtColor(Color.BLACK)); } - + @Test void white() { assertEquals(java.awt.Color.WHITE, - ColorUtil.fxToAwtColor(Color.WHITE)); + ColorUtil.fxToAwtColor(Color.WHITE)); } - + @Test void gray() { assertEquals(java.awt.Color.GRAY, - ColorUtil.fxToAwtColor(Color.GRAY)); + ColorUtil.fxToAwtColor(Color.GRAY)); } - + @Test void test() { - Color fxColor = Color.web("#961e1e"); - + Color fxColor = Color.decode("#961e1e"); + java.awt.Color awtColor = java.awt.Color.decode("#961e1e"); - + java.awt.Color genAwtColor = ColorUtil.fxToAwtColor(fxColor); - assertEquals(awtColor,genAwtColor); + assertEquals(awtColor, genAwtColor); } - + @Test @Disabled void alpha() { //Color.web(colorString, opacity) // java.awt.Color.decode(nm) what is the awt equivalent? - + //DON'T compare float and doubles! - assertEquals(new Color(0.5d,0.5d,0.5d,0.3d), - ColorUtil.awtToFxColor(new java.awt.Color(0.5f,0.5f,0.5f,0.3f))); + assertEquals(new Color(0.5f, 0.5f, 0.5f, 0.3f), + ColorUtil.awtToFxColor(new java.awt.Color(0.5f, 0.5f, 0.5f, 0.3f))); } } - + @Nested - class ColorDistance{ - + class ColorDistance { + @Test void symmetry() { - Color c1 = Color.GREENYELLOW; - Color c2 = Color.DARKORANGE; - assertEquals(ColorUtil.distance(c1, c2),ColorUtil.distance(c2, c1)); + Color c1 = Color.green; + Color c2 = Color.orange; + assertEquals(ColorUtil.distance(c1, c2), ColorUtil.distance(c2, c1)); } - + @Test void identity() { - Color c1 = Color.DARKORANGE; - Color c2 = Color.DARKORANGE; - assertEquals(0,ColorUtil.distance(c2, c1)); + Color c1 = Color.orange; + Color c2 = Color.orange; + assertEquals(0, ColorUtil.distance(c2, c1)); } - + @Test void distance() { Color c1 = Color.WHITE; - Color c2 = Color.DARKORANGE; + Color c2 = Color.black; Color c3 = Color.ORANGE; assertTrue(ColorUtil.distance(c1, c2) > ColorUtil.distance(c2, c3)); } } @Nested - class ContrastColor{ + class ContrastColor { @Test - void white(){ - assertEquals(Color.BLACK,ColorUtil.getContrastColor(Color.WHITE)); + void white() { + assertEquals(Color.BLACK, ColorUtil.getContrastColor(Color.WHITE)); } - + @Test - void black(){ - assertEquals(Color.WHITE,ColorUtil.getContrastColor(Color.BLACK)); + void black() { + assertEquals(Color.WHITE, ColorUtil.getContrastColor(Color.BLACK)); } - + @Test - void yellow(){ - assertEquals(Color.BLACK,ColorUtil.getContrastColor(Color.YELLOW)); + void yellow() { + assertEquals(Color.BLACK, ColorUtil.getContrastColor(Color.YELLOW)); } - + @Test - void blue(){ - assertEquals(Color.WHITE,ColorUtil.getContrastColor(Color.BLUE)); + void blue() { + assertEquals(Color.WHITE, ColorUtil.getContrastColor(Color.BLUE)); } } } \ No newline at end of file