com.kqinfo.universal.imagecode.core.ImageCodeUtil Maven / Gradle / Ivy
The newest version!
package com.kqinfo.universal.imagecode.core;
import cn.hutool.core.img.GraphicsUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.util.RandomUtil;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import com.kqinfo.universal.imagecode.properties.ImageCodeProperties;
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Properties;
/**
* @author Zijian Liao
* @since 1.5.0
*/
@Slf4j
public class ImageCodeUtil {
/**
* 图片的宽度
*/
private static int width;
/**
* 图片的高度
*/
private static int height;
/**
* 验证码字符个数
*/
private static int codeCount;
private static String baseChars;
private static final String RANDOM_CODE = "ABCDEFGHJKMNPQRSTWXYZ123456789";
private static final DefaultKaptcha PRODUCER = new DefaultKaptcha();
private static final Properties properties = new Properties();
public ImageCodeUtil(ImageCodeProperties imageCodeProperties) {
ImageCodeUtil.width = imageCodeProperties.getWidth();
ImageCodeUtil.height = imageCodeProperties.getHeight();
ImageCodeUtil.codeCount = imageCodeProperties.getCodeCount();
ImageCodeUtil.baseChars = imageCodeProperties.getBaseChars();
init();
}
private static void init(){
// 无边框
properties.setProperty("kaptcha.border", "no");
// 验证码图片宽度 默认为200
properties.setProperty("kaptcha.image.width", String.valueOf(ImageCodeUtil.width));
// 验证码图片高度 默认为50
properties.setProperty("kaptcha.image.height", String.valueOf(ImageCodeUtil.height));
// 验证码文本字符大小 默认为40
int frontSize = (int) (ImageCodeUtil.height * 0.75);
properties.setProperty("kaptcha.textproducer.font.size", String.valueOf(frontSize));
// 验证码文本字符间距 默认为2
properties.setProperty("kaptcha.textproducer.char.space", "5");
// 验证码文本字符长度 默认为4
properties.setProperty("kaptcha.textproducer.char.length", String.valueOf(ImageCodeUtil.codeCount));
properties.setProperty("kaptcha.textproducer.font.names", "Arial,Courier");
// abcde2345678gfynmnpwx
properties.setProperty("kaptcha.textproducer.char.string", ImageCodeUtil.baseChars);
Config config = new Config(properties);
PRODUCER.setConfig(config);
}
private static void refresh() {
properties.setProperty("kaptcha.textproducer.font.color", getRandomColor());
properties.setProperty("kaptcha.noise.color", getRandomColor());
}
private static String getRandomColor(){
Color color = ImgUtil.randomColor();
return color.getRed() + "," + color.getGreen() + "," + color.getBlue();
}
public static ImageCode createImage() {
refresh();
String code = PRODUCER.createText();
BufferedImage image = PRODUCER.createImage(code);
return new ImageCode()
.setBufferedImage(image)
.setCode(code.toLowerCase());
}
@Deprecated
private static void doCreateImage(String code, BufferedImage image) {
final Graphics2D g = GraphicsUtil.createGraphics(image, Color.WHITE);
Font font = new Font(Font.SANS_SERIF, Font.BOLD, (int) (height * 0.75));
GraphicsUtil.drawStringColourful(g, code, font, width, height);
// 扭曲
shear(g, width, height, Color.WHITE);
// 画干扰线
// y2和y1应该在不同的部分,如y1在上部分,那么y2必须在下部分
// 位置标志,0上半部分 1下半部分
int indexFlag = RandomUtil.randomInt(2);
// 20
int halfHeight = height >> 1;
// 0~20 + 20*(0|1) = (0~20| 20~40)
int y1 = RandomUtil.randomInt(halfHeight) + halfHeight * indexFlag;
int y2 = RandomUtil.randomInt(halfHeight) + halfHeight * (1 - indexFlag);
drawInterfere(g, 0, y1, width, y2, 3, ImgUtil.randomColor());
}
public static void sendImage(BufferedImage bufferedImage, OutputStream outputStream) {
try {
ImageIO.write(bufferedImage, "png", outputStream);
} catch (IOException ignored) {
} finally {
if (outputStream != null) {
try {
outputStream.flush();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
try {
outputStream.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
}
/**
* 扭曲
*
* @param g {@link Graphics}
* @param w1 w1
* @param h1 h1
* @param color 颜色
*/
private static void shear(Graphics g, int w1, int h1, Color color) {
shearX(g, w1, h1, color);
shearY(g, w1, h1, color);
}
/**
* X坐标扭曲
*
* @param g {@link Graphics}
* @param w1 宽
* @param h1 高
* @param color 颜色
*/
private static void shearX(Graphics g, int w1, int h1, Color color) {
int period;
if (w1 > 20) {
// period不在0~5,0~5倾斜度不高
period = RandomUtil.randomInt(w1 - 10) + 5;
} else {
period = RandomUtil.randomInt(w1);
}
int frames = 1;
int phase = RandomUtil.randomInt(2);
for (int i = 0; i < h1; i++) {
double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
g.copyArea(0, i, w1, 1, (int) d, 0);
g.setColor(color);
g.drawLine((int) d, i, 0, i);
g.drawLine((int) d + w1, i, w1, i);
}
}
/**
* Y坐标扭曲
*
* @param g {@link Graphics}
* @param w1 宽
* @param h1 高
* @param color 颜色
*/
private static void shearY(Graphics g, int w1, int h1, Color color) {
int period;
if (w1 > 30) {
// period不在0~8,0~8倾斜度不高
period = RandomUtil.randomInt((h1 - 16) >> 1) + 8;
} else {
period = RandomUtil.randomInt(h1 >> 1);
}
int frames = 20;
int phase = 7;
for (int i = 0; i < w1; i++) {
double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
g.copyArea(i, 0, 1, h1, 0, (int) d);
g.setColor(color);
// 擦除原位置的痕迹
g.drawLine(i, (int) d, i, 0);
g.drawLine(i, (int) d + h1, i, h1);
}
}
/**
* 干扰线
*
* @param g {@link Graphics}
* @param x1 x1
* @param y1 y1
* @param x2 x2
* @param y2 y2
* @param thickness 粗细
* @param c 颜色
*/
@SuppressWarnings("SameParameterValue")
private static void drawInterfere(Graphics g, int x1, int y1, int x2, int y2, int thickness, Color c) {
// The thick line is in fact a filled polygon
g.setColor(c);
int dX = x2 - x1;
int dY = y2 - y1;
// line length
double lineLength = Math.sqrt(dX * dX + (double) (dY * dY));
double scale = (double) (thickness) / (2 * lineLength);
// The x and y increments from an endpoint needed to create a
// rectangle...
double ddx = -scale * (double) dY;
double ddy = scale * (double) dX;
ddx += (ddx > 0) ? 0.5 : -0.5;
ddy += (ddy > 0) ? 0.5 : -0.5;
int dx = (int) ddx;
int dy = (int) ddy;
// Now we can compute the corner points...
int[] xPoints = new int[4];
int[] yPoints = new int[4];
xPoints[0] = x1 + dx;
yPoints[0] = y1 + dy;
xPoints[1] = x1 - dx;
yPoints[1] = y1 - dy;
xPoints[2] = x2 - dx;
yPoints[2] = y2 - dy;
xPoints[3] = x2 + dx;
yPoints[3] = y2 + dy;
g.fillPolygon(xPoints, yPoints, 4);
}
}