1、生成前端小图片,以及对于大图片加背景颜色
package com.stock.core.util;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import sun.misc.BASE64Encoder;
/**
* 随机切割图片工具类
*
* @Author: liuqiaoyue
* @Date: 2022-05-13 13:32
* @Version 1.0
*/
public class RandomCutPicUtils {
/**
* 背景图的宽、高
*/
private static final int BG_WIDTH = 590;
private static final int BG_HEIGHT = 360;
/**
* 模板图的宽、高
*/
private static final int TEMPLATE_WIDTH = 120;
private static final int TEMPLATE_HEIGHT = 100;
/**
* 圆的半径
*/
private static final int CIRCLE_RADIO = 20;
/**
* 抠图的边框宽度
*/
private static int SLIDER_IMG_OUT_PADDING = 1;
// --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- //
/**
* 获取大图,小图
* @param bgPathInputStream 背景图文件
* @return
* @throws Exception
*/
public static Map<String, Object> generateTwoImage(InputStream bgPathInputStream) throws Exception {
// public static Map<String, Object> generateTwoImage(HttpSession session, HttpServletResponse response, File bgPath) throws Exception {
Map<String, Integer> startPoint = getStartPoint();
int xBegin = startPoint.get("xBegin");
int yBegin = startPoint.get("yBegin");
// 抠图轮廓
int[][] templateImage = getBlockData();
// 原始背景图
BufferedImage bgImage = ImageIO.read(bgPathInputStream);
// 抠图(空白)
BufferedImage blankImage = new BufferedImage(TEMPLATE_WIDTH, TEMPLATE_HEIGHT,
BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D graphics = blankImage.createGraphics();
graphics.setBackground(Color.white);
cutByTemplate(bgImage, blankImage, templateImage, xBegin, yBegin);
// “抗锯齿”的属性
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setStroke(new BasicStroke(5, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
graphics.drawImage(blankImage, 0, 0, null);
graphics.dispose();
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("image", getImageBASE64(bgImage));//大图
resultMap.put("image2", getImageBASE64(blankImage)); //小图
resultMap.put("xBegin",xBegin);
resultMap.put("yBegin", yBegin);
return resultMap;
}
/**
* 获取大图,小图Base64码
*
* @param bgPath 背景图路径
*/
public static Map<String, String> generateTwoImageBase64(String bgPath)
throws Exception {
Map<String, Integer> startPoint = getStartPoint();
int xBegin = startPoint.get("xBegin");
int yBegin = startPoint.get("yBegin");
// 抠图轮廓
int[][] templateImage = getBlockData();
// 原始背景图
BufferedImage bgImage = ImageIO.read(new FileInputStream(bgPath));
// 空白抠图
BufferedImage blankImage = new BufferedImage(TEMPLATE_WIDTH, TEMPLATE_HEIGHT,
BufferedImage.TYPE_4BYTE_ABGR);
cutByTemplate(bgImage, blankImage, templateImage, xBegin, yBegin);
Map<String, String> resultMap = new HashMap<>(2);
// 大图
resultMap.put("big", getImageBASE64(bgImage));
// 小图
resultMap.put("small", getImageBASE64(blankImage));
return resultMap;
}
/**
* 获取切图在背景图中的 开始点
*/
private static Map<String, Integer> getStartPoint() {
Random random = new Random();
int xBegin = random.nextInt(BG_WIDTH - 3*TEMPLATE_WIDTH) + 2*TEMPLATE_WIDTH + 5;
int yBegin = TEMPLATE_HEIGHT + random.nextInt(BG_HEIGHT - TEMPLATE_HEIGHT * 2);
Map<String, Integer> map = new HashMap<>(2);
map.put("xBegin", xBegin);
map.put("yBegin", yBegin);
return map;
}
/**
* 生成随机滑块形状
* <p>
* 0 透明像素
* 1 滑块像素
* 2 阴影像素
*
* @return int[][]
*/
private static int[][] getBlockData() {
int[][] data = new int[TEMPLATE_WIDTH][TEMPLATE_HEIGHT];
Random random = new Random();
//(x-a)²+(y-b)²=r²
//x中心位置左右5像素随机
double x1 = CIRCLE_RADIO + (TEMPLATE_WIDTH - 2 * CIRCLE_RADIO) / 2.0 - 5 + random.nextInt(10);
//y 矩形上边界半径-1像素移动
double y1_top = CIRCLE_RADIO - random.nextInt(3);
double y1_bottom = TEMPLATE_HEIGHT - CIRCLE_RADIO + random.nextInt(3);
double y1 = random.nextInt(2) == 1 ? y1_top : y1_bottom;
double x2_right = TEMPLATE_WIDTH - CIRCLE_RADIO - CIRCLE_RADIO + random.nextInt(2 * CIRCLE_RADIO - 4);
double x2_left = CIRCLE_RADIO + CIRCLE_RADIO - 2 - random.nextInt(2 * CIRCLE_RADIO - 4);
double x2 = random.nextInt(2) == 1 ? x2_right : x2_left;
double y2 = CIRCLE_RADIO + (TEMPLATE_HEIGHT - 2 * CIRCLE_RADIO) / 2.0 - 4 + random.nextInt(10);
double po = Math.pow(CIRCLE_RADIO, 2);
for (int i = 0; i < TEMPLATE_WIDTH; i++) {
for (int j = 0; j < TEMPLATE_HEIGHT; j++) {
//矩形区域
boolean fill;
if ((i >= CIRCLE_RADIO && i < TEMPLATE_WIDTH - CIRCLE_RADIO)
&& (j >= CIRCLE_RADIO && j < TEMPLATE_HEIGHT - CIRCLE_RADIO)) {
data[i][j] = 1;
fill = true;
} else {
data[i][j] = 0;
fill = false;
}
//凸出区域
double d3 = Math.pow(i - x1, 2) + Math.pow(j - y1, 2);
if (d3 < po) {
data[i][j] = 1;
} else {
if (!fill) {
data[i][j] = 0;
}
}
//凹进区域
double d4 = Math.pow(i - x2, 2) + Math.pow(j - y2, 2);
if (d4 < po) {
data[i][j] = 0;
}
}
}
//边界阴影
for (int i = 0; i < TEMPLATE_WIDTH; i++) {
for (int j = 0; j < TEMPLATE_HEIGHT; j++) {
//四个正方形边角处理
for (int k = 1; k <= SLIDER_IMG_OUT_PADDING; k++) {
//左上、右上
if (i >= CIRCLE_RADIO - k && i < CIRCLE_RADIO
&& ((j >= CIRCLE_RADIO - k && j < CIRCLE_RADIO)
|| (j >= TEMPLATE_HEIGHT - CIRCLE_RADIO - k && j < TEMPLATE_HEIGHT - CIRCLE_RADIO + 1))) {
data[i][j] = 2;
}
//左下、右下
if (i >= TEMPLATE_WIDTH - CIRCLE_RADIO + k - 1 && i < TEMPLATE_WIDTH - CIRCLE_RADIO + 1) {
for (int n = 1; n <= SLIDER_IMG_OUT_PADDING; n++) {
if (((j >= CIRCLE_RADIO - n && j < CIRCLE_RADIO)
|| (j >= TEMPLATE_HEIGHT - CIRCLE_RADIO - n && j <= TEMPLATE_HEIGHT - CIRCLE_RADIO))) {
data[i][j] = 2;
}
}
}
}
if (data[i][j] == 1 && j - SLIDER_IMG_OUT_PADDING > 0 && data[i][j - SLIDER_IMG_OUT_PADDING] == 0) {
data[i][j - SLIDER_IMG_OUT_PADDING] = 2;
}
//解决凸出圆弧没有上/下边框问题
if (data[i][j] == 1 && j + SLIDER_IMG_OUT_PADDING > 0 && j + SLIDER_IMG_OUT_PADDING <= TEMPLATE_HEIGHT) {
if (j == 0 || (j+1) == TEMPLATE_HEIGHT){
data[i][j] = 2;
}else {
if (j + SLIDER_IMG_OUT_PADDING < TEMPLATE_HEIGHT && data[i][j + SLIDER_IMG_OUT_PADDING] == 0) {
data[i][j + SLIDER_IMG_OUT_PADDING] = 2;
}
}
}
if (data[i][j] == 1 && i - SLIDER_IMG_OUT_PADDING > 0 && data[i - SLIDER_IMG_OUT_PADDING][j] == 0) {
data[i - SLIDER_IMG_OUT_PADDING][j] = 2;
}
if (data[i][j] == 1 && i + SLIDER_IMG_OUT_PADDING > 0 && i + SLIDER_IMG_OUT_PADDING < TEMPLATE_WIDTH && data[i + SLIDER_IMG_OUT_PADDING][j] == 0) {
data[i + SLIDER_IMG_OUT_PADDING][j] = 2;
}
}
}
return data;
}
/**
* 生成小图片、给大图片添加阴影
*
* @param bgImage 背景图
* @param blankImage 抠图(空白)
* @param templateImage 抠图模板
* @param x 抠图开始坐标
* @param y 抠图开始坐标
*/
private static void cutByTemplate(BufferedImage bgImage, BufferedImage blankImage,
int[][] templateImage, int x, int y) {
for (int i = 0; i < TEMPLATE_WIDTH; i++) {
for (int j = 0; j < TEMPLATE_HEIGHT; j++) {
int _x = x + i;
int _y = y + j;
int rgbFlg = templateImage[i][j];
int rgb_ori = bgImage.getRGB(_x, _y);
// 原图中对应位置变色处理
if (rgbFlg == 1) {
//抠图上复制对应颜色值
blankImage.setRGB(i, j, rgb_ori);
//原图对应位置颜色变化
bgImage.setRGB(_x, _y, rgb_ori & 0x363636);
} else if (rgbFlg == 2) {
blankImage.setRGB(i, j, Color.WHITE.getRGB());
bgImage.setRGB(_x, _y, Color.GRAY.getRGB());
} else if (rgbFlg == 0) {
//int alpha = 0;
blankImage.setRGB(i, j, rgb_ori & 0x00ffffff);
}
}
}
}
/**
* 获取大图,小图
*
* @param bgPath 背景图路径
*/
public static Map<String, BufferedImage> generateTwoImage(HttpSession session, HttpServletResponse response, String bgPath) throws Exception {
Map<String, Integer> startPoint = getStartPoint();
int xBegin = startPoint.get("xBegin");
int yBegin = startPoint.get("yBegin");
// 抠图轮廓
int[][] templateImage = getBlockData();
// 原始背景图
BufferedImage bgImage = ImageIO.read(new FileInputStream(bgPath));
// 抠图(空白)
BufferedImage blankImage = new BufferedImage(TEMPLATE_WIDTH, TEMPLATE_HEIGHT,
BufferedImage.TYPE_4BYTE_ABGR);
cutByTemplate(bgImage, blankImage, templateImage, xBegin, yBegin);
Map<String, BufferedImage> resultMap = new HashMap<>(2);
//大图
resultMap.put("big", bgImage);
//小图
resultMap.put("small", blankImage);
return resultMap;
}
/**
* 图片转BASE64
*/
private static String getImageBASE64(BufferedImage image) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ImageIO.write(image, "png", out);
//转成byte数组
byte[] b = out.toByteArray();
BASE64Encoder encoder = new BASE64Encoder();
//生成base64编码
return encoder.encode(b);
}
/**
* 图片文件输出
*
* @param image 图
* @param imagePath 图的输出路径
*/
public static void outputImageFile(BufferedImage image, String imagePath) throws Exception {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(image, "png", os);
byte[] newImages = os.toByteArray();
FileOutputStream fos = new FileOutputStream(imagePath);
fos.write(newImages);
fos.close();
}
/**
* 图片文件输出
*
* @param image 图
* @param imagePath 图的输出路径
*/
public static void outputImageFile(BufferedImage image, File imagePath) throws Exception {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(image, "png", os);
byte[] newImages = os.toByteArray();
FileOutputStream fos = new FileOutputStream(imagePath);
fos.write(newImages);
fos.close();
}
}
2、请求地址调用以base64方式传到前端。
最好选择传到文件服务器或者其他更好的方式。两张图片的base64很大,传输比较耗时。其次,如果请求验证码的接口被攻击,base64传输图片的方式会照成服务器瘫痪。
@PostMapping("/getImg")
@ResponseBody
public JsonResponse<Map<String, Object>> createCodePlus() throws Exception {
JsonResponse<Map<String, Object>> responseTemp = new JsonResponse<Map<String, Object>>();
Map<String, Object> rtnMap = new HashMap<String, Object>();
//随机选择目标图片
Random randomImages = new Random();
String picPath = "templates/randomImg/" + randomImages.nextInt(20) + ".png";
picPath = URLDecoder.decode(picPath,"utf-8");
Resource resource = new ClassPathResource(picPath);
//获取坐标、大小图片
rtnMap = RandomCutPicUtils.generateTwoImage(resource.getInputStream());
rtnMap.remove("xBegin");
responseTemp.setResult(rtnMap);
return responseTemp;
}