美文网首页
java实现拼图验证码

java实现拼图验证码

作者: 半杯红碎茶 | 来源:发表于2017-12-15 17:35 被阅读0次

    最近项目需要将原有的图形验证码更换为拼图验证,开始去网上搜了一些相关的DEMO,发现做这个的DEMO很少。不得已,只能自己慢慢摸索啦。 经过几天的坑,算是基本完成了需求。现分享下实现的过程

    DEMO效果图如下

    demo效果

    1:首先需要一些模板图, 固定尺寸,下边demo项目中固定为260*116的尺寸。

    原图

    2:通过原图,在随机位置生成小方格,并根据小方格生成底图,底图由小方格的灰度化之后,绘制到原图上。

    private Map<String, String> createImg(File file, String filename) throws IOException {
            BufferedImage sourceBuff = ImageIO.read(file);
            int width = sourceBuff.getWidth();
            int height = sourceBuff.getHeight();
            //生成随机x,y
            Random random = new Random();
            //X轴距离右端tailoring_w 以上)  Y轴距离底部tailoring_y以上
            this.location_x = random.nextInt(width - tailoring_w * 2) + tailoring_w;
            this.location_y = random.nextInt(height - tailoring_h);
            //裁剪小图
            BufferedImage sourceSmall = cutImg(file, location_x, location_y, tailoring_w, tailoring_h);
            //创建shape区域
            List<Shape> shapes = createSmallShape();
            Shape area = shapes.get(0);
            Shape bigarea = shapes.get(1);
            //创建图层用于处理小图的阴影
            BufferedImage bfm1 = new BufferedImage(tailoring_w, tailoring_h, BufferedImage.TYPE_INT_ARGB);
            //创建图层用于处理大图的凹槽
            BufferedImage bfm2 = new BufferedImage(tailoring_w, tailoring_h, BufferedImage.TYPE_INT_ARGB);
            for (int i = 0; i < tailoring_w; i++) {
                for (int j = 0; j < tailoring_h; j++) {
                    if (area.contains(i, j)) {
                        bfm1.setRGB(i, j, sourceSmall.getRGB(i, j));
                    }
                    if (bigarea.contains(i, j)) {
                        bfm2.setRGB(i, j, Color.black.getRGB());
                    }
                }
            }
            //处理图片的边缘高亮及其阴影效果
            BufferedImage resultImgBuff = dealLightAndShadow(bfm1, area);
            //生成随机名称
            String smallFileName = randomImgName("small_" + filename.replaceAll(".png", "") + "_");
            File smallfile = new File(imgPath + File.separator + smallFileName);
            if (!smallfile.exists()) { //因为根据坐标点生成小图,如果已经存在,那么就不需要重复创建,直接使用
                ImageIO.write(resultImgBuff, "png", smallfile);
            }
            Map<String, String> result = new HashMap<String, String>();
            result.put("smallImgName", smallFileName);
            //将灰色图当做水印印到原图上
            String bigImgName = createBigImg(bfm2, new File(sourceImgPath + File.separator + filename), filename);
            result.put("bigImgName", bigImgName);
            result.put("location_y", String.valueOf(location_y));
            result.put("sourceImgName", filename);
            return result;
        }
    

    3: 生成Shape区域,即小方格区域,注意:此处需要生成2个Shape区域, 一个为小方格的Shape,一个为凹槽(拼接小方格的区域)Shape。为什么需要生成2个,因为生成的小方格需要做阴影效果,会生成在Shape之外,因此如果直接使用Shape当做凹槽的区域,那么生成效果图,方格区域会比凹槽略大,出现叠影。

    private java.util.List<Shape> createSmallShape() {
            //处理小图,在4个方向上 随机找到2个方向添加凸出
            int face1 = RandomUtils.nextInt(3); //凸出1
            int face2; //凸出2
            //使凸出1 与 凸出2不在同一个方向
            while (true) {
                face2 = RandomUtils.nextInt(3);
                if (face1 != face2) {
                    break;
                }
            }
            //生成随机区域值, (10-20)之间
            int position1 = RandomUtils.nextInt((tailoring_h - arc * 2) / 2) + (tailoring_h - arc * 2)/2;
            Shape shape1 = createShape(face1, 0, position1);
            Shape bigshape1 = createShape(face1, 2, position1);
    
            //生成中间正方体Shape, (具体边界+弧半径 = x坐标位)
            Shape centre = new Rectangle2D.Float(arc, arc, tailoring_w - 2 * 10, tailoring_h - 2 * 10);
            int position2 = RandomUtils.nextInt((tailoring_h - arc * 2) / 2) + (tailoring_h - arc * 2)/2;
            Shape shape2 = createShape(face2, 0, position2);
    
            //因为后边图形需要生成阴影, 所以生成的小图shape + 阴影宽度 = 灰度化的背景小图shape(即大图上的凹槽)
            Shape bigshape2 = createShape(face2, shadowWidth / 2, position2);
            Shape bigcentre = new Rectangle2D.Float(10 - shadowWidth / 2, 10 - shadowWidth / 2, 30 + shadowWidth, 30 + shadowWidth);
    
            //合并Shape
            Area area = new Area(centre);
            area.add(new Area(shape1));
            area.add(new Area(shape2));
            //合并大Shape
            Area bigarea = new Area(bigcentre);
            bigarea.add(new Area(bigshape1));
            bigarea.add(new Area(bigshape2));
            List<Shape> list = new ArrayList<Shape>();
            list.add(area);
            list.add(bigarea);
            return list;
        }
    

    4:生成Shape区域之后,根据小方格Shape区域,根据原图创建出方格,根据凹槽Shape创建一个灰化的凹槽区域。

    5:对小方格进行边缘亮化、阴影处理。 在处理阴影时,需要注意:因为阴影生成在Shape外部,我们还需要使用一个图层用来绘制阴影。然后将小方格绘制到这个图层上

        //处理小图的边缘灯光及其阴影效果
        private BufferedImage dealLightAndShadow(BufferedImage bfm, Shape shape) throws IOException {
            //创建新的透明图层,该图层用于边缘化阴影, 将生成的小图合并到该图上
            BufferedImage buffimg = ((Graphics2D) bfm.getGraphics()).getDeviceConfiguration().createCompatibleImage(50, 50, Transparency.TRANSLUCENT);
            Graphics2D graphics2D = buffimg.createGraphics();
            Graphics2D g2 = (Graphics2D) bfm.getGraphics();
            //原有小图,边缘亮色处理
            paintBorderGlow(g2, lightHeightWidth, shape);
            //新图层添加阴影
            paintBorderShadow(graphics2D, shadowWidth, shape);
            graphics2D.drawImage(bfm, 0, 0, null);
            return buffimg;
        }
    
        /**
         * 处理阴影
         * @param g2
         * @param shadowWidth
         * @param clipShape
         */
        private void paintBorderShadow(Graphics2D g2, int shadowWidth, Shape clipShape) {
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            int sw = shadowWidth * 2;
            for (int i = sw; i >= 2; i -= 2) {
                float pct = (float) (sw - i) / (sw - 1);
                //pct<03. 用于去掉阴影边缘白边,  pct>0.8用于去掉过深的色彩, 如果使用Color.lightGray. 可去掉pct>0.8
                if (pct < 0.3 || pct > 0.8) {
                    continue;
                }
                g2.setColor(getMixedColor(new Color(54, 54, 54), pct, Color.WHITE, 1.0f - pct));
                g2.setStroke(new BasicStroke(i));
                g2.draw(clipShape);
            }
        }
        /**
         * 处理边缘亮色
         * @param g2
         * @param glowWidth
         * @param clipShape
         */
        public void paintBorderGlow(Graphics2D g2, int glowWidth, Shape clipShape) {
            int gw = glowWidth * 2;
            for (int i = gw; i >= 2; i -= 2) {
                float pct = (float) (gw - i) / (gw - 1);
                Color mixHi = getMixedColor(clrGlowInnerHi, pct, clrGlowOuterHi, 1.0f - pct);
                Color mixLo = getMixedColor(clrGlowInnerLo, pct, clrGlowOuterLo, 1.0f - pct);
                g2.setPaint(new GradientPaint(0.0f, 35 * 0.25f, mixHi, 0.0f, 35, mixLo));
                g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, pct));
                g2.setStroke(new BasicStroke(i));
                g2.draw(clipShape);
            }
        }
    

    6:最后,将凹槽图层绘制到原图上,生成底部。

    至此,生成图形的代码已经完成。 下面就是处理页面展示的问题啦。已将DEMO分享github上。

    github上项目地址:
    https://github.com/devinbo/DragYzm.git

    相关文章

      网友评论

          本文标题:java实现拼图验证码

          本文链接:https://www.haomeiwen.com/subject/ydpdwxtx.html