U 解决 背景图为 png 设置指定转换类型为 jpg 导致的图片变色问题

This commit is contained in:
天爱有情
2022-05-09 11:48:43 +08:00
parent bbf65018ed
commit f51cd66a9b
6 changed files with 239 additions and 35 deletions
@@ -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.GenerateParam;
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo; import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
import cloud.tianai.captcha.generator.common.util.CaptchaImageUtils; 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 cloud.tianai.captcha.resource.common.model.dto.Resource;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Base64; import java.util.Base64;
import java.util.Map; import java.util.Map;
@@ -59,7 +58,7 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
* @param formatType 格式化类型 * @param formatType 格式化类型
* @return String * @return String
*/ */
@SneakyThrows(IOException.class) @SneakyThrows(Exception.class)
public String transform(BufferedImage bufferedImage, String formatType) { public String transform(BufferedImage bufferedImage, String formatType) {
// 这里判断处理一下,加一些警告日志 // 这里判断处理一下,加一些警告日志
String result = beforeTransform(bufferedImage, formatType); String result = beforeTransform(bufferedImage, formatType);
@@ -67,7 +66,9 @@ public abstract class AbstractImageCaptchaGenerator implements ImageCaptchaGener
return result; return result;
} }
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 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(); byte[] data = byteArrayOutputStream.toByteArray();
String base64 = Base64.getEncoder().encodeToString(data); String base64 = Base64.getEncoder().encodeToString(data);
@@ -9,9 +9,8 @@ import java.awt.geom.Area;
import java.awt.geom.CubicCurve2D; import java.awt.geom.CubicCurve2D;
import java.awt.geom.QuadCurve2D; import java.awt.geom.QuadCurve2D;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.PixelGrabber; import java.awt.image.PixelGrabber;
import java.awt.image.WritableRaster; import java.awt.image.RenderedImage;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
@@ -25,6 +24,10 @@ import java.util.concurrent.ThreadLocalRandom;
*/ */
public class CaptchaImageUtils { 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 @SneakyThrows
public static BufferedImage wrapFile2BufferedImage(URL resourceImage) { public static BufferedImage wrapFile2BufferedImage(URL resourceImage) {
if (resourceImage == null) { if (resourceImage == null) {
@@ -133,19 +136,6 @@ public class CaptchaImageUtils {
return rec; 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; return image;
} }
/**
* 旋转图片
*
* @param bufferedimage
* @param degree
* @return
*/
public static BufferedImage rotateImage(final BufferedImage bufferedimage, public static BufferedImage rotateImage(final BufferedImage bufferedimage,
final double degree) { 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 * 后缀是否是jpg
* *
@@ -470,7 +552,7 @@ public class CaptchaImageUtils {
* @return boolean * @return boolean
*/ */
public static boolean isJpeg(String type) { 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 * @return boolean
*/ */
public static boolean isPng(String type) { 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;
}
}
@@ -79,7 +79,7 @@ public class StandardRotateImageCaptchaGenerator extends AbstractImageCaptchaGen
inputStreams.add(resourceInputStream); inputStreams.add(resourceInputStream);
BufferedImage cutBackground = CaptchaImageUtils.wrapFile2BufferedImage(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); InputStream fixedTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME);
inputStreams.add(fixedTemplateInput); inputStreams.add(fixedTemplateInput);
@@ -69,7 +69,7 @@ public class StandardSliderImageCaptchaGenerator extends AbstractImageCaptchaGen
inputStreams.add(resourceInputStream); inputStreams.add(resourceInputStream);
BufferedImage cutBackground = CaptchaImageUtils.wrapFile2BufferedImage(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); InputStream fixedTemplateInput = getTemplateFile(templateImages, SliderCaptchaConstant.TEMPLATE_FIXED_IMAGE_NAME);
inputStreams.add(fixedTemplateInput); inputStreams.add(fixedTemplateInput);
+13 -11
View File
@@ -26,7 +26,7 @@ public class CaptchaGenTest {
ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager(); ImageCaptchaResourceManager imageCaptchaResourceManager = new DefaultImageCaptchaResourceManager();
ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore(); ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore();
// 添加一些系统的资源文件 // 添加一些系统的资源文件
resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource(FileResourceProvider.NAME, "C:\\Users\\Thinkpad\\Desktop\\111\\test.png")); resourceStore.addResource(CaptchaTypeConstant.SLIDER, new Resource(FileResourceProvider.NAME, "C:\\Users\\Thinkpad\\Desktop\\111\\66.jpg"));
// 添加一些系统的 模板文件 // 添加一些系统的 模板文件
Map<String, Resource> template1 = new HashMap<>(4); Map<String, Resource> template1 = new HashMap<>(4);
@@ -40,18 +40,20 @@ public class CaptchaGenTest {
MultiImageCaptchaGenerator imageCaptchaGenerator = new MultiImageCaptchaGenerator(imageCaptchaResourceManager, false); MultiImageCaptchaGenerator imageCaptchaGenerator = new MultiImageCaptchaGenerator(imageCaptchaResourceManager, false);
GenerateParam generateParam = GenerateParam.builder() GenerateParam generateParam = GenerateParam.builder()
.type(CaptchaTypeConstant.SLIDER) .type(CaptchaTypeConstant.SLIDER)
.backgroundFormatName("jpg") .backgroundFormatName("webp")
.sliderFormatName("png") .sliderFormatName("webp")
.obfuscate(false) .obfuscate(false)
.build(); .build();
ImageCaptchaInfo imageCaptchaInfo = imageCaptchaGenerator.generateCaptchaImage(generateParam); // for (int i = 0; i < 10; i++) {
System.out.println(imageCaptchaInfo.getBackgroundImage()); ImageCaptchaInfo imageCaptchaInfo = imageCaptchaGenerator.generateCaptchaImage(generateParam);
// System.out.println(imageCaptchaInfo.getSliderImage()); System.out.println(imageCaptchaInfo.getBackgroundImage());
System.out.println(imageCaptchaInfo.getSliderImage());
// 负责计算一些数据存到缓存中,用于校验使用 // 负责计算一些数据存到缓存中,用于校验使用
// ImageCaptchaValidator负责校验用户滑动滑块是否正确和生成滑块的一些校验数据; 比如滑块到凹槽的百分比值 // ImageCaptchaValidator负责校验用户滑动滑块是否正确和生成滑块的一些校验数据; 比如滑块到凹槽的百分比值
ImageCaptchaValidator imageCaptchaValidator = new BasicCaptchaTrackValidator(); ImageCaptchaValidator imageCaptchaValidator = new BasicCaptchaTrackValidator();
// 这个map数据应该存到缓存中,校验的时候需要用到该数据 // 这个map数据应该存到缓存中,校验的时候需要用到该数据
Map<String, Object> map = imageCaptchaValidator.generateImageCaptchaValidData(imageCaptchaInfo); Map<String, Object> map = imageCaptchaValidator.generateImageCaptchaValidData(imageCaptchaInfo);
// }
} }
} }