diff --git a/hutool-swing/src/main/java/cn/hutool/swing/img/Img.java b/hutool-swing/src/main/java/cn/hutool/swing/img/Img.java
index 6654f759d..1f25d955a 100755
--- a/hutool-swing/src/main/java/cn/hutool/swing/img/Img.java
+++ b/hutool-swing/src/main/java/cn/hutool/swing/img/Img.java
@@ -1,29 +1,18 @@
package cn.hutool.swing.img;
-import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.math.NumberUtil;
-import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.text.StrUtil;
+import cn.hutool.core.util.ObjUtil;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
-import java.awt.AlphaComposite;
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.Graphics2D;
-import java.awt.Image;
-import java.awt.Point;
-import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.Stroke;
+import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
@@ -61,6 +50,10 @@ public class Img implements Serializable {
* 图片输出质量,用于压缩
*/
private float quality = -1;
+ /**
+ * 图片背景色
+ */
+ private Color backgroundColor;
/**
* 从Path读取图片并开始处理
@@ -217,6 +210,17 @@ public class Img implements Serializable {
return this;
}
+ /**
+ * 设置图片的背景色
+ *
+ * @param backgroundColor{@link Color} 背景色
+ * @return this
+ */
+ public Img setBackgroundColor(final Color backgroundColor) {
+ this.backgroundColor = backgroundColor;
+ return this;
+ }
+
/**
* 缩放图像(按比例缩放)
*
@@ -598,7 +602,7 @@ public class Img implements Serializable {
* @since 3.2.2
*/
public Img rotate(final int degree) {
- if(0 == degree){
+ if (0 == degree) {
// 不旋转
return this;
}
@@ -606,13 +610,28 @@ public class Img implements Serializable {
final int width = image.getWidth(null);
final int height = image.getHeight(null);
final Rectangle rectangle = calcRotatedSize(width, height, degree);
+
+ // 目标图像
final BufferedImage targetImg = new BufferedImage(rectangle.width, rectangle.height, getTypeInt());
final Graphics2D graphics2d = targetImg.createGraphics();
- // 抗锯齿
- graphics2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+
+ // 填充背景色
+ if(null != this.backgroundColor){
+ graphics2d.setColor(this.backgroundColor);
+ graphics2d.fill(rectangle);
+ }
+
+ graphics2d.setRenderingHints(
+ RenderingHintsBuilder.of()
+ // 抗锯齿
+ .setAntialiasing(RenderingHintsBuilder.Antialias.ON)
+ // 双线性插值
+ .setInterpolation(RenderingHintsBuilder.Interpolation.BILINEAR).build());
+
// 从中心旋转
graphics2d.translate((rectangle.width - width) / 2D, (rectangle.height - height) / 2D);
graphics2d.rotate(Math.toRadians(degree), width / 2D, height / 2D);
+
graphics2d.drawImage(image, 0, 0, null);
graphics2d.dispose();
this.targetImage = targetImg;
@@ -714,7 +733,7 @@ public class Img implements Serializable {
final Image targetImage = (null == this.targetImage) ? this.srcImage : this.targetImage;
Assert.notNull(targetImage, "Target image is null !");
- return ImgUtil.write(targetImage, this.targetImageType, targetImageStream, this.quality);
+ return ImgUtil.write(targetImage, this.targetImageType, targetImageStream, this.quality, this.backgroundColor);
}
/**
diff --git a/hutool-swing/src/main/java/cn/hutool/swing/img/ImgUtil.java b/hutool-swing/src/main/java/cn/hutool/swing/img/ImgUtil.java
index e2b0ab457..6a30272c2 100755
--- a/hutool-swing/src/main/java/cn/hutool/swing/img/ImgUtil.java
+++ b/hutool-swing/src/main/java/cn/hutool/swing/img/ImgUtil.java
@@ -12,45 +12,17 @@ import cn.hutool.core.net.url.URLUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ObjUtil;
-import javax.imageio.IIOImage;
-import javax.imageio.ImageIO;
-import javax.imageio.ImageReader;
-import javax.imageio.ImageTypeSpecifier;
-import javax.imageio.ImageWriteParam;
-import javax.imageio.ImageWriter;
+import javax.imageio.*;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.swing.ImageIcon;
-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.*;
import java.awt.color.ColorSpace;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
-import java.awt.image.AffineTransformOp;
-import java.awt.image.BufferedImage;
-import java.awt.image.BufferedImageOp;
-import java.awt.image.ColorConvertOp;
-import java.awt.image.ColorModel;
-import java.awt.image.FilteredImageSource;
-import java.awt.image.ImageFilter;
-import java.awt.image.RenderedImage;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import java.awt.image.*;
+import java.io.*;
import java.net.URL;
import java.util.Iterator;
@@ -63,14 +35,34 @@ import java.util.Iterator;
*/
public class ImgUtil {
+ // region ----- [const] image type
+ /**
+ * GIF
+ */
public static final String IMAGE_TYPE_GIF = "gif";// 图形交换格式
+ /**
+ * JPG
+ */
public static final String IMAGE_TYPE_JPG = "jpg";// 联合照片专家组
+ /**
+ * JPEG
+ */
public static final String IMAGE_TYPE_JPEG = "jpeg";// 联合照片专家组
+ /**
+ * BMP
+ */
public static final String IMAGE_TYPE_BMP = "bmp";// 英文Bitmap(位图)的简写,它是Windows操作系统中的标准图像文件格式
+ /**
+ * PNG
+ */
public static final String IMAGE_TYPE_PNG = "png";// 可移植网络图形
+ /**
+ * PSD
+ */
public static final String IMAGE_TYPE_PSD = "psd";// Photoshop的专用格式Photoshop
+ // endregion
- // ---------------------------------------------------------------------------------------------------------------------- scale
+ // region ----- scale
/**
* 缩放图像(按比例缩放),目标文件的扩展名决定目标文件类型
@@ -253,8 +245,9 @@ public class ImgUtil {
public static Image scale(final Image srcImage, final int width, final int height, final Color fixedColor) {
return Img.from(srcImage).scale(width, height, fixedColor).getImg();
}
+ // endregion
- // ---------------------------------------------------------------------------------------------------------------------- cut
+ // region ----- cut and slice
/**
* 图像切割(按指定起点坐标和宽高切割)
@@ -494,8 +487,9 @@ public class ImgUtil {
throw new IORuntimeException(e);
}
}
+ // endregion
- // ---------------------------------------------------------------------------------------------------------------------- convert
+ // region ----- convert
/**
* 图像类型转换:GIF=》JPG、GIF=》PNG、PNG=》JPG、PNG=》GIF(X)、BMP=》PNG
@@ -554,8 +548,9 @@ public class ImgUtil {
throw new IORuntimeException(e);
}
}
+ // endregion
- // ---------------------------------------------------------------------------------------------------------------------- grey
+ // region ----- grey
/**
* 彩色转为黑白
@@ -637,8 +632,9 @@ public class ImgUtil {
public static Image gray(final Image srcImage) {
return Img.from(srcImage).gray().getImg();
}
+ // endregion
- // ---------------------------------------------------------------------------------------------------------------------- binary
+ // region ----- binary
/**
* 彩色转为黑白二值化图片,根据目标文件扩展名确定转换后的格式
@@ -724,8 +720,9 @@ public class ImgUtil {
public static Image binary(final Image srcImage) {
return Img.from(srcImage).binary().getImg();
}
+ // endregion
- // ---------------------------------------------------------------------------------------------------------------------- press
+ // region ----- press
/**
* 给图片添加文字水印
@@ -975,8 +972,9 @@ public class ImgUtil {
public static Image pressImage(final Image srcImage, final Image pressImg, final Rectangle rectangle, final float alpha) {
return Img.from(srcImage).pressImage(pressImg, rectangle, alpha).getImg();
}
+ // endregion
- // ---------------------------------------------------------------------------------------------------------------------- rotate
+ // region ----- rotate
/**
* 旋转图片为指定角度
@@ -1046,8 +1044,9 @@ public class ImgUtil {
public static Image rotate(final Image image, final int degree) {
return Img.from(image).rotate(degree).getImg();
}
+ // endregion
- // ---------------------------------------------------------------------------------------------------------------------- flip
+ // region ----- flip
/**
* 水平翻转图像
@@ -1107,8 +1106,9 @@ public class ImgUtil {
public static Image flip(final Image image) {
return Img.from(image).flip().getImg();
}
+ // endregion
- // ---------------------------------------------------------------------------------------------------------------------- compress
+ // region ----- compress
/**
* 压缩图像,输出图像只支持jpg文件
@@ -1122,8 +1122,10 @@ public class ImgUtil {
public static void compress(final File imageFile, final File outFile, final float quality) throws IORuntimeException {
Img.from(imageFile).setQuality(quality).write(outFile);
}
+ // endregion
- // ---------------------------------------------------------------------------------------------------------------------- other
+
+ // region ------ toImage
/**
* {@link Image} 转 {@link RenderedImage}
@@ -1167,10 +1169,25 @@ public class ImgUtil {
* @since 4.3.2
*/
public static BufferedImage toBufferedImage(final Image image, final String imageType) {
+ return toBufferedImage(image, imageType, null);
+ }
+
+ /**
+ * {@link Image} 转 {@link BufferedImage}
+ * 如果源图片的RGB模式与目标模式一致,则直接转换,否则重新绘制
+ * 默认的,png图片使用 {@link BufferedImage#TYPE_INT_ARGB}模式,其它使用 {@link BufferedImage#TYPE_INT_RGB} 模式
+ *
+ * @param image {@link Image}
+ * @param imageType 目标图片类型,例如jpg或png等
+ * @param backgroundColor 背景色{@link Color},{@code null} 表示默认背景色(黑色或者透明)
+ * @return {@link BufferedImage}
+ * @since 4.3.2
+ */
+ public static BufferedImage toBufferedImage(final Image image, final String imageType, final Color backgroundColor) {
final int type = IMAGE_TYPE_PNG.equalsIgnoreCase(imageType)
? BufferedImage.TYPE_INT_ARGB
: BufferedImage.TYPE_INT_RGB;
- return toBufferedImage(image, type);
+ return toBufferedImage(image, type, backgroundColor);
}
/**
@@ -1183,18 +1200,33 @@ public class ImgUtil {
* @since 5.4.7
*/
public static BufferedImage toBufferedImage(final Image image, final int imageType) {
+ return toBufferedImage(image, imageType, null);
+ }
+
+ /**
+ * {@link Image} 转 {@link BufferedImage}
+ * 如果源图片的RGB模式与目标模式一致,则直接转换,否则重新绘制
+ *
+ * @param image {@link Image}
+ * @param imageType 目标图片类型,{@link BufferedImage}中的常量,例如黑白等
+ * @param backgroundColor 背景色{@link Color},{@code null} 表示默认背景色(黑色或者透明)
+ * @return {@link BufferedImage}
+ * @since 5.4.7
+ */
+ public static BufferedImage toBufferedImage(final Image image, final int imageType, final Color backgroundColor) {
BufferedImage bufferedImage;
if (image instanceof BufferedImage) {
bufferedImage = (BufferedImage) image;
if (imageType != bufferedImage.getType()) {
- bufferedImage = copyImage(image, imageType);
+ bufferedImage = copyImage(image, imageType, backgroundColor);
}
return bufferedImage;
}
- bufferedImage = copyImage(image, imageType);
+ bufferedImage = copyImage(image, imageType, backgroundColor);
return bufferedImage;
}
+ // endregion
/**
* 将已有Image复制新的一份出来
@@ -1256,22 +1288,6 @@ public class ImgUtil {
return bimage;
}
- /**
- * 创建与当前设备颜色模式兼容的 {@link BufferedImage}
- *
- * @param width 宽度
- * @param height 高度
- * @param transparency 透明模式,见 {@link java.awt.Transparency}
- * @return {@link BufferedImage}
- * @since 5.7.13
- */
- public static BufferedImage createCompatibleImage(final int width, final int height, final int transparency) throws HeadlessException {
- final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
- final GraphicsDevice gs = ge.getDefaultScreenDevice();
- final GraphicsConfiguration gc = gs.getDefaultConfiguration();
- return gc.createCompatibleImage(width, height, transparency);
- }
-
/**
* 将Base64编码的图像信息转为 {@link BufferedImage}
*
@@ -1346,6 +1362,24 @@ public class ImgUtil {
return out.toByteArray();
}
+ // region ----- createImage
+
+ /**
+ * 创建与当前设备颜色模式兼容的 {@link BufferedImage}
+ *
+ * @param width 宽度
+ * @param height 高度
+ * @param transparency 透明模式,见 {@link java.awt.Transparency}
+ * @return {@link BufferedImage}
+ * @since 5.7.13
+ */
+ public static BufferedImage createCompatibleImage(final int width, final int height, final int transparency) {
+ final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ final GraphicsDevice gs = ge.getDefaultScreenDevice();
+ final GraphicsConfiguration gc = gs.getDefaultConfiguration();
+ return gc.createCompatibleImage(width, height, transparency);
+ }
+
/**
* 根据文字创建透明背景的PNG图片
*
@@ -1355,7 +1389,7 @@ public class ImgUtil {
* @param out 图片输出地
* @throws IORuntimeException IO异常
*/
- public static void createTransparentImage(String str, Font font, Color fontColor, ImageOutputStream out) throws IORuntimeException {
+ public static void createTransparentImage(final String str, final Font font, final Color fontColor, final ImageOutputStream out) throws IORuntimeException {
writePng(createImage(str, font, null, fontColor, BufferedImage.TYPE_INT_ARGB), out);
}
@@ -1409,6 +1443,7 @@ public class ImgUtil {
return image;
}
+ // endregion
/**
* 获取font的样式应用在str上的整个矩形
@@ -1425,6 +1460,8 @@ public class ImgUtil {
false));
}
+ // region ----- write
+
/**
* 写出图像为JPG格式
*
@@ -1516,20 +1553,38 @@ public class ImgUtil {
/**
* 写出图像为指定格式
*
- * @param image {@link Image}
- * @param imageType 图片类型(图片扩展名)
- * @param destImageStream 写出到的目标流
- * @param quality 质量,数字为0~1(不包括0和1)表示质量压缩比,除此数字外设置表示不压缩
+ * @param image {@link Image}
+ * @param imageType 图片类型(图片扩展名)
+ * @param targetImageStream 写出到的目标流
+ * @param quality 质量,数字为0~1(不包括0和1)表示质量压缩比,除此数字外设置表示不压缩
* @return 是否成功写出,如果返回false表示未找到合适的Writer
* @throws IORuntimeException IO异常
* @since 4.3.2
*/
- public static boolean write(final Image image, String imageType, final ImageOutputStream destImageStream, final float quality) throws IORuntimeException {
+ public static boolean write(final Image image, final String imageType, final ImageOutputStream targetImageStream,
+ final float quality) throws IORuntimeException {
+ return write(image, imageType, targetImageStream, quality, null);
+ }
+
+ /**
+ * 写出图像为指定格式
+ *
+ * @param image {@link Image}
+ * @param imageType 图片类型(图片扩展名)
+ * @param destImageStream 写出到的目标流
+ * @param quality 质量,数字为0~1(不包括0和1)表示质量压缩比,除此数字外设置表示不压缩
+ * @param backgroundColor 背景色{@link Color}
+ * @return 是否成功写出,如果返回false表示未找到合适的Writer
+ * @throws IORuntimeException IO异常
+ * @since 4.3.2
+ */
+ public static boolean write(final Image image, String imageType, final ImageOutputStream destImageStream,
+ final float quality, final Color backgroundColor) throws IORuntimeException {
if (StrUtil.isBlank(imageType)) {
imageType = IMAGE_TYPE_JPG;
}
- final BufferedImage bufferedImage = toBufferedImage(image, imageType);
+ final BufferedImage bufferedImage = toBufferedImage(image, imageType, backgroundColor);
final ImageWriter writer = getWriter(bufferedImage, imageType);
return write(bufferedImage, writer, destImageStream, quality);
}
@@ -1598,19 +1653,45 @@ public class ImgUtil {
}
/**
- * 获得{@link ImageReader}
+ * 根据给定的Image对象和格式获取对应的{@link ImageWriter},如果未找到合适的Writer,返回null
*
- * @param type 图片文件类型,例如 "jpeg" 或 "tiff"
- * @return {@link ImageReader}
+ * @param img {@link Image}
+ * @param formatName 图片格式,例如"jpg"、"png"
+ * @return {@link ImageWriter}
+ * @since 4.3.2
*/
- public static ImageReader getReader(final String type) {
- final Iterator iterator = ImageIO.getImageReadersByFormatName(type);
- if (iterator.hasNext()) {
- return iterator.next();
- }
- return null;
+ public static ImageWriter getWriter(final Image img, final String formatName) {
+ final ImageTypeSpecifier type = ImageTypeSpecifier.createFromRenderedImage(toBufferedImage(img, formatName));
+ final Iterator iter = ImageIO.getImageWriters(type, formatName);
+ return iter.hasNext() ? iter.next() : null;
}
+ /**
+ * 根据给定的图片格式或者扩展名获取{@link ImageWriter},如果未找到合适的Writer,返回null
+ *
+ * @param formatName 图片格式或扩展名,例如"jpg"、"png"
+ * @return {@link ImageWriter}
+ * @since 4.3.2
+ */
+ public static ImageWriter getWriter(final String formatName) {
+ ImageWriter writer = null;
+ Iterator iter = ImageIO.getImageWritersByFormatName(formatName);
+ if (iter.hasNext()) {
+ writer = iter.next();
+ }
+ if (null == writer) {
+ // 尝试扩展名获取
+ iter = ImageIO.getImageWritersBySuffix(formatName);
+ if (iter.hasNext()) {
+ writer = iter.next();
+ }
+ }
+ return writer;
+ }
+ // endregion
+
+ // region ----- read
+
/**
* 从文件中读取图片,请使用绝对路径,使用相对路径会相对于ClassPath
*
@@ -1644,17 +1725,6 @@ public class ImgUtil {
return result;
}
- /**
- * 从URL中获取或读取图片对象
- *
- * @param url URL
- * @return {@link Image}
- * @since 5.5.8
- */
- public static Image getImage(final URL url) {
- return Toolkit.getDefaultToolkit().getImage(url);
- }
-
/**
* 从{@link Resource}中读取图片
*
@@ -1732,6 +1802,34 @@ public class ImgUtil {
return result;
}
+ /**
+ * 获得{@link ImageReader}
+ *
+ * @param type 图片文件类型,例如 "jpeg" 或 "tiff"
+ * @return {@link ImageReader}
+ */
+ public static ImageReader getReader(final String type) {
+ final Iterator iterator = ImageIO.getImageReadersByFormatName(type);
+ if (iterator.hasNext()) {
+ return iterator.next();
+ }
+ return null;
+ }
+ // endregion
+
+ // region ----- getImage and getPoint
+
+ /**
+ * 从URL中获取或读取图片对象
+ *
+ * @param url URL
+ * @return {@link Image}
+ * @since 5.5.8
+ */
+ public static Image getImage(final URL url) {
+ return Toolkit.getDefaultToolkit().getImage(url);
+ }
+
/**
* 获取{@link ImageOutputStream}
*
@@ -1801,45 +1899,6 @@ public class ImgUtil {
return result;
}
- /**
- * 根据给定的Image对象和格式获取对应的{@link ImageWriter},如果未找到合适的Writer,返回null
- *
- * @param img {@link Image}
- * @param formatName 图片格式,例如"jpg"、"png"
- * @return {@link ImageWriter}
- * @since 4.3.2
- */
- public static ImageWriter getWriter(final Image img, final String formatName) {
- final ImageTypeSpecifier type = ImageTypeSpecifier.createFromRenderedImage(toBufferedImage(img, formatName));
- final Iterator iter = ImageIO.getImageWriters(type, formatName);
- return iter.hasNext() ? iter.next() : null;
- }
-
- /**
- * 根据给定的图片格式或者扩展名获取{@link ImageWriter},如果未找到合适的Writer,返回null
- *
- * @param formatName 图片格式或扩展名,例如"jpg"、"png"
- * @return {@link ImageWriter}
- * @since 4.3.2
- */
- public static ImageWriter getWriter(final String formatName) {
- ImageWriter writer = null;
- Iterator iter = ImageIO.getImageWritersByFormatName(formatName);
- if (iter.hasNext()) {
- writer = iter.next();
- }
- if (null == writer) {
- // 尝试扩展名获取
- iter = ImageIO.getImageWritersBySuffix(formatName);
- if (iter.hasNext()) {
- writer = iter.next();
- }
- }
- return writer;
- }
-
- // -------------------------------------------------------------------------------------------------------------------- Color
-
/**
* 获得修正后的矩形坐标位置,变为以背景中心为基准坐标(即x,y == 0,0时,处于背景正中)
*
@@ -1855,8 +1914,9 @@ public class ImgUtil {
rectangle.y + (Math.abs(backgroundHeight - rectangle.height) / 2)//
);
}
+ // endregion
- // ------------------------------------------------------------------------------------------------------ 背景图换算
+ // region ----- backgroundRemoval
/**
* 背景移除
@@ -1948,6 +2008,9 @@ public class ImgUtil {
public static BufferedImage backgroundRemoval(final ByteArrayOutputStream outputStream, final Color override, final int tolerance) {
return BackgroundRemoval.backgroundRemoval(outputStream, override, tolerance);
}
+ // endregion
+
+ // region ------ transform and filter
/**
* 图片颜色转换
@@ -1999,4 +2062,5 @@ public class ImgUtil {
return Toolkit.getDefaultToolkit().createImage(
new FilteredImageSource(image.getSource(), filter));
}
+ // endregion
}
diff --git a/hutool-swing/src/main/java/cn/hutool/swing/img/RenderingHintsBuilder.java b/hutool-swing/src/main/java/cn/hutool/swing/img/RenderingHintsBuilder.java
new file mode 100644
index 000000000..fecf0ab4c
--- /dev/null
+++ b/hutool-swing/src/main/java/cn/hutool/swing/img/RenderingHintsBuilder.java
@@ -0,0 +1,566 @@
+package cn.hutool.swing.img;
+
+import cn.hutool.core.lang.builder.Builder;
+
+import java.awt.RenderingHints;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 着色微调构建器
+ *
+ * @author looly
+ * @since 6.0.0
+ */
+public class RenderingHintsBuilder implements Builder {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 创建{@link RenderingHints} 构建器
+ *
+ * @return {@code RenderingHintsBuilder}
+ */
+ public static RenderingHintsBuilder of() {
+ return new RenderingHintsBuilder();
+ }
+
+ private final Map hintsMap;
+
+ /**
+ * 构造
+ */
+ private RenderingHintsBuilder() {
+ // 共计10项配置
+ hintsMap = new HashMap<>(10, 1);
+ }
+
+ /**
+ * 设置是否使用抗锯齿
+ *
+ * @param antialias 抗锯齿选项,{@code null}表示移除此选项
+ * @return this
+ */
+ public RenderingHintsBuilder setAntialiasing(final Antialias antialias) {
+ final RenderingHints.Key key = RenderingHints.KEY_ANTIALIASING;
+ if (null == antialias) {
+ this.hintsMap.remove(key);
+ } else {
+ this.hintsMap.put(key, antialias.getValue());
+ }
+ return this;
+ }
+
+ /**
+ * 设置控制颜色着色的渲染方式
+ *
+ * @param colorRender 颜色着色的渲染方式,{@code null}表示移除此选项
+ * @return this
+ */
+ public RenderingHintsBuilder setColorRendering(final ColorRender colorRender) {
+ final RenderingHints.Key key = RenderingHints.KEY_COLOR_RENDERING;
+ if (null == colorRender) {
+ this.hintsMap.remove(key);
+ } else {
+ this.hintsMap.put(key, colorRender.getValue());
+ }
+ return this;
+ }
+
+ /**
+ * 设置控制如何处理抖动
+ * 抖动是用一组有限的颜色合成出一个更大范围的颜色的过程,方法是给相邻像素着色以产生不在该组颜色中的新的颜色幻觉。
+ *
+ * @param dither 如何处理抖动,{@code null}表示移除此选项
+ * @return this
+ */
+ public RenderingHintsBuilder setDithering(final Dither dither) {
+ final RenderingHints.Key key = RenderingHints.KEY_DITHERING;
+ if (null == dither) {
+ this.hintsMap.remove(key);
+ } else {
+ this.hintsMap.put(key, dither.getValue());
+ }
+ return this;
+ }
+
+ /**
+ * 设置字体规格
+ *
+ * @param fractionalMetrics 字体规格,{@code null}表示移除此选项
+ * @return this
+ */
+ public RenderingHintsBuilder setFractionalMetrics(final FractionalMetrics fractionalMetrics) {
+ final RenderingHints.Key key = RenderingHints.KEY_FRACTIONALMETRICS;
+ if (null == fractionalMetrics) {
+ this.hintsMap.remove(key);
+ } else {
+ this.hintsMap.put(key, fractionalMetrics.getValue());
+ }
+ return this;
+ }
+
+ /**
+ * 设置怎样做内插
+ * 在对一个源图像做变形时,变形后的像素很少能够恰好对应目标像素位置。
+ * 在这种情况下,每个变形后的像素的颜色值不得不由周围的像素决定。内插就是实现上述过程。
+ *
+ * @param interpolation 内插方式,{@code null}表示移除此选项
+ * @return this
+ */
+ public RenderingHintsBuilder setInterpolation(final Interpolation interpolation) {
+ final RenderingHints.Key key = RenderingHints.KEY_INTERPOLATION;
+ if (null == interpolation) {
+ this.hintsMap.remove(key);
+ } else {
+ this.hintsMap.put(key, interpolation.getValue());
+ }
+ return this;
+ }
+
+ /**
+ * 设置着色技术,在速度和质量之间进行权衡。
+ *
+ * @param render 着色技术,{@code null}表示移除此选项
+ * @return this
+ */
+ public RenderingHintsBuilder setRendering(final Render render) {
+ final RenderingHints.Key key = RenderingHints.KEY_RENDERING;
+ if (null == render) {
+ this.hintsMap.remove(key);
+ } else {
+ this.hintsMap.put(key, render.getValue());
+ }
+ return this;
+ }
+
+ /**
+ * 设置对文本着色时是否抗锯齿
+ *
+ * @param textAntialias 文本抗锯齿方式,{@code null}表示移除此选项
+ * @return this
+ */
+ public RenderingHintsBuilder setTextAntialias(final TextAntialias textAntialias) {
+ final RenderingHints.Key key = RenderingHints.KEY_TEXT_ANTIALIASING;
+ if (null == textAntialias) {
+ this.hintsMap.remove(key);
+ } else {
+ this.hintsMap.put(key, textAntialias.getValue());
+ }
+ return this;
+ }
+
+ /**
+ * 设置alpha合成微调
+ *
+ * @param alphaInterpolation alpha合成微调,{@code null}表示移除此选项
+ * @return this
+ */
+ public RenderingHintsBuilder setAlphaInterpolation(final AlphaInterpolation alphaInterpolation) {
+ final RenderingHints.Key key = RenderingHints.KEY_ALPHA_INTERPOLATION;
+ if (null == alphaInterpolation) {
+ this.hintsMap.remove(key);
+ } else {
+ this.hintsMap.put(key, alphaInterpolation.getValue());
+ }
+ return this;
+ }
+
+ /**
+ * 设置LCD文本对比呈现
+ * ,100 到 250 之间的正整数。通常,有用值的范围缩小到 140-180
+ *
+ * @param textLCDContrast LCD文本对比呈现,100 到 250 之间的正整数
+ * @return this
+ */
+ public RenderingHintsBuilder setAlphaInterpolation(final Integer textLCDContrast) {
+ final RenderingHints.Key key = RenderingHints.KEY_TEXT_LCD_CONTRAST;
+ if (null == textLCDContrast) {
+ this.hintsMap.remove(key);
+ } else {
+ this.hintsMap.put(key, textLCDContrast);
+ }
+ return this;
+ }
+
+ /**
+ * 设置笔划规范化控制
+ *
+ * @param strokeControl 笔划规范化控制,{@code null}表示移除此选项
+ * @return this
+ */
+ public RenderingHintsBuilder setAlphaInterpolation(final StrokeControl strokeControl) {
+ final RenderingHints.Key key = RenderingHints.KEY_STROKE_CONTROL;
+ if (null == strokeControl) {
+ this.hintsMap.remove(key);
+ } else {
+ this.hintsMap.put(key, strokeControl.getValue());
+ }
+ return this;
+ }
+
+ @Override
+ public RenderingHints build() {
+ return new RenderingHints(this.hintsMap);
+ }
+
+ // region ----- enums
+
+ /**
+ * 抗锯齿选项
+ *
+ * @see RenderingHints#VALUE_ANTIALIAS_ON
+ * @see RenderingHints#VALUE_ANTIALIAS_OFF
+ * @see RenderingHints#VALUE_ANTIALIAS_DEFAULT
+ */
+ public enum Antialias {
+ /**
+ * 使用抗锯齿
+ */
+ ON(RenderingHints.VALUE_ANTIALIAS_ON),
+ /**
+ * 不使用抗锯齿
+ */
+ OFF(RenderingHints.VALUE_ANTIALIAS_OFF),
+ /**
+ * 默认的抗锯齿
+ */
+ DEFAULT(RenderingHints.VALUE_ANTIALIAS_OFF);
+
+ private final Object value;
+
+ Antialias(final Object value) {
+ this.value = value;
+ }
+
+ /**
+ * 获取值
+ *
+ * @return 值
+ */
+ public Object getValue() {
+ return this.value;
+ }
+ }
+
+ /**
+ * 文本抗锯齿选项
+ *
+ * @see RenderingHints#VALUE_TEXT_ANTIALIAS_ON
+ * @see RenderingHints#VALUE_TEXT_ANTIALIAS_OFF
+ * @see RenderingHints#VALUE_TEXT_ANTIALIAS_DEFAULT
+ */
+ public enum TextAntialias {
+ /**
+ * 使用抗锯齿呈现文本
+ */
+ ON(RenderingHints.VALUE_TEXT_ANTIALIAS_ON),
+ /**
+ * 不使用抗锯齿呈现文本
+ */
+ OFF(RenderingHints.VALUE_TEXT_ANTIALIAS_OFF),
+ /**
+ * 使用平台默认的文本抗锯齿模式呈现文本
+ */
+ DEFAULT(RenderingHints.VALUE_TEXT_ANTIALIAS_OFF),
+ /**
+ * 自动的使用字体中的信息决定是否使用抗锯齿或使用实心颜色
+ */
+ GASP(RenderingHints.VALUE_TEXT_ANTIALIAS_GASP),
+ /**
+ * 针对LCD显示器优化文本显示LCD_HRGB
+ */
+ LCD_HRGB(RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB),
+ /**
+ * 针对LCD显示器优化文本显示LCD_HBGR
+ */
+ LCD_HBGR(RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR),
+ /**
+ * 针对LCD显示器优化文本显示LCD_VRGB
+ */
+ LCD_VRGB(RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB),
+ /**
+ * 针对LCD显示器优化文本显示LCD_VBGR
+ */
+ LCD_VBGR(RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VBGR);
+
+ private final Object value;
+
+ TextAntialias(final Object value) {
+ this.value = value;
+ }
+
+ /**
+ * 获取值
+ *
+ * @return 值
+ */
+ public Object getValue() {
+ return this.value;
+ }
+ }
+
+ /**
+ * 颜色着色的渲染方式
+ *
+ * @see RenderingHints#VALUE_COLOR_RENDER_SPEED
+ * @see RenderingHints#VALUE_COLOR_RENDER_QUALITY
+ * @see RenderingHints#VALUE_COLOR_RENDER_DEFAULT
+ */
+ public enum ColorRender {
+ /**
+ * 追求速度
+ */
+ SPEED(RenderingHints.VALUE_COLOR_RENDER_SPEED),
+ /**
+ * 追求质量
+ */
+ QUALITY(RenderingHints.VALUE_COLOR_RENDER_QUALITY),
+ /**
+ * 默认渲染方式
+ */
+ DEFAULT(RenderingHints.VALUE_COLOR_RENDER_DEFAULT);
+
+ private final Object value;
+
+ ColorRender(final Object value) {
+ this.value = value;
+ }
+
+ /**
+ * 获取值
+ *
+ * @return 值
+ */
+ public Object getValue() {
+ return this.value;
+ }
+ }
+
+ /**
+ * 着色技术
+ *
+ * @see RenderingHints#VALUE_RENDER_SPEED
+ * @see RenderingHints#VALUE_RENDER_QUALITY
+ * @see RenderingHints#VALUE_RENDER_DEFAULT
+ */
+ public enum Render {
+ /**
+ * 追求速度
+ */
+ SPEED(RenderingHints.VALUE_RENDER_SPEED),
+ /**
+ * 追求质量
+ */
+ QUALITY(RenderingHints.VALUE_RENDER_QUALITY),
+ /**
+ * 默认
+ */
+ DEFAULT(RenderingHints.VALUE_RENDER_DEFAULT);
+
+ private final Object value;
+
+ Render(final Object value) {
+ this.value = value;
+ }
+
+ /**
+ * 获取值
+ *
+ * @return 值
+ */
+ public Object getValue() {
+ return this.value;
+ }
+ }
+
+ /**
+ * 控制如何处理抖动
+ * 抖动是用一组有限的颜色合成出一个更大范围的颜色的过程,方法是给相邻像素着色以产生不在该组颜色中的新的颜色幻觉。
+ *
+ * @see RenderingHints#VALUE_DITHER_ENABLE
+ * @see RenderingHints#VALUE_DITHER_DISABLE
+ * @see RenderingHints#VALUE_DITHER_DEFAULT
+ */
+ public enum Dither {
+ /**
+ * 抖动
+ */
+ ENABLE(RenderingHints.VALUE_DITHER_ENABLE),
+ /**
+ * 不抖动
+ */
+ DISABLE(RenderingHints.VALUE_DITHER_DISABLE),
+ /**
+ * 默认
+ */
+ DEFAULT(RenderingHints.VALUE_DITHER_DEFAULT);
+
+ private final Object value;
+
+ Dither(final Object value) {
+ this.value = value;
+ }
+
+ /**
+ * 获取值
+ *
+ * @return 值
+ */
+ public Object getValue() {
+ return this.value;
+ }
+ }
+
+ /**
+ * 字体规格
+ *
+ * @see RenderingHints#VALUE_FRACTIONALMETRICS_ON
+ * @see RenderingHints#VALUE_FRACTIONALMETRICS_OFF
+ * @see RenderingHints#VALUE_FRACTIONALMETRICS_DEFAULT
+ */
+ public enum FractionalMetrics {
+ /**
+ * 启用字体规格
+ */
+ SPEED(RenderingHints.VALUE_FRACTIONALMETRICS_ON),
+ /**
+ * 禁用字体规格
+ */
+ QUALITY(RenderingHints.VALUE_FRACTIONALMETRICS_OFF),
+ /**
+ * 默认
+ */
+ DEFAULT(RenderingHints.VALUE_FRACTIONALMETRICS_DEFAULT);
+
+ private final Object value;
+
+ FractionalMetrics(final Object value) {
+ this.value = value;
+ }
+
+ /**
+ * 获取值
+ *
+ * @return 值
+ */
+ public Object getValue() {
+ return this.value;
+ }
+ }
+
+ /**
+ * 内插
+ * 在对一个源图像做变形时,变形后的像素很少能够恰好对应目标像素位置。
+ * 在这种情况下,每个变形后的像素的颜色值不得不由周围的像素决定。内插就是实现上述过程。
+ *
+ * @see RenderingHints#VALUE_INTERPOLATION_BICUBIC
+ * @see RenderingHints#VALUE_INTERPOLATION_BILINEAR
+ * @see RenderingHints#VALUE_INTERPOLATION_NEAREST_NEIGHBOR
+ */
+ public enum Interpolation {
+ /**
+ * 双三次插值
+ */
+ BICUBIC(RenderingHints.VALUE_INTERPOLATION_BICUBIC),
+ /**
+ * 双线性插值
+ */
+ BILINEAR(RenderingHints.VALUE_INTERPOLATION_BILINEAR),
+ /**
+ * 最近邻插值
+ */
+ NEAREST_NEIGHBOR(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
+
+ private final Object value;
+
+ Interpolation(final Object value) {
+ this.value = value;
+ }
+
+ /**
+ * 获取值
+ *
+ * @return 值
+ */
+ public Object getValue() {
+ return this.value;
+ }
+ }
+
+ /**
+ * alpha合成微调
+ *
+ * @see RenderingHints#VALUE_ALPHA_INTERPOLATION_SPEED
+ * @see RenderingHints#VALUE_ALPHA_INTERPOLATION_QUALITY
+ * @see RenderingHints#VALUE_ALPHA_INTERPOLATION_DEFAULT
+ */
+ public enum AlphaInterpolation {
+ /**
+ * 追求速度
+ */
+ SPEED(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED),
+ /**
+ * 追求质量
+ */
+ QUALITY(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY),
+ /**
+ * 平台默认
+ */
+ DEFAULT(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT);
+
+ private final Object value;
+
+ AlphaInterpolation(final Object value) {
+ this.value = value;
+ }
+
+ /**
+ * 获取值
+ *
+ * @return 值
+ */
+ public Object getValue() {
+ return this.value;
+ }
+ }
+
+ /**
+ * 笔划规范化控制
+ *
+ * @see RenderingHints#VALUE_STROKE_NORMALIZE
+ * @see RenderingHints#VALUE_STROKE_PURE
+ * @see RenderingHints#VALUE_STROKE_DEFAULT
+ */
+ public enum StrokeControl {
+ /**
+ * 追求速度
+ */
+ NORMALIZE(RenderingHints.VALUE_STROKE_NORMALIZE),
+ /**
+ * 追求质量
+ */
+ PURE(RenderingHints.VALUE_STROKE_PURE),
+ /**
+ * 平台默认
+ */
+ DEFAULT(RenderingHints.VALUE_STROKE_DEFAULT);
+
+ private final Object value;
+
+ StrokeControl(final Object value) {
+ this.value = value;
+ }
+
+ /**
+ * 获取值
+ *
+ * @return 值
+ */
+ public Object getValue() {
+ return this.value;
+ }
+ }
+
+
+ // endregion
+}
diff --git a/hutool-swing/src/test/java/cn/hutool/swing/img/ImgTest.java b/hutool-swing/src/test/java/cn/hutool/swing/img/ImgTest.java
index b57d487c3..4973c1823 100755
--- a/hutool-swing/src/test/java/cn/hutool/swing/img/ImgTest.java
+++ b/hutool-swing/src/test/java/cn/hutool/swing/img/ImgTest.java
@@ -96,4 +96,13 @@ public class ImgTest {
final Image img = ImgUtil.getImage(URLUtil.getURL(file));
ImgUtil.scale(img, fileScale, 0.8f);
}
+
+ @Test
+ @Ignore
+ public void rotateWithBackgroundTest() {
+ Img.from(FileUtil.file("d:/test/aaa.jpg"))
+ .setBackgroundColor(Color.RED)
+ .rotate(45)
+ .write(FileUtil.file("d:/test/aaa45.jpg"));
+ }
}