diff --git a/CHANGELOG.md b/CHANGELOG.md index ebd9c027a..88771f6c5 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * 【core 】 IdcardUtil增加convert18To15方法(issue#I5QYCP@Gitee) * 【core 】 新增AnsiColors(改自Spring Boot)、AnsiColorWrapper,优化QrCodeUtil(pr#778@Gitee) * 【core 】 TemplateUtil的实现类增加getRawEngine方法(issues#2530@Github) +* 【core 】 ImgUtil中颜色相关方法剥离到ColorUtil中 ### 🐞Bug修复 * 【core 】 修复ObjectUtil.defaultIfXXX中NPE问题(pr#2603@Github) diff --git a/hutool-core/src/main/java/cn/hutool/core/img/ColorUtil.java b/hutool-core/src/main/java/cn/hutool/core/img/ColorUtil.java new file mode 100644 index 000000000..f67acc40f --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/img/ColorUtil.java @@ -0,0 +1,262 @@ +package cn.hutool.core.img; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +/** + * 颜色工具类 + * + * @since 5.8.7 + */ +public class ColorUtil { + + /** + * RGB颜色范围上限 + */ + private static final int RGB_COLOR_BOUND = 256; + + /** + * Color对象转16进制表示,例如#fcf6d6 + * + * @param color {@link Color} + * @return 16进制的颜色值,例如#fcf6d6 + * @since 4.1.14 + */ + public static String toHex(Color color) { + return toHex(color.getRed(), color.getGreen(), color.getBlue()); + } + + /** + * RGB颜色值转换成十六进制颜色码 + * + * @param r 红(R) + * @param g 绿(G) + * @param b 蓝(B) + * @return 返回字符串形式的 十六进制颜色码 如 + */ + public static String toHex(int r, int g, int b) { + // rgb 小于 255 + if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) { + throw new IllegalArgumentException("RGB must be 0~255!"); + } + return String.format("#%02X%02X%02X", r, g, b); + } + + /** + * 将颜色值转换成具体的颜色类型 汇集了常用的颜色集,支持以下几种形式: + * + *
+	 * 1. 颜色的英文名(大小写皆可)
+	 * 2. 16进制表示,例如:#fcf6d6或者$fcf6d6
+	 * 3. RGB形式,例如:13,148,252
+	 * 
+ *

+ * 方法来自:com.lnwazg.kit + * + * @param colorName 颜色的英文名,16进制表示或RGB表示 + * @return {@link Color} + * @since 4.1.14 + */ + public static Color getColor(String colorName) { + if (StrUtil.isBlank(colorName)) { + return null; + } + colorName = colorName.toUpperCase(); + + if ("BLACK".equals(colorName)) { + return Color.BLACK; + } else if ("WHITE".equals(colorName)) { + return Color.WHITE; + } else if ("LIGHTGRAY".equals(colorName) || "LIGHT_GRAY".equals(colorName)) { + return Color.LIGHT_GRAY; + } else if ("GRAY".equals(colorName)) { + return Color.GRAY; + } else if ("DARKGRAY".equals(colorName) || "DARK_GRAY".equals(colorName)) { + return Color.DARK_GRAY; + } else if ("RED".equals(colorName)) { + return Color.RED; + } else if ("PINK".equals(colorName)) { + return Color.PINK; + } else if ("ORANGE".equals(colorName)) { + return Color.ORANGE; + } else if ("YELLOW".equals(colorName)) { + return Color.YELLOW; + } else if ("GREEN".equals(colorName)) { + return Color.GREEN; + } else if ("MAGENTA".equals(colorName)) { + return Color.MAGENTA; + } else if ("CYAN".equals(colorName)) { + return Color.CYAN; + } else if ("BLUE".equals(colorName)) { + return Color.BLUE; + } else if ("DARKGOLD".equals(colorName)) { + // 暗金色 + return hexToColor("#9e7e67"); + } else if ("LIGHTGOLD".equals(colorName)) { + // 亮金色 + return hexToColor("#ac9c85"); + } else if (StrUtil.startWith(colorName, '#')) { + return hexToColor(colorName); + } else if (StrUtil.startWith(colorName, '$')) { + // 由于#在URL传输中无法传输,因此用$代替# + return hexToColor("#" + colorName.substring(1)); + } else { + // rgb值 + final List rgb = StrUtil.split(colorName, ','); + if (3 == rgb.size()) { + final Integer r = Convert.toInt(rgb.get(0)); + final Integer g = Convert.toInt(rgb.get(1)); + final Integer b = Convert.toInt(rgb.get(2)); + if (false == ArrayUtil.hasNull(r, g, b)) { + return new Color(r, g, b); + } + } else { + return null; + } + } + return null; + } + + /** + * 获取一个RGB值对应的颜色 + * + * @param rgb RGB值 + * @return {@link Color} + * @since 4.1.14 + */ + public static Color getColor(int rgb) { + return new Color(rgb); + } + + /** + * 16进制的颜色值转换为Color对象,例如#fcf6d6 + * + * @param hex 16进制的颜色值,例如#fcf6d6 + * @return {@link Color} + * @since 4.1.14 + */ + public static Color hexToColor(String hex) { + return getColor(Integer.parseInt(StrUtil.removePrefix(hex, "#"), 16)); + } + + /** + * 叠加颜色 + * @param color1 颜色1 + * @param color2 颜色2 + * @return 叠加后的颜色 + */ + public static Color add(Color color1, Color color2) { + double r1 = color1.getRed(); + double g1 = color1.getGreen(); + double b1 = color1.getBlue(); + double a1 = color1.getAlpha(); + double r2 = color2.getRed(); + double g2 = color2.getGreen(); + double b2 = color2.getBlue(); + double a2 = color2.getAlpha(); + int r = (int) ((r1 * a1 / 255 + r2 * a2 / 255) / (a1 / 255 + a2 / 255)); + int g = (int) ((g1 * a1 / 255 + g2 * a2 / 255) / (a1 / 255 + a2 / 255)); + int b = (int) ((b1 * a1 / 255 + b2 * a2 / 255) / (a1 / 255 + a2 / 255)); + return new Color(r, g, b); + } + + /** + * 生成随机颜色 + * + * @return 随机颜色 + * @since 3.1.2 + */ + public static Color randomColor() { + return randomColor(null); + } + + /** + * 生成随机颜色 + * + * @param random 随机对象 {@link Random} + * @return 随机颜色 + * @since 3.1.2 + */ + public static Color randomColor(Random random) { + if (null == random) { + random = RandomUtil.getRandom(); + } + return new Color(random.nextInt(RGB_COLOR_BOUND), random.nextInt(RGB_COLOR_BOUND), random.nextInt(RGB_COLOR_BOUND)); + } + + /** + * 获取给定图片的主色调,背景填充用 + * + * @param image {@link BufferedImage} + * @param rgbFilters 过滤多种颜色 + * @return {@link String} #ffffff + * @since 5.6.7 + */ + public static String getMainColor(BufferedImage image, int[]... rgbFilters) { + int r, g, b; + Map countMap = new HashMap<>(); + int width = image.getWidth(); + int height = image.getHeight(); + int minx = image.getMinX(); + int miny = image.getMinY(); + for (int i = minx; i < width; i++) { + for (int j = miny; j < height; j++) { + int pixel = image.getRGB(i, j); + r = (pixel & 0xff0000) >> 16; + g = (pixel & 0xff00) >> 8; + b = (pixel & 0xff); + if (matchFilters(r, g, b, rgbFilters)) { + continue; + } + countMap.merge(r + "-" + g + "-" + b, 1L, Long::sum); + } + } + String maxColor = null; + long maxCount = 0; + for (Map.Entry entry : countMap.entrySet()) { + String key = entry.getKey(); + Long count = entry.getValue(); + if (count > maxCount) { + maxColor = key; + maxCount = count; + } + } + final String[] splitRgbStr = StrUtil.splitToArray(maxColor, '-'); + String rHex = Integer.toHexString(Integer.parseInt(splitRgbStr[0])); + String gHex = Integer.toHexString(Integer.parseInt(splitRgbStr[1])); + String bHex = Integer.toHexString(Integer.parseInt(splitRgbStr[2])); + rHex = rHex.length() == 1 ? "0" + rHex : rHex; + gHex = gHex.length() == 1 ? "0" + gHex : gHex; + bHex = bHex.length() == 1 ? "0" + bHex : bHex; + return "#" + rHex + gHex + bHex; + } + + /** + * 给定RGB是否匹配过滤器中任何一个RGB颜色 + * + * @param r R + * @param g G + * @param b B + * @param rgbFilters 颜色过滤器 + * @return 是否匹配 + */ + private static boolean matchFilters(int r, int g, int b, int[]... rgbFilters) { + if (rgbFilters != null && rgbFilters.length > 0) { + for (int[] rgbFilter : rgbFilters) { + if (r == rgbFilter[0] && g == rgbFilter[1] && b == rgbFilter[2]) { + return true; + } + } + } + return false; + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java b/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java index 8c504c658..a62b12ba2 100755 --- a/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java @@ -1,16 +1,13 @@ package cn.hutool.core.img; import cn.hutool.core.codec.Base64; -import cn.hutool.core.convert.Convert; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.resource.Resource; import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.URLUtil; @@ -23,7 +20,18 @@ import javax.imageio.ImageWriter; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageOutputStream; import javax.swing.ImageIcon; -import java.awt.*; +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.Image; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Toolkit; import java.awt.color.ColorSpace; import java.awt.font.FontRenderContext; import java.awt.geom.AffineTransform; @@ -43,10 +51,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; -import java.util.HashMap; import java.util.Iterator; -import java.util.List; -import java.util.Map; import java.util.Random; /** @@ -65,12 +70,6 @@ public class ImgUtil { public static final String IMAGE_TYPE_PNG = "png";// 可移植网络图形 public static final String IMAGE_TYPE_PSD = "psd";// Photoshop的专用格式Photoshop - /** - * RGB颜色范围上限 - */ - private static final int RGB_COLOR_BOUND = 256; - - // ---------------------------------------------------------------------------------------------------------------------- scale /** @@ -1201,9 +1200,9 @@ public class ImgUtil { * 如果源图片的RGB模式与目标模式一致,则直接转换,否则重新绘制
* 默认的,png图片使用 {@link BufferedImage#TYPE_INT_ARGB}模式,其它使用 {@link BufferedImage#TYPE_INT_RGB} 模式 * - * @param image {@link Image} - * @param imageType 目标图片类型,例如jpg或png等 - * @param backgroundColor 背景色{@link Color} + * @param image {@link Image} + * @param imageType 目标图片类型,例如jpg或png等 + * @param backgroundColor 背景色{@link Color} * @return {@link BufferedImage} * @since 4.3.2 */ @@ -1241,9 +1240,9 @@ public class ImgUtil { * {@link Image} 转 {@link BufferedImage}
* 如果源图片的RGB模式与目标模式一致,则直接转换,否则重新绘制 * - * @param image {@link Image} - * @param imageType 目标图片类型,{@link BufferedImage}中的常量,例如黑白等 - * @param backgroundColor 背景色{@link Color} + * @param image {@link Image} + * @param imageType 目标图片类型,{@link BufferedImage}中的常量,例如黑白等 + * @param backgroundColor 背景色{@link Color} * @return {@link BufferedImage} * @since 5.4.7 */ @@ -1428,10 +1427,10 @@ public class ImgUtil { /** * 根据文字创建透明背景的PNG图片 * - * @param str 文字 - * @param font 字体{@link Font} - * @param fontColor 字体颜色,默认黑色 - * @param out 图片输出地 + * @param str 文字 + * @param font 字体{@link Font} + * @param fontColor 字体颜色,默认黑色 + * @param out 图片输出地 * @throws IORuntimeException IO异常 */ public static void createTransparentImage(String str, Font font, Color fontColor, ImageOutputStream out) throws IORuntimeException { @@ -1638,7 +1637,7 @@ public class ImgUtil { * @param imageType 图片类型(图片扩展名) * @param destImageStream 写出到的目标流 * @param quality 质量,数字为0~1(不包括0和1)表示质量压缩比,除此数字外设置表示不压缩 - * @param backgroundColor 背景色{@link Color} + * @param backgroundColor 背景色{@link Color} * @return 是否成功写出,如果返回false表示未找到合适的Writer * @throws IORuntimeException IO异常 * @since 4.3.2 @@ -1964,10 +1963,11 @@ public class ImgUtil { * * @param color {@link Color} * @return 16进制的颜色值,例如#fcf6d6 + * @see ColorUtil#toHex(Color) * @since 4.1.14 */ public static String toHex(Color color) { - return toHex(color.getRed(), color.getGreen(), color.getBlue()); + return ColorUtil.toHex(color); } /** @@ -1976,14 +1976,11 @@ public class ImgUtil { * @param r 红(R) * @param g 绿(G) * @param b 蓝(B) - * @return 返回字符串形式的 十六进制颜色码 如 + * @return 返回字符串形式的 十六进制颜色码 + * @see ColorUtil#toHex(int, int, int) */ public static String toHex(int r, int g, int b) { - // rgb 小于 255 - if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) { - throw new IllegalArgumentException("RGB must be 0~255!"); - } - return String.format("#%02X%02X%02X", r, g, b); + return ColorUtil.toHex(r, g, b); } /** @@ -1994,7 +1991,7 @@ public class ImgUtil { * @since 4.1.14 */ public static Color hexToColor(String hex) { - return getColor(Integer.parseInt(StrUtil.removePrefix(hex, "#"), 16)); + return ColorUtil.hexToColor(hex); } /** @@ -2002,10 +1999,11 @@ public class ImgUtil { * * @param rgb RGB值 * @return {@link Color} + * @see ColorUtil#getColor(int) * @since 4.1.14 */ public static Color getColor(int rgb) { - return new Color(rgb); + return ColorUtil.getColor(rgb); } /** @@ -2021,76 +2019,22 @@ public class ImgUtil { * * @param colorName 颜色的英文名,16进制表示或RGB表示 * @return {@link Color} + * @see ColorUtil#getColor(String) * @since 4.1.14 */ public static Color getColor(String colorName) { - if (StrUtil.isBlank(colorName)) { - return null; - } - colorName = colorName.toUpperCase(); - - if ("BLACK".equals(colorName)) { - return Color.BLACK; - } else if ("WHITE".equals(colorName)) { - return Color.WHITE; - } else if ("LIGHTGRAY".equals(colorName) || "LIGHT_GRAY".equals(colorName)) { - return Color.LIGHT_GRAY; - } else if ("GRAY".equals(colorName)) { - return Color.GRAY; - } else if ("DARKGRAY".equals(colorName) || "DARK_GRAY".equals(colorName)) { - return Color.DARK_GRAY; - } else if ("RED".equals(colorName)) { - return Color.RED; - } else if ("PINK".equals(colorName)) { - return Color.PINK; - } else if ("ORANGE".equals(colorName)) { - return Color.ORANGE; - } else if ("YELLOW".equals(colorName)) { - return Color.YELLOW; - } else if ("GREEN".equals(colorName)) { - return Color.GREEN; - } else if ("MAGENTA".equals(colorName)) { - return Color.MAGENTA; - } else if ("CYAN".equals(colorName)) { - return Color.CYAN; - } else if ("BLUE".equals(colorName)) { - return Color.BLUE; - } else if ("DARKGOLD".equals(colorName)) { - // 暗金色 - return hexToColor("#9e7e67"); - } else if ("LIGHTGOLD".equals(colorName)) { - // 亮金色 - return hexToColor("#ac9c85"); - } else if (StrUtil.startWith(colorName, '#')) { - return hexToColor(colorName); - } else if (StrUtil.startWith(colorName, '$')) { - // 由于#在URL传输中无法传输,因此用$代替# - return hexToColor("#" + colorName.substring(1)); - } else { - // rgb值 - final List rgb = StrUtil.split(colorName, ','); - if (3 == rgb.size()) { - final Integer r = Convert.toInt(rgb.get(0)); - final Integer g = Convert.toInt(rgb.get(1)); - final Integer b = Convert.toInt(rgb.get(2)); - if (false == ArrayUtil.hasNull(r, g, b)) { - return new Color(r, g, b); - } - } else { - return null; - } - } - return null; + return ColorUtil.getColor(colorName); } /** * 生成随机颜色 * * @return 随机颜色 + * @see ColorUtil#randomColor() * @since 3.1.2 */ public static Color randomColor() { - return randomColor(null); + return ColorUtil.randomColor(); } /** @@ -2098,13 +2042,11 @@ public class ImgUtil { * * @param random 随机对象 {@link Random} * @return 随机颜色 + * @see ColorUtil#randomColor(Random) * @since 3.1.2 */ public static Color randomColor(Random random) { - if (null == random) { - random = RandomUtil.getRandom(); - } - return new Color(random.nextInt(RGB_COLOR_BOUND), random.nextInt(RGB_COLOR_BOUND), random.nextInt(RGB_COLOR_BOUND)); + return ColorUtil.randomColor(random); } /** @@ -2129,66 +2071,12 @@ public class ImgUtil { * @param image {@link BufferedImage} * @param rgbFilters 过滤多种颜色 * @return {@link String} #ffffff + * @see ColorUtil#getMainColor(BufferedImage, int[]...) * @since 5.6.7 */ public static String getMainColor(BufferedImage image, int[]... rgbFilters) { - int r, g, b; - Map countMap = new HashMap<>(); - int width = image.getWidth(); - int height = image.getHeight(); - int minx = image.getMinX(); - int miny = image.getMinY(); - for (int i = minx; i < width; i++) { - for (int j = miny; j < height; j++) { - int pixel = image.getRGB(i, j); - r = (pixel & 0xff0000) >> 16; - g = (pixel & 0xff00) >> 8; - b = (pixel & 0xff); - if(matchFilters(r, g, b, rgbFilters)){ - continue; - } - countMap.merge(r + "-" + g + "-" + b, 1L, Long::sum); - } - } - String maxColor = null; - long maxCount = 0; - for (Map.Entry entry : countMap.entrySet()) { - String key = entry.getKey(); - Long count = entry.getValue(); - if (count > maxCount) { - maxColor = key; - maxCount = count; - } - } - final String[] splitRgbStr = StrUtil.splitToArray(maxColor, '-'); - String rHex = Integer.toHexString(Integer.parseInt(splitRgbStr[0])); - String gHex = Integer.toHexString(Integer.parseInt(splitRgbStr[1])); - String bHex = Integer.toHexString(Integer.parseInt(splitRgbStr[2])); - rHex = rHex.length() == 1 ? "0" + rHex : rHex; - gHex = gHex.length() == 1 ? "0" + gHex : gHex; - bHex = bHex.length() == 1 ? "0" + bHex : bHex; - return "#" + rHex + gHex + bHex; + return ColorUtil.getMainColor(image, rgbFilters); } - - /** - * 给定RGB是否匹配过滤器中任何一个RGB颜色 - * @param r R - * @param g G - * @param b B - * @param rgbFilters 颜色过滤器 - * @return 是否匹配 - */ - private static boolean matchFilters(int r, int g, int b, int[]... rgbFilters){ - if (rgbFilters != null && rgbFilters.length > 0) { - for (int[] rgbFilter : rgbFilters) { - if (r == rgbFilter[0] && g == rgbFilter[1] && b == rgbFilter[2]) { - return true; - } - } - } - return false; - } - // ------------------------------------------------------------------------------------------------------ 背景图换算 /** diff --git a/hutool-core/src/main/java/cn/hutool/core/img/LabColor.java b/hutool-core/src/main/java/cn/hutool/core/img/LabColor.java new file mode 100644 index 000000000..216f5a2f4 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/img/LabColor.java @@ -0,0 +1,90 @@ +package cn.hutool.core.img; + +import cn.hutool.core.lang.Assert; + +import java.awt.Color; +import java.awt.color.ColorSpace; + +/** + * 表示以 LAB 形式存储的颜色。
+ *

+ * + * @author Tom Xin + * @since 5.8.7 + */ +public class LabColor { + + private static final ColorSpace XYZ_COLOR_SPACE = ColorSpace.getInstance(ColorSpace.CS_CIEXYZ); + + /** + * L: 亮度 + */ + private final double l; + /** + * A: 正数代表红色,负端代表绿色 + */ + private final double a; + /** + * B: 正数代表黄色,负端代表蓝色 + */ + private final double b; + + public LabColor(Integer rgb) { + this((rgb != null) ? new Color(rgb) : null); + } + + public LabColor(Color color) { + Assert.notNull(color, "Color must not be null"); + final float[] lab = fromXyz(color.getColorComponents(XYZ_COLOR_SPACE, null)); + this.l = lab[0]; + this.a = lab[1]; + this.b = lab[2]; + } + + /** + * 获取颜色差 + * @param other 其他Lab颜色 + * @return 颜色差 + */ + // See https://en.wikipedia.org/wiki/Color_difference#CIE94 + public double getDistance(LabColor other) { + double c1 = Math.sqrt(this.a * this.a + this.b * this.b); + double deltaC = c1 - Math.sqrt(other.a * other.a + other.b * other.b); + double deltaA = this.a - other.a; + double deltaB = this.b - other.b; + double deltaH = Math.sqrt(Math.max(0.0, deltaA * deltaA + deltaB * deltaB - deltaC * deltaC)); + return Math.sqrt(Math.max(0.0, Math.pow((this.l - other.l), 2) + + Math.pow(deltaC / (1 + 0.045 * c1), 2) + Math.pow(deltaH / (1 + 0.015 * c1), 2.0))); + } + + private float[] fromXyz(float[] xyz) { + return fromXyz(xyz[0], xyz[1], xyz[2]); + } + + /** + * 从xyz换算
+ * L=116f(y)-16
+ * a=500[f(x/0.982)-f(y)]
+ * b=200[f(y)-f(z/1.183 )]
+ * 其中: f(x)=7.787x+0.138, x<0.008856; f(x)=(x)1/3,x>0.008856 + * + * @param x X + * @param y Y + * @param z Z + * @return Lab + */ + private static float[] fromXyz(float x, float y, float z) { + final double l = (f(y) - 16.0) * 116.0; + final double a = (f(x) - f(y)) * 500.0; + final double b = (f(y) - f(z)) * 200.0; + return new float[]{(float) l, (float) a, (float) b}; + } + + private static double f(double t) { + return (t > (216.0 / 24389.0)) ? Math.cbrt(t) : (1.0 / 3.0) * Math.pow(29.0 / 6.0, 2) * t + (4.0 / 29.0); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/ansi/AnsiColors.java b/hutool-core/src/main/java/cn/hutool/core/lang/ansi/AnsiColors.java index a6ab61543..aed812d9d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/ansi/AnsiColors.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/ansi/AnsiColors.java @@ -1,9 +1,8 @@ package cn.hutool.core.lang.ansi; -import cn.hutool.core.lang.Assert; +import cn.hutool.core.img.LabColor; import java.awt.Color; -import java.awt.color.ColorSpace; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @@ -117,59 +116,6 @@ public final class AnsiColors { return closest; } - /** - * 表示以 LAB 形式存储的颜色。 - */ - private static final class LabColor { - - private static final ColorSpace XYZ_COLOR_SPACE = ColorSpace.getInstance(ColorSpace.CS_CIEXYZ); - - private final double l; - - private final double a; - - private final double b; - - LabColor(Integer rgb) { - this((rgb != null) ? new Color(rgb) : null); - } - - LabColor(Color color) { - Assert.notNull(color, "Color must not be null"); - float[] lab = fromXyz(color.getColorComponents(XYZ_COLOR_SPACE, null)); - this.l = lab[0]; - this.a = lab[1]; - this.b = lab[2]; - } - - private float[] fromXyz(float[] xyz) { - return fromXyz(xyz[0], xyz[1], xyz[2]); - } - - private float[] fromXyz(float x, float y, float z) { - double l = (f(y) - 16.0) * 116.0; - double a = (f(x) - f(y)) * 500.0; - double b = (f(y) - f(z)) * 200.0; - return new float[] { (float) l, (float) a, (float) b }; - } - - private double f(double t) { - return (t > (216.0 / 24389.0)) ? Math.cbrt(t) : (1.0 / 3.0) * Math.pow(29.0 / 6.0, 2) * t + (4.0 / 29.0); - } - - // See https://en.wikipedia.org/wiki/Color_difference#CIE94 - double getDistance(LabColor other) { - double c1 = Math.sqrt(this.a * this.a + this.b * this.b); - double deltaC = c1 - Math.sqrt(other.a * other.a + other.b * other.b); - double deltaA = this.a - other.a; - double deltaB = this.b - other.b; - double deltaH = Math.sqrt(Math.max(0.0, deltaA * deltaA + deltaB * deltaB - deltaC * deltaC)); - return Math.sqrt(Math.max(0.0, Math.pow((this.l - other.l), 2) - + Math.pow(deltaC / (1 + 0.045 * c1), 2) + Math.pow(deltaH / (1 + 0.015 * c1), 2.0))); - } - - } - /** * 此类支持的位深度。 */