This commit is contained in:
Looly 2023-03-11 02:43:45 +08:00
parent 4c5e127c88
commit 7b0f88d110
4 changed files with 219 additions and 115 deletions

View File

@ -3,17 +3,7 @@ package cn.hutool.swing.img;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.swing.img.color.ColorUtil;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.*;
import java.awt.image.BufferedImage;
/**
@ -34,7 +24,6 @@ public class GraphicsUtil {
*/
public static Graphics2D createGraphics(final BufferedImage image, final Color color) {
final Graphics2D g = image.createGraphics();
if (null != color) {
// 填充背景
g.setColor(color);
@ -99,9 +88,7 @@ public class GraphicsUtil {
*/
public static Graphics drawString(final Graphics g, final String str, final Font font, final Color color, final int width, final int height) {
// 抗锯齿
if (g instanceof Graphics2D) {
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
enableAntialias(g);
// 创建字体
g.setFont(font);
@ -168,9 +155,7 @@ public class GraphicsUtil {
*/
public static Graphics drawString(final Graphics g, final String str, final Font font, final Color color, final Point point) {
// 抗锯齿
if (g instanceof Graphics2D) {
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
enableAntialias(g);
g.setFont(font);
g.setColor(ObjUtil.defaultIfNull(color, Color.BLACK));
@ -182,8 +167,8 @@ public class GraphicsUtil {
/**
* 绘制图片
*
* @param g 画笔
* @param img 要绘制的图片
* @param g 画笔
* @param img 要绘制的图片
* @param point 绘制的位置基于左上角
* @return 画笔对象
*/
@ -208,12 +193,27 @@ public class GraphicsUtil {
/**
* 设置画笔透明度
*
* @param g 画笔
* @param g 画笔
* @param alpha 透明度alpha 必须是范围 [0.0, 1.0] 之内包含边界值的一个浮点数字
* @return 画笔
*/
public static Graphics2D setAlpha(final Graphics2D g, final float alpha){
public static Graphics2D setAlpha(final Graphics2D g, final float alpha) {
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
return g;
}
/**
* 打开抗锯齿和文本抗锯齿
*
* @param g {@link Graphics}
*/
private static void enableAntialias(final Graphics g) {
if (g instanceof Graphics2D) {
((Graphics2D) g).setRenderingHints(
RenderingHintsBuilder.of()
.setAntialiasing(RenderingHintsBuilder.Antialias.ON)
.setTextAntialias(RenderingHintsBuilder.TextAntialias.ON).build()
);
}
}
}

View File

@ -706,11 +706,10 @@ public class Img implements Serializable {
* 结果类型设定见{@link #setTargetImageType(String)}
*
* @param out 写出到的目标流
* @return 是否成功写出如果返回false表示未找到合适的Writer
* @throws IORuntimeException IO异常
*/
public boolean write(final OutputStream out) throws IORuntimeException {
return write(ImgUtil.getImageOutputStream(out));
public void write(final OutputStream out) throws IORuntimeException {
write(ImgUtil.getImageOutputStream(out));
}
/**
@ -718,27 +717,25 @@ public class Img implements Serializable {
* 结果类型设定见{@link #setTargetImageType(String)}
*
* @param targetImageStream 写出到的目标流
* @return 是否成功写出如果返回false表示未找到合适的Writer
* @throws IORuntimeException IO异常
*/
public boolean write(final ImageOutputStream targetImageStream) throws IORuntimeException {
public void write(final ImageOutputStream targetImageStream) throws IORuntimeException {
Assert.notBlank(this.targetImageType, "Target image type is blank !");
Assert.notNull(targetImageStream, "Target output stream is null !");
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, this.backgroundColor);
ImgUtil.write(targetImage, this.targetImageType, targetImageStream, this.quality, this.backgroundColor);
}
/**
* 写出图像为目标文件扩展名对应的格式
*
* @param targetFile 目标文件
* @return 是否成功写出如果返回false表示未找到合适的Writer
* @throws IORuntimeException IO异常
*/
public boolean write(final File targetFile) throws IORuntimeException {
public void write(final File targetFile) throws IORuntimeException {
final String formatName = FileNameUtil.extName(targetFile);
if (StrUtil.isNotBlank(formatName)) {
this.targetImageType = formatName;
@ -752,7 +749,7 @@ public class Img implements Serializable {
ImageOutputStream out = null;
try {
out = ImgUtil.getImageOutputStream(targetFile);
return write(out);
write(out);
} finally {
IoUtil.close(out);
}

View File

@ -10,7 +10,6 @@ import cn.hutool.core.lang.Assert;
import cn.hutool.core.math.NumberUtil;
import cn.hutool.core.net.url.URLUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ObjUtil;
import javax.imageio.*;
import javax.imageio.stream.ImageInputStream;
@ -502,6 +501,7 @@ public class ImgUtil {
Assert.notNull(destImageFile);
Assert.isFalse(srcImageFile.equals(destImageFile), "Src file is equals to dest file!");
// 通过扩展名检查图片类型相同类型直接复制
final String srcExtName = FileNameUtil.extName(srcImageFile);
final String destExtName = FileNameUtil.extName(destImageFile);
if (StrUtil.equalsIgnoreCase(srcExtName, destExtName)) {
@ -1430,15 +1430,8 @@ public class ImgUtil {
// 创建图片
final BufferedImage image = new BufferedImage(width, height, imageType);
final Graphics g = image.getGraphics();
if (null != backgroundColor) {
// 先用背景色填充整张图片,也就是背景
g.setColor(backgroundColor);
g.fillRect(0, 0, width, height);
}
g.setColor(ObjUtil.defaultIfNull(fontColor, Color.BLACK));
g.setFont(font);// 设置画笔字体
g.drawString(str, 0, font.getSize());// 画出字符串
final Graphics g = GraphicsUtil.createGraphics(image, backgroundColor);
GraphicsUtil.drawString(g, str, font, fontColor, new Point(0, font.getSize()));
g.dispose();
return image;
@ -1535,6 +1528,19 @@ public class ImgUtil {
write(image, imageType, getImageOutputStream(out));
}
/**
* 写出图像为目标文件扩展名对应的格式
*
* @param image {@link Image}
* @param targetFile 目标文件
* @throws IORuntimeException IO异常
* @since 3.1.0
*/
public static void write(final Image image, final File targetFile) throws IORuntimeException {
final String imageType = FileNameUtil.extName(targetFile);
ImgWriter.of(image, imageType).write(targetFile);
}
/**
* 写出图像为指定格式GIF=JPGGIF=PNGPNG=JPGPNG=GIF(X)BMP=PNG<br>
* 此方法并不关闭流
@ -1542,12 +1548,11 @@ public class ImgUtil {
* @param image {@link Image}
* @param imageType 图片类型图片扩展名
* @param destImageStream 写出到的目标流
* @return 是否成功写出如果返回false表示未找到合适的Writer
* @throws IORuntimeException IO异常
* @since 3.1.2
*/
public static boolean write(final Image image, final String imageType, final ImageOutputStream destImageStream) throws IORuntimeException {
return write(image, imageType, destImageStream, 1);
public static void write(final Image image, final String imageType, final ImageOutputStream destImageStream) throws IORuntimeException {
write(image, imageType, destImageStream, 1);
}
/**
@ -1557,110 +1562,57 @@ public class ImgUtil {
* @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, final String imageType, final ImageOutputStream targetImageStream,
final float quality) throws IORuntimeException {
return write(image, imageType, targetImageStream, quality, null);
public static void write(final Image image, final String imageType, final ImageOutputStream targetImageStream,
final float quality) throws IORuntimeException {
write(image, imageType, targetImageStream, quality, null);
}
/**
* 写出图像为指定格式
*
* @param image {@link Image}
* @param imageType 图片类型图片扩展名
* @param imageType 图片类型图片扩展名{@code null}表示使用RGB模式JPG
* @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;
}
public static void write(final Image image, final String imageType, final ImageOutputStream destImageStream,
final float quality, final Color backgroundColor) throws IORuntimeException {
final BufferedImage bufferedImage = toBufferedImage(image, imageType, backgroundColor);
final ImageWriter writer = getWriter(bufferedImage, imageType);
return write(bufferedImage, writer, destImageStream, quality);
}
/**
* 写出图像为目标文件扩展名对应的格式
*
* @param image {@link Image}
* @param targetFile 目标文件
* @throws IORuntimeException IO异常
* @since 3.1.0
*/
public static void write(final Image image, final File targetFile) throws IORuntimeException {
FileUtil.touch(targetFile);
ImageOutputStream out = null;
try {
out = getImageOutputStream(targetFile);
write(image, FileNameUtil.extName(targetFile), out);
} finally {
IoUtil.close(out);
}
write(bufferedImage, destImageStream, quality);
}
/**
* 通过{@link ImageWriter}写出图片到输出流
*
* @param image 图片
* @param writer {@link ImageWriter}
* @param output 输出的Image流{@link ImageOutputStream}
* @param quality 质量数字为0~1不包括0和1表示质量压缩比除此数字外设置表示不压缩
* @return 是否成功写出
* @since 4.3.2
*/
public static boolean write(final Image image, final ImageWriter writer, final ImageOutputStream output, final float quality) {
if (writer == null) {
return false;
}
writer.setOutput(output);
final RenderedImage renderedImage = toRenderedImage(image);
// 设置质量
ImageWriteParam imgWriteParams = null;
if (quality > 0 && quality < 1) {
imgWriteParams = writer.getDefaultWriteParam();
if (imgWriteParams.canWriteCompressed()) {
imgWriteParams.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
imgWriteParams.setCompressionQuality(quality);
final ColorModel colorModel = renderedImage.getColorModel();// ColorModel.getRGBdefault();
imgWriteParams.setDestinationType(new ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(16, 16)));
}
}
try {
if (null != imgWriteParams) {
writer.write(null, new IIOImage(renderedImage, null, null), imgWriteParams);
} else {
writer.write(renderedImage);
}
output.flush();
} catch (final IOException e) {
throw new IORuntimeException(e);
} finally {
writer.dispose();
}
return true;
public static void write(final Image image, final ImageOutputStream output, final float quality) {
ImgWriter.of(image, null)
.setQuality(quality)
.write(output);
}
/**
* 根据给定的Image对象和格式获取对应的{@link ImageWriter}如果未找到合适的Writer返回null
*
* @param img {@link Image}
* @param formatName 图片格式例如"jpg""png"
* @param formatName 图片格式例如"jpg""png"{@code null}则使用默认值"jpg"
* @return {@link ImageWriter}
* @since 4.3.2
*/
public static ImageWriter getWriter(final Image img, final String formatName) {
public static ImageWriter getWriter(final Image img, String formatName) {
if (null == formatName) {
formatName = IMAGE_TYPE_JPG;
}
final ImageTypeSpecifier type = ImageTypeSpecifier.createFromRenderedImage(toBufferedImage(img, formatName));
final Iterator<ImageWriter> iter = ImageIO.getImageWriters(type, formatName);
return iter.hasNext() ? iter.next() : null;
@ -1669,11 +1621,15 @@ public class ImgUtil {
/**
* 根据给定的图片格式或者扩展名获取{@link ImageWriter}如果未找到合适的Writer返回null
*
* @param formatName 图片格式或扩展名例如"jpg""png"
* @param formatName 图片格式或扩展名例如"jpg""png"{@code null}则使用默认值"jpg"
* @return {@link ImageWriter}
* @since 4.3.2
*/
public static ImageWriter getWriter(final String formatName) {
public static ImageWriter getWriter(String formatName) {
if (null == formatName) {
formatName = IMAGE_TYPE_JPG;
}
ImageWriter writer = null;
Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName(formatName);
if (iter.hasNext()) {

View File

@ -0,0 +1,151 @@
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.lang.Assert;
import javax.imageio.IIOImage;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import java.awt.Color;
import java.awt.Image;
import java.awt.image.ColorModel;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
/**
* 图片写出封装
*/
public class ImgWriter {
/**
* 创建图片写出器
*
* @param image 图片
* @param imageType 图片类型图片扩展名{@code null}表示使用RGB模式JPG
* @param backgroundColor 背景色{@link Color}{@code null}表示黑色或透明
* @return {@code ImgWriter}
*/
public static ImgWriter of(final Image image, final String imageType, final Color backgroundColor) {
return of(ImgUtil.toBufferedImage(image, imageType, backgroundColor), imageType);
}
/**
* 创建图片写出器
*
* @param image 图片
* @param imageType 图片类型图片扩展名{@code null}表示使用RGB模式JPG
* @return {@code ImgWriter}
*/
public static ImgWriter of(final Image image, final String imageType) {
return new ImgWriter(image, imageType);
}
private final RenderedImage image;
private final ImageWriter writer;
private ImageWriteParam writeParam;
/**
* 构造
*
* @param image {@link Image}
* @param imageType 图片类型图片扩展名{@code null}表示使用RGB模式JPG
*/
public ImgWriter(final Image image, final String imageType) {
this.image = ImgUtil.toRenderedImage(image);
this.writer = ImgUtil.getWriter(image, imageType);
}
/**
* 设置写出质量数字为0~1不包括0和1表示质量压缩比除此数字外设置表示不压缩
*
* @param quality 写出质量数字为0~1不包括0和1表示质量压缩比除此数字外设置表示不压缩
* @return this
*/
public ImgWriter setQuality(final float quality) {
this.writeParam = buildParam(this.image, this.writer, quality);
return this;
}
/**
* 写出图像GIF=JPGGIF=PNGPNG=JPGPNG=GIF(X)BMP=PNG<br>
* 此方法并不关闭流
*
* @param out 写出到的目标流
* @throws IORuntimeException IO异常
*/
public void write(final OutputStream out) throws IORuntimeException {
write(ImgUtil.getImageOutputStream(out));
}
/**
* 写出图像为目标文件扩展名对应的格式
*
* @param targetFile 目标文件
* @throws IORuntimeException IO异常
*/
public void write(final File targetFile) throws IORuntimeException {
FileUtil.touch(targetFile);
ImageOutputStream out = null;
try {
out = ImgUtil.getImageOutputStream(targetFile);
write(out);
} finally {
IoUtil.close(out);
}
}
/**
* 通过{@link ImageWriter}写出图片到输出流
*
* @param output 输出的Image流{@link ImageOutputStream} 非空
*/
public void write(final ImageOutputStream output) {
Assert.notNull(output);
final ImageWriter writer = this.writer;
final RenderedImage image = this.image;
writer.setOutput(output);
// 设置质量
try {
if (null != this.writeParam) {
writer.write(null, new IIOImage(image, null, null), this.writeParam);
} else {
writer.write(image);
}
output.flush();
} catch (final IOException e) {
throw new IORuntimeException(e);
} finally {
writer.dispose();
}
}
/**
* 构建图片写出参数
*
* @param renderedImage 图片
* @param writer {@link ImageWriter}
* @param quality 质量范围0~1
* @return {@link ImageWriteParam} or {@code null}
*/
private static ImageWriteParam buildParam(final RenderedImage renderedImage, final ImageWriter writer, final float quality) {
// 设置质量
ImageWriteParam imgWriteParams = null;
if (quality > 0 && quality < 1) {
imgWriteParams = writer.getDefaultWriteParam();
if (imgWriteParams.canWriteCompressed()) {
imgWriteParams.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
imgWriteParams.setCompressionQuality(quality);
final ColorModel colorModel = renderedImage.getColorModel();// ColorModel.getRGBdefault();
imgWriteParams.setDestinationType(new ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(16, 16)));
}
}
return imgWriteParams;
}
}