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
* 默认的,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
+ *
+ *
+ *
+ * @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)));
- }
-
- }
-
/**
* 此类支持的位深度。
*/