diff --git a/src/main/java/cloud/tianai/captcha/template/slider/common/util/CaptchaImageUtils.java b/src/main/java/cloud/tianai/captcha/template/slider/common/util/CaptchaImageUtils.java index 7b91d32..13800c6 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/common/util/CaptchaImageUtils.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/common/util/CaptchaImageUtils.java @@ -175,23 +175,6 @@ public class CaptchaImageUtils { return image; } - -// /** -// * 旋转图片 -// * -// * @param bufferedImage 图片 -// * @param degree 旋转xx度 -// */ -// public static void rotateImage(BufferedImage bufferedImage, int degree) { -// // 创建Graphics2D对象,用在底图对象上绘图 -// Graphics2D g2d = bufferedImage.createGraphics(); -// // 绘制 -// g2d.rotate(Math.toRadians(degree), bufferedImage.getWidth() / 2, bufferedImage.getHeight()/2); -// g2d.drawImage(bufferedImage, 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), null); -// // 释放图形上下文使用的系统资源 -// g2d.dispose(); -// } - public static BufferedImage rotateImage(final BufferedImage bufferedimage, final double degree) { // 得到图片宽度。 @@ -225,4 +208,102 @@ public class CaptchaImageUtils { overlayImage(baseBufferedImage, coverBufferedImage, bw / 2 - cw / 2, bh / 2 - ch / 2); } + + /** + * 通过x和y轴截取图片 + * + * @param x x + * @param y y + * @param width 宽度 + * @param height 高度 + * @param img 截取的图片 + * @return BufferedImage + */ + public static BufferedImage subImage(int x, int y, int width, int height, BufferedImage img) { + int[] simgRgb = new int[width * height]; + img.getRGB(x, y, width, height, simgRgb, 0, width); + // 得到图片透明度。 + int type = img.getColorModel().getTransparency(); + BufferedImage newImage = new BufferedImage(width, height, type); + newImage.setRGB(0, 0, width, height, simgRgb, 0, width); + return newImage; + } + + /** + * 分隔图片 + * + * @param pos 分隔点 + * @param direction true为水平方向, false为垂直方向 + * @param img 待分割的图片 + * @return BufferedImage[] + */ + public static BufferedImage[] splitImage(int pos, boolean direction, BufferedImage img) { + int startImageWidth; + int startImageHeight; + int endImageWidth; + int endImageHeight; + int endScanX; + int endScanY; + if (direction) { + startImageHeight = img.getHeight() - pos; + startImageWidth = img.getWidth(); + endImageWidth = img.getWidth(); + endImageHeight = pos; + endScanX = 0; + endScanY = startImageHeight; + } else { + startImageWidth = pos; + startImageHeight = img.getHeight(); + endImageWidth = img.getWidth() - startImageWidth; + endImageHeight = img.getHeight(); + endScanX = pos; + endScanY = 0; + } + + // start + int[] rgbArr = new int[startImageWidth * startImageHeight]; + img.getRGB(0, 0, startImageWidth, startImageHeight, rgbArr, 0, startImageWidth); + int type = img.getColorModel().getTransparency(); + BufferedImage startImg = new BufferedImage(startImageWidth, startImageHeight, type); + startImg.setRGB(0, 0, startImageWidth, startImageHeight, rgbArr, 0, startImageWidth); + // end + rgbArr = new int[endImageWidth * endImageHeight]; + img.getRGB(endScanX, endScanY, endImageWidth, endImageHeight, rgbArr, 0, endImageWidth); + BufferedImage endImg = new BufferedImage(endImageWidth, endImageHeight, type); + endImg.setRGB(0, 0, endImageWidth, endImageHeight, rgbArr, 0, endImageWidth); + + BufferedImage[] splitImageArr = new BufferedImage[2]; + splitImageArr[0] = startImg; + splitImageArr[1] = endImg; + return splitImageArr; + } + + + /** + * 拼接图片 + * + * @param direction rue为水平方向, false为垂直方向 + * @param width 拼接后图片宽度 + * @param height 拼接后图片高度 + * @param imgArr 拼接的图片数组 + * @return BufferedImage + */ + public static BufferedImage concatImage(boolean direction, int width, int height, BufferedImage... imgArr) { + int pos = 0; + BufferedImage newImage = new BufferedImage(width, height, imgArr[0].getColorModel().getTransparency()); + for (BufferedImage img : imgArr) { + int[] rgbArr = new int[width * height]; + img.getRGB(0, 0, img.getWidth(), img.getHeight(), rgbArr, 0, img.getWidth()); + if (direction) { + newImage.setRGB(pos, 0, img.getWidth(), img.getHeight(), rgbArr, 0, img.getWidth()); + pos += img.getWidth(); + // 水平方向 + } else { + // 垂直方向 + newImage.setRGB(0, pos, img.getWidth(), img.getHeight(), rgbArr, 0, img.getWidth()); + pos += img.getHeight(); + } + } + return newImage; + } } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/constant/CaptchaTypeConstant.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/constant/CaptchaTypeConstant.java index c50e1ce..6ca3fbb 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/common/constant/CaptchaTypeConstant.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/common/constant/CaptchaTypeConstant.java @@ -11,4 +11,6 @@ public interface CaptchaTypeConstant { String SLIDER = "SLIDER"; /** 旋转. */ String ROTATE = "ROTATE"; + /** 拼接.*/ + String CONCAT = "CONCAT"; } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/MultiImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/MultiImageCaptchaGenerator.java index 9faa162..76e8dbd 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/MultiImageCaptchaGenerator.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/MultiImageCaptchaGenerator.java @@ -31,8 +31,12 @@ public class MultiImageCaptchaGenerator extends AbstractImageCaptchaGenerator { } protected void init() { + // 滑块验证码 addImageCaptchaGenerator(CaptchaTypeConstant.SLIDER, new StandardSliderImageCaptchaGenerator(imageCaptchaResourceManager, initDefaultResource)); + // 旋转验证码 addImageCaptchaGenerator(CaptchaTypeConstant.ROTATE, new StandardRotateImageCaptchaGenerator(imageCaptchaResourceManager, initDefaultResource)); + // 拼接验证码 + addImageCaptchaGenerator(CaptchaTypeConstant.CONCAT, new StandardConcatImageCaptchaGenerator(imageCaptchaResourceManager, initDefaultResource)); } public void addImageCaptchaGenerator(String key, ImageCaptchaGenerator captchaGenerator) { diff --git a/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardConcatImageCaptchaGenerator.java b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardConcatImageCaptchaGenerator.java new file mode 100644 index 0000000..da1ea4a --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/generator/impl/StandardConcatImageCaptchaGenerator.java @@ -0,0 +1,89 @@ +package cloud.tianai.captcha.template.slider.generator.impl; + +import cloud.tianai.captcha.template.slider.generator.AbstractImageCaptchaGenerator; +import cloud.tianai.captcha.template.slider.generator.common.constant.CaptchaTypeConstant; +import cloud.tianai.captcha.template.slider.generator.common.model.dto.GenerateParam; +import cloud.tianai.captcha.template.slider.generator.common.model.dto.ImageCaptchaInfo; +import cloud.tianai.captcha.template.slider.resource.ImageCaptchaResourceManager; +import cloud.tianai.captcha.template.slider.resource.ResourceStore; +import cloud.tianai.captcha.template.slider.resource.common.model.dto.Resource; +import cloud.tianai.captcha.template.slider.resource.impl.provider.ClassPathResourceProvider; +import lombok.SneakyThrows; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; +import java.util.LinkedList; +import java.util.concurrent.ThreadLocalRandom; + +import static cloud.tianai.captcha.template.slider.common.util.CaptchaImageUtils.*; +import static cloud.tianai.captcha.template.slider.generator.impl.StandardSliderImageCaptchaGenerator.DEFAULT_SLIDER_IMAGE_RESOURCE_PATH; + +/** + * @Author: 天爱有情 + * @date 2022/4/25 15:44 + * @Description 图片拼接滑动验证码生成器 + */ +public class StandardConcatImageCaptchaGenerator extends AbstractImageCaptchaGenerator { + + protected final ImageCaptchaResourceManager imageCaptchaResourceManager; + + public StandardConcatImageCaptchaGenerator(ImageCaptchaResourceManager imageCaptchaResourceManager, boolean initDefaultResource) { + this.imageCaptchaResourceManager = imageCaptchaResourceManager; + if (initDefaultResource) { + initDefaultResource(); + } + } + + public void initDefaultResource() { + ResourceStore resourceStore = imageCaptchaResourceManager.getResourceStore(); + // 添加一些系统的资源文件 + resourceStore.addResource(CaptchaTypeConstant.CONCAT, new Resource(ClassPathResourceProvider.NAME, DEFAULT_SLIDER_IMAGE_RESOURCE_PATH.concat("/1.jpg"))); + } + + @Override + public ImageCaptchaInfo generateCaptchaImage(GenerateParam param) { + // 拼接验证码不需要模板 只需要背景图 + Collection inputStreams = new LinkedList<>(); + try { + Resource resourceImage = imageCaptchaResourceManager.randomGetResource(param.getType()); + InputStream resourceInputStream = imageCaptchaResourceManager.getResourceInputStream(resourceImage); + inputStreams.add(resourceInputStream); + BufferedImage bgImage = wrapFile2BufferedImage(resourceInputStream); + int spacing = bgImage.getHeight() / 4; + BufferedImage[] bgImageSplit = splitImage(ThreadLocalRandom.current().nextInt(spacing, bgImage.getHeight() - spacing), true, bgImage); + + spacing = bgImage.getWidth() / 8; + int randomX = ThreadLocalRandom.current().nextInt(spacing, bgImage.getWidth() - spacing); + BufferedImage[] bgImageTopSplit = splitImage(randomX, false, bgImageSplit[0]); + + BufferedImage sliderImage = concatImage(true, + bgImageTopSplit[0].getWidth() + + bgImageTopSplit[1].getWidth() + + bgImageSplit[0].getWidth(), bgImageTopSplit[1].getHeight(), bgImageSplit[0], bgImageTopSplit[0]); + return wrapConcatCaptchaInfo(randomX, bgImageSplit[1], sliderImage, param); + } finally { + // 使用完后关闭流 + for (InputStream inputStream : inputStreams) { + try { + inputStream.close(); + } catch (IOException e) { + // ignore + } + } + } + } + + @SneakyThrows + private ImageCaptchaInfo wrapConcatCaptchaInfo(int randomX, BufferedImage bgImage, BufferedImage sliderImage, GenerateParam param) { + String backGroundImageBase64 = transform(bgImage, param.getBackgroundFormatName()); + String sliderImageBase64 = transform(sliderImage, param.getSliderFormatName()); + return ImageCaptchaInfo.of(backGroundImageBase64, sliderImageBase64, bgImage.getWidth(), bgImage.getHeight(), sliderImage.getWidth(), sliderImage.getHeight(), randomX); + } + + @Override + public ImageCaptchaResourceManager getImageResourceManager() { + return imageCaptchaResourceManager; + } +}