From 0bdeb7a17b27f74f5554265183f396b69b9dc1dd Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 11 Mar 2023 00:39:56 +0800 Subject: [PATCH] fix code --- .../main/java/cn/hutool/swing/img/Img.java | 53 +- .../java/cn/hutool/swing/img/ImgUtil.java | 320 ++++++---- .../swing/img/RenderingHintsBuilder.java | 566 ++++++++++++++++++ .../java/cn/hutool/swing/img/ImgTest.java | 9 + 4 files changed, 803 insertions(+), 145 deletions(-) create mode 100644 hutool-swing/src/main/java/cn/hutool/swing/img/RenderingHintsBuilder.java 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")); + } }