mirror of
https://github.com/dromara/tianai-captcha.git
synced 2026-05-07 14:13:05 +08:00
U 解决 背景图为 png 设置指定转换类型为 jpg 导致的图片变色问题
This commit is contained in:
@@ -3,16 +3,15 @@ package cloud.tianai.captcha.generator;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
|
||||
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils;
|
||||
import cloud.tianai.captcha.generator.common.util.ImgWriter;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
@@ -59,7 +58,7 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
|
||||
* @param formatType 格式化类型
|
||||
* @return String
|
||||
*/
|
||||
@SneakyThrows(IOException.class)
|
||||
@SneakyThrows(Exception.class)
|
||||
public String transform(BufferedImage bufferedImage, String formatType) {
|
||||
// 这里判断处理一下,加一些警告日志
|
||||
String result = beforeTransform(bufferedImage, formatType);
|
||||
@@ -67,7 +66,9 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
|
||||
return result;
|
||||
}
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
ImageIO.write(bufferedImage, formatType, byteArrayOutputStream);
|
||||
long currentTimeMillis = System.currentTimeMillis();
|
||||
ImgWriter.write(bufferedImage, formatType, byteArrayOutputStream, -1);
|
||||
System.out.println("耗时:" + (System.currentTimeMillis() - currentTimeMillis));
|
||||
//转换成字节码
|
||||
byte[] data = byteArrayOutputStream.toByteArray();
|
||||
String base64 = Base64.getEncoder().encodeToString(data);
|
||||
|
||||
@@ -9,9 +9,8 @@ import java.awt.geom.Area;
|
||||
import java.awt.geom.CubicCurve2D;
|
||||
import java.awt.geom.QuadCurve2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.PixelGrabber;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
@@ -25,6 +24,10 @@ import java.util.concurrent.ThreadLocalRandom;
|
||||
*/
|
||||
public class CaptchaImageUtils {
|
||||
|
||||
public static final String TYPE_JPG = "jpg";
|
||||
public static final String TYPE_JPEG = "jpeg";
|
||||
public static final String TYPE_PNG = "png";
|
||||
|
||||
@SneakyThrows
|
||||
public static BufferedImage wrapFile2BufferedImage(URL resourceImage) {
|
||||
if (resourceImage == null) {
|
||||
@@ -133,19 +136,6 @@ public class CaptchaImageUtils {
|
||||
return rec;
|
||||
}
|
||||
|
||||
/**
|
||||
* 深度拷贝图片
|
||||
*
|
||||
* @param bi 原图片
|
||||
* @return BufferedImage
|
||||
*/
|
||||
public static BufferedImage deepCopyBufferedImage(BufferedImage bi) {
|
||||
ColorModel cm = bi.getColorModel();
|
||||
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
|
||||
WritableRaster raster = bi.copyData(bi.getRaster().createCompatibleWritableRaster());
|
||||
return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过模板图片抠图(不透明部分)
|
||||
*
|
||||
@@ -180,6 +170,13 @@ public class CaptchaImageUtils {
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* 旋转图片
|
||||
*
|
||||
* @param bufferedimage
|
||||
* @param degree
|
||||
* @return
|
||||
*/
|
||||
public static BufferedImage rotateImage(final BufferedImage bufferedimage,
|
||||
final double degree) {
|
||||
// 得到图片宽度。
|
||||
@@ -463,6 +460,91 @@ public class CaptchaImageUtils {
|
||||
|
||||
}
|
||||
|
||||
public static RenderedImage toRenderedImage(Image img) {
|
||||
if (img instanceof RenderedImage) {
|
||||
return (RenderedImage) img;
|
||||
}
|
||||
return copyImage(img, BufferedImage.TYPE_INT_RGB);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换成指定类型的 BufferedImage
|
||||
*
|
||||
* @param image image
|
||||
* @param imageType imageType
|
||||
* @return BufferedImage
|
||||
*/
|
||||
public static BufferedImage toBufferedImage(Image image, String imageType) {
|
||||
final int type = TYPE_PNG.equalsIgnoreCase(imageType)
|
||||
? BufferedImage.TYPE_INT_ARGB
|
||||
: BufferedImage.TYPE_INT_RGB;
|
||||
return toBufferedImage(image, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换成指定类型的 BufferedImage
|
||||
*
|
||||
* @param image image
|
||||
* @param imageType imageType
|
||||
* @return BufferedImage
|
||||
*/
|
||||
public static BufferedImage toBufferedImage(Image image, int imageType) {
|
||||
BufferedImage bufferedImage;
|
||||
if (image instanceof BufferedImage) {
|
||||
bufferedImage = (BufferedImage) image;
|
||||
if (imageType != bufferedImage.getType()) {
|
||||
bufferedImage = copyImage(image, imageType);
|
||||
}
|
||||
} else {
|
||||
bufferedImage = copyImage(image, imageType);
|
||||
}
|
||||
return bufferedImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝图片
|
||||
*
|
||||
* @param img img
|
||||
* @param imageType imageType
|
||||
* @return BufferedImage
|
||||
*/
|
||||
public static BufferedImage copyImage(Image img, int imageType) {
|
||||
return copyImage(img, imageType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝图片
|
||||
*
|
||||
* @param img img
|
||||
* @param imageType imageType
|
||||
* @param backgroundColor backgroundColor
|
||||
* @return BufferedImage
|
||||
*/
|
||||
public static BufferedImage copyImage(Image img, int imageType, Color backgroundColor) {
|
||||
final BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), imageType);
|
||||
final Graphics2D bGr = createGraphics(bimage, backgroundColor);
|
||||
bGr.drawImage(img, 0, 0, null);
|
||||
bGr.dispose();
|
||||
return bimage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建画板
|
||||
*
|
||||
* @param image image
|
||||
* @param color color
|
||||
* @return Graphics2D
|
||||
*/
|
||||
public static Graphics2D createGraphics(BufferedImage image, Color color) {
|
||||
final Graphics2D g = image.createGraphics();
|
||||
if (null != color) {
|
||||
// 填充背景
|
||||
g.setColor(color);
|
||||
g.fillRect(0, 0, image.getWidth(), image.getHeight());
|
||||
}
|
||||
return g;
|
||||
}
|
||||
|
||||
/**
|
||||
* 后缀是否是jpg
|
||||
*
|
||||
@@ -470,7 +552,7 @@ public class CaptchaImageUtils {
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean isJpeg(String type) {
|
||||
return "jpg".equalsIgnoreCase(type) || "jpeg".equalsIgnoreCase(type);
|
||||
return TYPE_JPG.equalsIgnoreCase(type) || TYPE_JPEG.equalsIgnoreCase(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -480,7 +562,6 @@ public class CaptchaImageUtils {
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean isPng(String type) {
|
||||
return "png".equalsIgnoreCase(type);
|
||||
return TYPE_PNG.equalsIgnoreCase(type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
package cloud.tianai.captcha.generator.common.util;
|
||||
|
||||
import cloud.tianai.captcha.common.util.ObjectUtils;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2022/5/9 11:47
|
||||
* @Description 拷贝from hutool(https://gitee.com/dromara/hutool/blob/v5-master/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java)
|
||||
* 为了不依赖更多无用包, 单独拷贝出来
|
||||
*/
|
||||
public class ImgWriter {
|
||||
|
||||
/**
|
||||
* 输出
|
||||
*
|
||||
* @param image image
|
||||
* @param imageType imageType
|
||||
* @param destImageStream destImageStream
|
||||
* @param quality quality 0~1
|
||||
* @return
|
||||
*/
|
||||
public static boolean write(Image image, String imageType, OutputStream destImageStream, float quality) {
|
||||
if (ObjectUtils.isEmpty(imageType)) {
|
||||
imageType = CaptchaImageUtils.TYPE_JPG;
|
||||
}
|
||||
ImageOutputStream imageOutputStream = transformImageOutputStream(destImageStream);
|
||||
final BufferedImage bufferedImage = CaptchaImageUtils.toBufferedImage(image, imageType);
|
||||
final ImageWriter writer = getWriter(bufferedImage, imageType);
|
||||
return write(bufferedImage, writer, imageOutputStream, quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出
|
||||
*
|
||||
* @param image image
|
||||
* @param writer writer
|
||||
* @param output output
|
||||
* @param quality quality
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean write(Image image, ImageWriter writer, ImageOutputStream output, 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 (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
writer.dispose();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static RenderedImage toRenderedImage(Image img) {
|
||||
if (img instanceof RenderedImage) {
|
||||
return (RenderedImage) img;
|
||||
}
|
||||
return CaptchaImageUtils.copyImage(img, BufferedImage.TYPE_INT_RGB);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 ImageWriter
|
||||
*
|
||||
* @param img img
|
||||
* @param formatName formatName
|
||||
* @return ImageWriter
|
||||
*/
|
||||
public static ImageWriter getWriter(Image img, String formatName) {
|
||||
final ImageTypeSpecifier type = ImageTypeSpecifier.createFromRenderedImage(CaptchaImageUtils.toBufferedImage(img, formatName));
|
||||
final Iterator<ImageWriter> iter = ImageIO.getImageWriters(type, formatName);
|
||||
return iter.hasNext() ? iter.next() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 OutputStream 转换为 ImageOutputStream
|
||||
*
|
||||
* @param out out
|
||||
* @return ImageOutputStream
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
@SneakyThrows(IOException.class)
|
||||
public static ImageOutputStream transformImageOutputStream(OutputStream out) throws RuntimeException {
|
||||
ImageOutputStream result = ImageIO.createImageOutputStream(out);
|
||||
if (null == result) {
|
||||
throw new IllegalArgumentException("Image type is not supported!");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -79,7 +79,7 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
|
||||
inputStreams.add(resourceInputStream);
|
||||
BufferedImage cutBackground = CaptchaImageUtils.wrapFile2BufferedImage(resourceInputStream);
|
||||
// 拷贝一份图片
|
||||
BufferedImage targetBackground = CaptchaImageUtils.deepCopyBufferedImage(cutBackground);
|
||||
BufferedImage targetBackground = CaptchaImageUtils.copyImage(cutBackground, cutBackground.getType());
|
||||
|
||||
InputStream fixedTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME);
|
||||
inputStreams.add(fixedTemplateInput);
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
|
||||
inputStreams.add(resourceInputStream);
|
||||
BufferedImage cutBackground = CaptchaImageUtils.wrapFile2BufferedImage(resourceInputStream);
|
||||
// 拷贝一份图片
|
||||
BufferedImage targetBackground = CaptchaImageUtils.deepCopyBufferedImage(cutBackground);
|
||||
BufferedImage targetBackground = CaptchaImageUtils.copyImage(cutBackground, cutBackground.getType());
|
||||
|
||||
InputStream fixedTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME);
|
||||
inputStreams.add(fixedTemplateInput);
|
||||
|
||||
Reference in New Issue
Block a user