diff --git a/util/src/main/java/com/zfoo/util/captcha/ArithmeticCaptcha.java b/util/src/main/java/com/zfoo/util/captcha/ArithmeticCaptcha.java deleted file mode 100644 index 38ca9b93..00000000 --- a/util/src/main/java/com/zfoo/util/captcha/ArithmeticCaptcha.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2020 The zfoo Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and limitations under the License. - */ - -package com.zfoo.util.captcha; - - -import com.zfoo.protocol.util.IOUtils; -import com.zfoo.util.captcha.model.AbstractCaptcha; -import com.zfoo.util.math.RandomUtils; -import org.springframework.expression.Expression; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.Operation; -import org.springframework.expression.spel.standard.SpelExpressionParser; - -import javax.imageio.ImageIO; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.io.OutputStream; -import java.util.List; - -/** - * png格式验证码 - * - * @author godotg - * @version 3.0 - */ -public class ArithmeticCaptcha extends AbstractCaptcha { - - /** - * 计算公式 - */ - private String arithmeticString; - - @Override - public void buildCaptcha() { - var sb = new StringBuilder(); - for (var i = 1; i <= length; i++) { - sb.append(RandomUtils.randomInt(10)); - - if (i == length) { - break; - } - - var operation = RandomUtils.randomEle(List.of(Operation.ADD, Operation.SUBTRACT, Operation.MULTIPLY)); - switch (operation) { - case ADD: - sb.append("+"); - break; - case SUBTRACT: - sb.append("-"); - break; - case MULTIPLY: - sb.append("x"); - break; - default: - } - } - - ExpressionParser parser = new SpelExpressionParser(); - Expression exp = parser.parseExpression(sb.toString().replaceAll("x", "*")); - captcha = exp.getValue(String.class); - - sb.append("=?"); - arithmeticString = sb.toString(); - } - - - /** - * 生成验证码 - */ - @Override - public void drawImage(OutputStream out) { - var captchaChars = arithmeticString.toCharArray(); - BufferedImage bi = null; - Graphics2D g2d = null; - try { - bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - g2d = (Graphics2D) bi.getGraphics(); - // 填充背景 - g2d.setColor(Color.WHITE); - g2d.fillRect(0, 0, width, height); - // 抗锯齿 - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - // 画干扰圆 - drawOval(2, g2d); - // 画字符串 - g2d.setFont(getCaptchaFont().getFont()); - FontMetrics fontMetrics = g2d.getFontMetrics(); - int fW = width / captchaChars.length; // 每一个字符所占的宽度 - int fSp = (fW - (int) fontMetrics.getStringBounds("8", g2d).getWidth()) / 2; // 字符的左右边距 - for (int i = 0; i < captchaChars.length; i++) { - g2d.setColor(color()); - int fY = height - ((height - (int) fontMetrics.getStringBounds(String.valueOf(captchaChars[i]), g2d).getHeight()) >> 1); // 文字的纵坐标 - g2d.drawString(String.valueOf(captchaChars[i]), i * fW + fSp + 3, fY - 3); - } - ImageIO.write(bi, "png", out); - out.flush(); - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - IOUtils.closeIO(out); - if (g2d != null) { - g2d.dispose(); - } - if (bi != null) { - bi.getGraphics().dispose(); - } - } - } - - @Override - public String toBase64() { - return toBase64("data:image/png;base64,"); - } - - - public String getArithmeticString() { - return arithmeticString; - } - - public void setArithmeticString(String arithmeticString) { - this.arithmeticString = arithmeticString; - } - -} diff --git a/util/src/main/java/com/zfoo/util/captcha/GifCaptcha.java b/util/src/main/java/com/zfoo/util/captcha/GifCaptcha.java deleted file mode 100644 index f5b98da1..00000000 --- a/util/src/main/java/com/zfoo/util/captcha/GifCaptcha.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2020 The zfoo Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and limitations under the License. - */ - -package com.zfoo.util.captcha; - - -import com.zfoo.protocol.util.IOUtils; -import com.zfoo.util.captcha.gif.GifEncoder; -import com.zfoo.util.captcha.model.AbstractCaptcha; -import com.zfoo.util.math.RandomUtils; - -import java.awt.*; -import java.awt.geom.CubicCurve2D; -import java.awt.image.BufferedImage; -import java.io.OutputStream; - -/** - * Gif验证码类 - * - * @author godotg - * @version 3.0 - */ -public class GifCaptcha extends AbstractCaptcha { - - @Override - public void drawImage(OutputStream out) { - try { - char[] captchaChars = captcha.toCharArray(); // 获取验证码数组 - // 随机生成每个文字的颜色 - Color[] fontColor = new Color[length]; - for (int i = 0; i < length; i++) { - fontColor[i] = color(); - } - // 随机生成贝塞尔曲线参数 - int x1 = 5, y1 = RandomUtils.randomInt(5, height / 2); - int x2 = width - 5, y2 = RandomUtils.randomInt(height / 2, height - 5); - int ctrlx = RandomUtils.randomInt(width / 4, width / 4 * 3), ctrly = RandomUtils.randomInt(5, height - 5); - if (RandomUtils.randomInt(2) == 0) { - int ty = y1; - y1 = y2; - y2 = ty; - } - int ctrlx1 = RandomUtils.randomInt(width / 4, width / 4 * 3), ctrly1 = RandomUtils.randomInt(5, height - 5); - int[][] besselXY = new int[][]{{x1, y1}, {ctrlx, ctrly}, {ctrlx1, ctrly1}, {x2, y2}}; - // 开始画gif每一帧 - GifEncoder gifEncoder = new GifEncoder(); - gifEncoder.setQuality(180); - gifEncoder.setDelay(100); - gifEncoder.setRepeat(0); - gifEncoder.start(out); - for (int i = 0; i < length; i++) { - BufferedImage frame = drawImageFrame(fontColor, captchaChars, i, besselXY); - gifEncoder.addFrame(frame); - frame.flush(); - } - gifEncoder.finish(); - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - IOUtils.closeIO(out); - } - } - - @Override - public String toBase64() { - return toBase64("data:image/gif;base64,"); - } - - /** - * 画随机码图 - * - * @param fontColor 随机字体颜色 - * @param strs 字符数组 - * @param flag 透明度 - * @param besselXY 干扰线参数 - * @return BufferedImage - */ - private BufferedImage drawImageFrame(Color[] fontColor, char[] strs, int flag, int[][] besselXY) { - BufferedImage bi = null; - Graphics2D g2d = null; - try { - bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - g2d = (Graphics2D) bi.getGraphics(); - // 填充背景颜色 - g2d.setColor(Color.WHITE); - g2d.fillRect(0, 0, width, height); - // 抗锯齿 - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - // 画干扰圆圈 - g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f * RandomUtils.randomInt(10))); // 设置透明度 - drawOval(2, g2d); - // 画干扰线 - g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f)); // 设置透明度 - g2d.setStroke(new BasicStroke(1.2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); - g2d.setColor(fontColor[0]); - CubicCurve2D shape = new CubicCurve2D.Double(besselXY[0][0], besselXY[0][1], besselXY[1][0], besselXY[1][1], besselXY[2][0], besselXY[2][1], besselXY[3][0], besselXY[3][1]); - g2d.draw(shape); - // 画验证码 - g2d.setFont(getCaptchaFont().getFont()); - FontMetrics fontMetrics = g2d.getFontMetrics(); - int fW = width / strs.length; // 每一个字符所占的宽度 - int fSp = (fW - (int) fontMetrics.getStringBounds("W", g2d).getWidth()) / 2; // 字符的左右边距 - for (int i = 0; i < strs.length; i++) { - // 设置透明度 - AlphaComposite ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getAlpha(flag, i)); - g2d.setComposite(ac3); - g2d.setColor(fontColor[i]); - int fY = height - ((height - (int) fontMetrics.getStringBounds(String.valueOf(strs[i]), g2d).getHeight()) >> 1); // 文字的纵坐标 - g2d.drawString(String.valueOf(strs[i]), i * fW + fSp + 3, fY - 3); - } - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - if (g2d != null) { - g2d.dispose(); - } - if (bi != null) { - bi.getGraphics().dispose(); - } - } - return bi; - } - - /** - * 获取透明度,从0到1,自动计算步长 - * - * @param i - * @param j - * @return 透明度 - */ - private float getAlpha(int i, int j) { - int num = i + j; - float r = (float) 1 / (length - 1); - float s = length * r; - return num >= length ? (num * r - s) : num * r; - } - -} diff --git a/util/src/main/java/com/zfoo/util/captcha/PngCaptcha.java b/util/src/main/java/com/zfoo/util/captcha/PngCaptcha.java deleted file mode 100644 index 2a0fccf1..00000000 --- a/util/src/main/java/com/zfoo/util/captcha/PngCaptcha.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2020 The zfoo Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and limitations under the License. - */ - -package com.zfoo.util.captcha; - -import com.zfoo.protocol.util.IOUtils; -import com.zfoo.util.captcha.model.AbstractCaptcha; - -import javax.imageio.ImageIO; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.io.OutputStream; - -/** - * png格式验证码 - * - * @author godotg - * @version 3.0 - */ -public class PngCaptcha extends AbstractCaptcha { - - @Override - public void drawImage(OutputStream out) { - var captchaChars = captcha.toCharArray(); - BufferedImage bi = null; - Graphics2D g2d = null; - try { - bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - g2d = (Graphics2D) bi.getGraphics(); - - // 填充背景 - g2d.setColor(Color.WHITE); - g2d.fillRect(0, 0, width, height); - // 抗锯齿 - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - // 画干扰圆 - drawOval(2, g2d); - // 画干扰线 - g2d.setStroke(new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); - drawBesselLine(1, g2d); - // 画字符串 - g2d.setFont(getCaptchaFont().getFont()); - FontMetrics fontMetrics = g2d.getFontMetrics(); - int fW = width / captchaChars.length; // 每一个字符所占的宽度 - int fSp = (fW - (int) fontMetrics.getStringBounds("W", g2d).getWidth()) / 2; // 字符的左右边距 - for (int i = 0; i < captchaChars.length; i++) { - g2d.setColor(color()); - int fY = height - ((height - (int) fontMetrics.getStringBounds(String.valueOf(captchaChars[i]), g2d).getHeight()) >> 1); // 文字的纵坐标 - g2d.drawString(String.valueOf(captchaChars[i]), i * fW + fSp + 3, fY - 3); - } - ImageIO.write(bi, "png", out); - out.flush(); - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - IOUtils.closeIO(out); - if (g2d != null) { - g2d.dispose(); - } - if (bi != null) { - bi.getGraphics().dispose(); - } - } - } - - @Override - public String toBase64() { - return toBase64("data:image/png;base64,"); - } - -} diff --git a/util/src/main/java/com/zfoo/util/captcha/gif/Encoder.java b/util/src/main/java/com/zfoo/util/captcha/gif/Encoder.java deleted file mode 100644 index 184cc07b..00000000 --- a/util/src/main/java/com/zfoo/util/captcha/gif/Encoder.java +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright (C) 2020 The zfoo Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and limitations under the License. - */ - -package com.zfoo.util.captcha.gif; - -import java.io.IOException; -import java.io.OutputStream; - -/** - * @author godotg - * @version 3.0 - */ -public class Encoder { - static final int BITS = 12; - static final int HSIZE = 5003; // 80% 占用率 - private static final int EOF = -1; - int n_bits; // number of bits/code - int maxbits = BITS; // user settable max # bits/code - int maxcode; // maximum code, given n_bits - int maxmaxcode = 1 << BITS; // should NEVER generate this code - int[] htab = new int[HSIZE]; - int[] codetab = new int[HSIZE]; - int hsize = HSIZE; // for dynamic table sizing - int free_ent = 0; // first unused entry - // block compression parameters -- after all codes are used up, - // and compression rate changes, start over. - boolean clear_flg = false; - int g_init_bits; - int ClearCode; - int EOFCode; - int cur_accum = 0; - int cur_bits = 0; - - // Algorithm: use open addressing double hashing (no chaining) on the - // prefix code / next character combination. We do a variant of Knuth's - // algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime - // secondary probe. Here, the modular division first probe is gives way - // to a faster exclusive-or manipulation. Also do block compression with - // an adaptive reset, whereby the code table is cleared when the compression - // ratio decreases, but after the table fills. The variable-length output - // codes are re-sized at this point, and a special CLEAR code is generated - // for the decompressor. Late addition: construct the table according to - // file size for noticeable speed improvement on small files. Please direct - // questions about this implementation to ames!jaw. - int[] masks = - { - 0x0000, - 0x0001, - 0x0003, - 0x0007, - 0x000F, - 0x001F, - 0x003F, - 0x007F, - 0x00FF, - 0x01FF, - 0x03FF, - 0x07FF, - 0x0FFF, - 0x1FFF, - 0x3FFF, - 0x7FFF, - 0xFFFF}; - // Number of characters so far in this 'packet' - int a_count; - // Define the storage for the packet accumulator - byte[] accum = new byte[256]; - - // output - // - // Output the given code. - // Inputs: - // code: A n_bits-bit integer. If == -1, then EOF. This assumes - // that n_bits =< wordsize - 1. - // Outputs: - // Outputs code to the file. - // Assumptions: - // Chars are 8 bits long. - // Algorithm: - // Maintain a BITS character long buffer (so that 8 codes will - // fit in it exactly). Use the VAX insv instruction to insert each - // code in turn. When the buffer fills up empty it and start over. - // 图片的宽高 - private final int imgW; - private final int imgH; - private final byte[] pixAry; - private final int initCodeSize; // 验证码位数 - private int remaining; // 剩余数量 - private int curPixel; // 像素 - - //---------------------------------------------------------------------------- - - /** - * @param width 宽度 - * @param height 高度 - * @param pixels 像素 - * @param color_depth 颜色 - */ - Encoder(int width, int height, byte[] pixels, int color_depth) { - imgW = width; - imgH = height; - pixAry = pixels; - initCodeSize = Math.max(2, color_depth); - } - - // Add a character to the end of the current packet, and if it is 254 - // characters, flush the packet to disk. - - /** - * @param c 字节 - * @param outs 输出流 - * @throws IOException IO异常 - */ - void char_out(byte c, OutputStream outs) throws IOException { - accum[a_count++] = c; - if (a_count >= 254) { - flush_char(outs); - } - } - - // Clear out the hash table - - // table clear for block compress - - /** - * @param outs 输出流 - * @throws IOException IO异常 - */ - void cl_block(OutputStream outs) throws IOException { - cl_hash(hsize); - free_ent = ClearCode + 2; - clear_flg = true; - - output(ClearCode, outs); - } - - // reset code table - - /** - * @param hsize int - */ - void cl_hash(int hsize) { - for (int i = 0; i < hsize; ++i) { - htab[i] = -1; - } - } - - /** - * @param init_bits int - * @param outs 输出流 - * @throws IOException IO异常 - */ - void compress(int init_bits, OutputStream outs) throws IOException { - int fcode; - int i /* = 0 */; - int c; - int ent; - int disp; - int hsize_reg; - int hshift; - - // Set up the globals: g_init_bits - initial number of bits - g_init_bits = init_bits; - - // Set up the necessary values - clear_flg = false; - n_bits = g_init_bits; - maxcode = MAXCODE(n_bits); - - ClearCode = 1 << (init_bits - 1); - EOFCode = ClearCode + 1; - free_ent = ClearCode + 2; - - a_count = 0; // clear packet - - ent = nextPixel(); - - hshift = 0; - for (fcode = hsize; fcode < 65536; fcode *= 2) { - ++hshift; - } - hshift = 8 - hshift; // set hash code range bound - - hsize_reg = hsize; - cl_hash(hsize_reg); // clear hash table - - output(ClearCode, outs); - - outer_loop: - while ((c = nextPixel()) != EOF) { - fcode = (c << maxbits) + ent; - i = (c << hshift) ^ ent; // xor hashing - - if (htab[i] == fcode) { - ent = codetab[i]; - continue; - } else if (htab[i] >= 0) // non-empty slot - { - disp = hsize_reg - i; // secondary hash (after G. Knott) - if (i == 0) { - disp = 1; - } - do { - if ((i -= disp) < 0) { - i += hsize_reg; - } - - if (htab[i] == fcode) { - ent = codetab[i]; - continue outer_loop; - } - } while (htab[i] >= 0); - } - output(ent, outs); - ent = c; - if (free_ent < maxmaxcode) { - codetab[i] = free_ent++; // code -> hashtable - htab[i] = fcode; - } else { - cl_block(outs); - } - } - // Put out the final code. - output(ent, outs); - output(EOFCode, outs); - } - - //---------------------------------------------------------------------------- - - /** - * @param os 输出流 - * @throws IOException IO异常 - */ - void encode(OutputStream os) throws IOException { - os.write(initCodeSize); // write "initial code size" byte - - remaining = imgW * imgH; // reset navigation variables - curPixel = 0; - - compress(initCodeSize + 1, os); // compress and write the pixel data - - os.write(0); // write block terminator - } - - // Flush the packet to disk, and reset the accumulator - - /** - * @param outs 输出流 - * @throws IOException IO异常 - */ - void flush_char(OutputStream outs) throws IOException { - if (a_count > 0) { - outs.write(a_count); - outs.write(accum, 0, a_count); - a_count = 0; - } - } - - /** - * @param n_bits int - * @return int - */ - final int MAXCODE(int n_bits) { - return (1 << n_bits) - 1; - } - - //---------------------------------------------------------------------------- - // Return the next pixel from the image - //---------------------------------------------------------------------------- - - /** - * @return int - */ - private int nextPixel() { - if (remaining == 0) { - return EOF; - } - - --remaining; - - byte pix = pixAry[curPixel++]; - - return pix & 0xff; - } - - /** - * @param code int - * @param outs 输出流 - * @throws IOException IO异常 - */ - void output(int code, OutputStream outs) throws IOException { - cur_accum &= masks[cur_bits]; - - if (cur_bits > 0) { - cur_accum |= (code << cur_bits); - } else { - cur_accum = code; - } - - cur_bits += n_bits; - - while (cur_bits >= 8) { - char_out((byte) (cur_accum & 0xff), outs); - cur_accum >>= 8; - cur_bits -= 8; - } - - // If the next entry is going to be too big for the code size, - // then increase it, if possible. - if (free_ent > maxcode || clear_flg) { - if (clear_flg) { - maxcode = MAXCODE(n_bits = g_init_bits); - clear_flg = false; - } else { - ++n_bits; - if (n_bits == maxbits) { - maxcode = maxmaxcode; - } else { - maxcode = MAXCODE(n_bits); - } - } - } - - if (code == EOFCode) { - // At EOF, write the rest of the buffer. - while (cur_bits > 0) { - char_out((byte) (cur_accum & 0xff), outs); - cur_accum >>= 8; - cur_bits -= 8; - } - - flush_char(outs); - } - } -} diff --git a/util/src/main/java/com/zfoo/util/captcha/gif/GifEncoder.java b/util/src/main/java/com/zfoo/util/captcha/gif/GifEncoder.java deleted file mode 100644 index 547f09c6..00000000 --- a/util/src/main/java/com/zfoo/util/captcha/gif/GifEncoder.java +++ /dev/null @@ -1,513 +0,0 @@ -/* - * Copyright (C) 2020 The zfoo Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and limitations under the License. - */ - -package com.zfoo.util.captcha.gif; - -import java.awt.*; -import java.awt.image.BufferedImage; -import java.awt.image.DataBufferByte; -import java.io.*; - -public class GifEncoder { - protected int width; // image size - protected int height; - protected Color transparent = null; // transparent color if given - protected int transIndex; // transparent index in color table - protected int repeat = -1; // no repeat - protected int delay = 0; // frame delay (hundredths) - protected boolean started = false; // ready to output frames - protected OutputStream out; - protected BufferedImage image; // current frame - protected byte[] pixels; // BGR byte array from frame - protected byte[] indexedPixels; // converted frame indexed to palette - protected int colorDepth; // number of bit planes - protected byte[] colorTab; // RGB palette - protected boolean[] usedEntry = new boolean[256]; // active palette entries - protected int palSize = 7; // color table size (bits-1) - protected int dispose = -1; // disposal code (-1 = use default) - protected boolean closeStream = false; // close stream when finished - protected boolean firstFrame = true; - protected boolean sizeSet = false; // if false, get size from first frame - protected int sample = 10; // default sample interval for quantizer - - /** - * Sets the delay time between each frame, or changes it - * for subsequent frames (applies to last frame added). - * - * @param ms int delay time in milliseconds - */ - public void setDelay(int ms) { - delay = Math.round(ms / 10.0f); - } - - /** - * Sets the GIF frame disposal code for the last added frame - * and any subsequent frames. Default is 0 if no transparent - * color has been set, otherwise 2. - * - * @param code int disposal code. - */ - public void setDispose(int code) { - if (code >= 0) { - dispose = code; - } - } - - /** - * Sets the number of times the set of GIF frames - * should be played. Default is 1; 0 means play - * indefinitely. Must be invoked before the first - * image is added. - * - * @param iter int number of iterations. - */ - public void setRepeat(int iter) { - if (iter >= 0) { - repeat = iter; - } - } - - /** - * Sets the transparent color for the last added frame - * and any subsequent frames. - * Since all colors are subject to modification - * in the quantization process, the color in the final - * palette for each frame closest to the given color - * becomes the transparent color for that frame. - * May be set to null to indicate no transparent color. - * - * @param c Color to be treated as transparent on display. - */ - public void setTransparent(Color c) { - transparent = c; - } - - /** - * Adds next GIF frame. The frame is not written immediately, but is - * actually deferred until the next frame is received so that timing - * data can be inserted. Invoking finish() flushes all - * frames. If setSize was not invoked, the size of the - * first image is used for all subsequent frames. - * - * @param im BufferedImage containing frame to write. - * @return true if successful. - */ - public boolean addFrame(BufferedImage im) { - if ((im == null) || !started) { - return false; - } - boolean ok = true; - try { - if (!sizeSet) { - // use first frame's size - setSize(im.getWidth(), im.getHeight()); - } - image = im; - getImagePixels(); // convert to correct format if necessary - analyzePixels(); // build color table & map pixels - if (firstFrame) { - writeLSD(); // logical screen descriptior - writePalette(); // global color table - if (repeat >= 0) { - // use NS app extension to indicate reps - writeNetscapeExt(); - } - } - writeGraphicCtrlExt(); // write graphic control extension - writeImageDesc(); // image descriptor - if (!firstFrame) { - writePalette(); // local color table - } - writePixels(); // encode and write pixel data - firstFrame = false; - } catch (IOException e) { - ok = false; - } - - return ok; - } - - //added by alvaro - public boolean outFlush() { - boolean ok = true; - try { - out.flush(); - return ok; - } catch (IOException e) { - ok = false; - } - - return ok; - } - - public byte[] getFrameByteArray() { - return ((ByteArrayOutputStream) out).toByteArray(); - } - - /** - * Flushes any pending data and closes output file. - * If writing to an OutputStream, the stream is not - * closed. - * - * @return boolean - */ - public boolean finish() { - if (!started) { - return false; - } - boolean ok = true; - started = false; - try { - out.write(0x3b); // gif trailer - out.flush(); - if (closeStream) { - out.close(); - } - } catch (IOException e) { - ok = false; - } - - return ok; - } - - public void reset() { - // reset for subsequent use - transIndex = 0; - out = null; - image = null; - pixels = null; - indexedPixels = null; - colorTab = null; - closeStream = false; - firstFrame = true; - } - - /** - * Sets frame rate in frames per second. Equivalent to - * setDelay(1000/fps). - * - * @param fps float frame rate (frames per second) - */ - public void setFrameRate(float fps) { - if (fps != 0f) { - delay = Math.round(100f / fps); - } - } - - /** - * Sets quality of color quantization (conversion of images - * to the maximum 256 colors allowed by the GIF specification). - * Lower values (minimum = 1) produce better colors, but slow - * processing significantly. 10 is the default, and produces - * good color mapping at reasonable speeds. Values greater - * than 20 do not yield significant improvements in speed. - * - * @param quality int greater than 0. - */ - public void setQuality(int quality) { - if (quality < 1) { - quality = 1; - } - sample = quality; - } - - /** - * Sets the GIF frame size. The default size is the - * size of the first frame added if this method is - * not invoked. - * - * @param w int frame width. - * @param h int frame width. - */ - public void setSize(int w, int h) { - if (started && !firstFrame) { - return; - } - width = w; - height = h; - if (width < 1) { - width = 320; - } - if (height < 1) { - height = 240; - } - sizeSet = true; - } - - /** - * Initiates GIF file creation on the given stream. The stream - * is not closed automatically. - * - * @param os OutputStream on which GIF images are written. - * @return false if initial write failed. - */ - public boolean start(OutputStream os) { - if (os == null) { - return false; - } - boolean ok = true; - closeStream = false; - out = os; - try { - writeString("GIF89a"); // header - } catch (IOException e) { - ok = false; - } - return started = ok; - } - - /** - * Initiates writing of a GIF file with the specified name. - * - * @param file String containing output file name. - * @return false if open or initial write failed. - */ - public boolean start(String file) { - boolean ok = true; - try { - out = new BufferedOutputStream(new FileOutputStream(file)); - ok = start(out); - closeStream = true; - } catch (IOException e) { - ok = false; - } - return started = ok; - } - - /** - * Analyzes image colors and creates color map. - */ - protected void analyzePixels() { - int len = pixels.length; - int nPix = len / 3; - indexedPixels = new byte[nPix]; - Quant nq = new Quant(pixels, len, sample); - // initialize quantizer - colorTab = nq.process(); // create reduced palette - // convert map from BGR to RGB - for (int i = 0; i < colorTab.length; i += 3) { - byte temp = colorTab[i]; - colorTab[i] = colorTab[i + 2]; - colorTab[i + 2] = temp; - usedEntry[i / 3] = false; - } - // map image pixels to new palette - int k = 0; - for (int i = 0; i < nPix; i++) { - int index = - nq.map(pixels[k++] & 0xff, - pixels[k++] & 0xff, - pixels[k++] & 0xff); - usedEntry[index] = true; - indexedPixels[i] = (byte) index; - } - pixels = null; - colorDepth = 8; - palSize = 7; - // get closest match to transparent color if specified - if (transparent != null) { - transIndex = findClosest(transparent); - } - } - - /** - * Returns index of palette color closest to c - * - * @param c color - * @return int - */ - protected int findClosest(Color c) { - if (colorTab == null) { - return -1; - } - int r = c.getRed(); - int g = c.getGreen(); - int b = c.getBlue(); - int minpos = 0; - int dmin = 256 * 256 * 256; - int len = colorTab.length; - for (int i = 0; i < len; ) { - int dr = r - (colorTab[i++] & 0xff); - int dg = g - (colorTab[i++] & 0xff); - int db = b - (colorTab[i] & 0xff); - int d = dr * dr + dg * dg + db * db; - int index = i / 3; - if (usedEntry[index] && (d < dmin)) { - dmin = d; - minpos = index; - } - i++; - } - return minpos; - } - - /** - * Extracts image pixels into byte array "pixels" - */ - protected void getImagePixels() { - int w = image.getWidth(); - int h = image.getHeight(); - int type = image.getType(); - if ((w != width) - || (h != height) - || (type != BufferedImage.TYPE_3BYTE_BGR)) { - // create new image with right size/format - BufferedImage temp = - new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); - Graphics2D g = temp.createGraphics(); - g.drawImage(image, 0, 0, null); - image = temp; - } - pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); - } - - /** - * Writes Graphic Control Extension - * - * @throws IOException IO异常 - */ - protected void writeGraphicCtrlExt() throws IOException { - out.write(0x21); // extension introducer - out.write(0xf9); // GCE label - out.write(4); // data block size - int transp, disp; - if (transparent == null) { - transp = 0; - disp = 0; // dispose = no action - } else { - transp = 1; - disp = 2; // force clear if using transparent color - } - if (dispose >= 0) { - disp = dispose & 7; // user override - } - disp <<= 2; - - // packed fields - out.write(0 | // 1:3 reserved - disp | // 4:6 disposal - 0 | // 7 user input - 0 = none - transp); // 8 transparency flag - - writeShort(delay); // delay x 1/100 sec - out.write(transIndex); // transparent color index - out.write(0); // block terminator - } - - /** - * Writes Image Descriptor - * - * @throws IOException IO异常 - */ - protected void writeImageDesc() throws IOException { - out.write(0x2c); // image separator - writeShort(0); // image position x,y = 0,0 - writeShort(0); - writeShort(width); // image size - writeShort(height); - // packed fields - if (firstFrame) { - // no LCT - GCT is used for first (or only) frame - out.write(0); - } else { - // specify normal LCT - out.write(0x80 | // 1 local color table 1=yes - 0 | // 2 interlace - 0=no - 0 | // 3 sorted - 0=no - 0 | // 4-5 reserved - palSize); // 6-8 size of color table - } - } - - /** - * Writes Logical Screen Descriptor - * - * @throws IOException IO异常 - */ - protected void writeLSD() throws IOException { - // logical screen size - writeShort(width); - writeShort(height); - // packed fields - out.write((0x80 | // 1 : global color table flag = 1 (gct used) - 0x70 | // 2-4 : color resolution = 7 - 0x00 | // 5 : gct sort flag = 0 - palSize)); // 6-8 : gct size - - out.write(0); // background color index - out.write(0); // pixel aspect ratio - assume 1:1 - } - - /** - * Writes Netscape application extension to define - * repeat count. - * - * @throws IOException IO异常 - */ - protected void writeNetscapeExt() throws IOException { - out.write(0x21); // extension introducer - out.write(0xff); // app extension label - out.write(11); // block size - writeString("NETSCAPE" + "2.0"); // app id + auth code - out.write(3); // sub-block size - out.write(1); // loop sub-block id - writeShort(repeat); // loop count (extra iterations, 0=repeat forever) - out.write(0); // block terminator - } - - /** - * Writes color table - * - * @throws IOException IO异常 - */ - protected void writePalette() throws IOException { - out.write(colorTab, 0, colorTab.length); - int n = (3 * 256) - colorTab.length; - for (int i = 0; i < n; i++) { - out.write(0); - } - } - - /** - * Encodes and writes pixel data - * - * @throws IOException IO异常 - */ - protected void writePixels() throws IOException { - Encoder encoder = new Encoder(width, height, indexedPixels, colorDepth); - encoder.encode(out); - } - - /** - * Write 16-bit value to output stream, LSB first - * - * @param value int - * @throws IOException IO异常 - */ - protected void writeShort(int value) throws IOException { - out.write(value & 0xff); - out.write((value >> 8) & 0xff); - } - - /** - * Writes string to output stream - * - * @param s string - * @throws IOException IO异常 - */ - protected void writeString(String s) throws IOException { - for (int i = 0; i < s.length(); i++) { - out.write((byte) s.charAt(i)); - } - } -} diff --git a/util/src/main/java/com/zfoo/util/captcha/gif/Quant.java b/util/src/main/java/com/zfoo/util/captcha/gif/Quant.java deleted file mode 100644 index bd6b7679..00000000 --- a/util/src/main/java/com/zfoo/util/captcha/gif/Quant.java +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Copyright (C) 2020 The zfoo Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and limitations under the License. - */ - -package com.zfoo.util.captcha.gif; - -/** - * @author godotg - * @version 3.0 - */ -public class Quant { - protected static final int netsize = 256; /* number of colours used */ - - /* four primes near 500 - assume no image has a length so large */ - /* that it is divisible by all four primes */ - protected static final int prime1 = 499; - protected static final int prime2 = 491; - protected static final int prime3 = 487; - protected static final int prime4 = 503; - - protected static final int minpicturebytes = (3 * prime4); - /* minimum size for input image */ - - /* Program Skeleton - ---------------- - [select samplefac in range 1..30] - [read image from input file] - pic = (unsigned char*) malloc(3*width*height); - initnet(pic,3*width*height,samplefac); - learn(); - unbiasnet(); - [write output image header, using writecolourmap(f)] - inxbuild(); - write output image using inxsearch(b,g,r) */ - - /* Network Definitions - ------------------- */ - - protected static final int maxnetpos = (netsize - 1); - protected static final int netbiasshift = 4; /* bias for colour values */ - protected static final int ncycles = 100; /* no. of learning cycles */ - - /* defs for freq and bias */ - protected static final int intbiasshift = 16; /* bias for fractions */ - protected static final int intbias = (1 << intbiasshift); - protected static final int gammashift = 10; /* gamma = 1024 */ - protected static final int gamma = (1 << gammashift); - protected static final int betashift = 10; - protected static final int beta = (intbias >> betashift); /* beta = 1/1024 */ - protected static final int betagamma = - (intbias << (gammashift - betashift)); - - /* defs for decreasing radius factor */ - protected static final int initrad = (netsize >> 3); /* for 256 cols, radius starts */ - protected static final int radiusbiasshift = 6; /* at 32.0 biased by 6 bits */ - protected static final int radiusbias = (1 << radiusbiasshift); - protected static final int initradius = (initrad * radiusbias); /* and decreases by a */ - protected static final int radiusdec = 30; /* factor of 1/30 each cycle */ - - /* defs for decreasing alpha factor */ - protected static final int alphabiasshift = 10; /* alpha starts at 1.0 */ - protected static final int initalpha = (1 << alphabiasshift); - /* radbias and alpharadbias used for radpower calculation */ - protected static final int radbiasshift = 8; - protected static final int radbias = (1 << radbiasshift); - protected static final int alpharadbshift = (alphabiasshift + radbiasshift); - protected static final int alpharadbias = (1 << alpharadbshift); - protected int alphadec; /* biased by 10 bits */ - - /* Types and Global Variables - -------------------------- */ - protected byte[] thepicture; /* the input image itself */ - protected int lengthcount; /* lengthcount = H*W*3 */ - - protected int samplefac; /* sampling factor 1..30 */ - - // typedef int pixel[4]; /* BGRc */ - protected int[][] network; /* the network itself - [netsize][4] */ - - protected int[] netindex = new int[256]; - /* for network lookup - really 256 */ - - protected int[] bias = new int[netsize]; - /* bias and freq arrays for learning */ - protected int[] freq = new int[netsize]; - protected int[] radpower = new int[initrad]; - /* radpower for precomputation */ - - /* Initialise network in range (0,0,0) to (255,255,255) and set parameters - ----------------------------------------------------------------------- */ - public Quant(byte[] thepic, int len, int sample) { - - int i; - int[] p; - - thepicture = thepic; - lengthcount = len; - samplefac = sample; - - network = new int[netsize][]; - for (i = 0; i < netsize; i++) { - network[i] = new int[4]; - p = network[i]; - p[0] = p[1] = p[2] = (i << (netbiasshift + 8)) / netsize; - freq[i] = intbias / netsize; /* 1/netsize */ - bias[i] = 0; - } - } - - public byte[] colorMap() { - byte[] map = new byte[3 * netsize]; - int[] index = new int[netsize]; - for (int i = 0; i < netsize; i++) { - index[network[i][3]] = i; - } - int k = 0; - for (int i = 0; i < netsize; i++) { - int j = index[i]; - map[k++] = (byte) (network[j][0]); - map[k++] = (byte) (network[j][1]); - map[k++] = (byte) (network[j][2]); - } - return map; - } - - /* Insertion sort of network and building of netindex[0..255] (to do after unbias) - ------------------------------------------------------------------------------- */ - public void inxbuild() { - - int i, j, smallpos, smallval; - int[] p; - int[] q; - int previouscol, startpos; - - previouscol = 0; - startpos = 0; - for (i = 0; i < netsize; i++) { - p = network[i]; - smallpos = i; - smallval = p[1]; /* index on g */ - /* find smallest in i..netsize-1 */ - for (j = i + 1; j < netsize; j++) { - q = network[j]; - if (q[1] < smallval) { /* index on g */ - smallpos = j; - smallval = q[1]; /* index on g */ - } - } - q = network[smallpos]; - /* swap p (i) and q (smallpos) entries */ - if (i != smallpos) { - j = q[0]; - q[0] = p[0]; - p[0] = j; - j = q[1]; - q[1] = p[1]; - p[1] = j; - j = q[2]; - q[2] = p[2]; - p[2] = j; - j = q[3]; - q[3] = p[3]; - p[3] = j; - } - /* smallval entry is now in position i */ - if (smallval != previouscol) { - netindex[previouscol] = (startpos + i) >> 1; - for (j = previouscol + 1; j < smallval; j++) { - netindex[j] = i; - } - previouscol = smallval; - startpos = i; - } - } - netindex[previouscol] = (startpos + maxnetpos) >> 1; - for (j = previouscol + 1; j < 256; j++) { - netindex[j] = maxnetpos; /* really 256 */ - } - } - - /* Main Learning Loop - ------------------ */ - public void learn() { - - int i, j, b, g, r; - int radius, rad, alpha, step, delta, samplepixels; - byte[] p; - int pix, lim; - - if (lengthcount < minpicturebytes) { - samplefac = 1; - } - alphadec = 30 + ((samplefac - 1) / 3); - p = thepicture; - pix = 0; - lim = lengthcount; - samplepixels = lengthcount / (3 * samplefac); - delta = samplepixels / ncycles; - alpha = initalpha; - radius = initradius; - - rad = radius >> radiusbiasshift; - if (rad <= 1) { - rad = 0; - } - for (i = 0; i < rad; i++) { - radpower[i] = - alpha * (((rad * rad - i * i) * radbias) / (rad * rad)); - } - - //fprintf(stderr,"beginning 1D learning: initial radius=%d\n", rad); - - if (lengthcount < minpicturebytes) { - step = 3; - } else if ((lengthcount % prime1) != 0) { - step = 3 * prime1; - } else { - if ((lengthcount % prime2) != 0) { - step = 3 * prime2; - } else { - if ((lengthcount % prime3) != 0) { - step = 3 * prime3; - } else { - step = 3 * prime4; - } - } - } - - i = 0; - while (i < samplepixels) { - b = (p[pix + 0] & 0xff) << netbiasshift; - g = (p[pix + 1] & 0xff) << netbiasshift; - r = (p[pix + 2] & 0xff) << netbiasshift; - j = contest(b, g, r); - - altersingle(alpha, j, b, g, r); - if (rad != 0) { - alterneigh(rad, j, b, g, r); /* alter neighbours */ - } - - pix += step; - if (pix >= lim) { - pix -= lengthcount; - } - - i++; - if (delta == 0) { - delta = 1; - } - if (i % delta == 0) { - alpha -= alpha / alphadec; - radius -= radius / radiusdec; - rad = radius >> radiusbiasshift; - if (rad <= 1) { - rad = 0; - } - for (j = 0; j < rad; j++) { - radpower[j] = - alpha * (((rad * rad - j * j) * radbias) / (rad * rad)); - } - } - } - //fprintf(stderr,"finished 1D learning: final alpha=%f !\n",((float)alpha)/initalpha); - } - - /* Search for BGR values 0..255 (after net is unbiased) and return colour index - ---------------------------------------------------------------------------- */ - public int map(int b, int g, int r) { - - int i, j, dist, a, bestd; - int[] p; - int best; - - bestd = 1000; /* biggest possible dist is 256*3 */ - best = -1; - i = netindex[g]; /* index on g */ - j = i - 1; /* start at netindex[g] and work outwards */ - - while ((i < netsize) || (j >= 0)) { - if (i < netsize) { - p = network[i]; - dist = p[1] - g; /* inx key */ - if (dist >= bestd) { - i = netsize; /* stop iter */ - } else { - i++; - if (dist < 0) { - dist = -dist; - } - a = p[0] - b; - if (a < 0) { - a = -a; - } - dist += a; - if (dist < bestd) { - a = p[2] - r; - if (a < 0) { - a = -a; - } - dist += a; - if (dist < bestd) { - bestd = dist; - best = p[3]; - } - } - } - } - if (j >= 0) { - p = network[j]; - dist = g - p[1]; /* inx key - reverse dif */ - if (dist >= bestd) { - j = -1; /* stop iter */ - } else { - j--; - if (dist < 0) { - dist = -dist; - } - a = p[0] - b; - if (a < 0) { - a = -a; - } - dist += a; - if (dist < bestd) { - a = p[2] - r; - if (a < 0) { - a = -a; - } - dist += a; - if (dist < bestd) { - bestd = dist; - best = p[3]; - } - } - } - } - } - return (best); - } - - public byte[] process() { - learn(); - unbiasnet(); - inxbuild(); - return colorMap(); - } - - /* Unbias network to give byte values 0..255 and record position i to prepare for sort - ----------------------------------------------------------------------------------- */ - public void unbiasnet() { - - int i, j; - - for (i = 0; i < netsize; i++) { - network[i][0] >>= netbiasshift; - network[i][1] >>= netbiasshift; - network[i][2] >>= netbiasshift; - network[i][3] = i; /* record colour no */ - } - } - - /* Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in radpower[|i-j|] - --------------------------------------------------------------------------------- */ - protected void alterneigh(int rad, int i, int b, int g, int r) { - - int j, k, lo, hi, a, m; - int[] p; - - lo = i - rad; - if (lo < -1) { - lo = -1; - } - hi = i + rad; - if (hi > netsize) { - hi = netsize; - } - - j = i + 1; - k = i - 1; - m = 1; - while ((j < hi) || (k > lo)) { - a = radpower[m++]; - if (j < hi) { - p = network[j++]; - try { - p[0] -= (a * (p[0] - b)) / alpharadbias; - p[1] -= (a * (p[1] - g)) / alpharadbias; - p[2] -= (a * (p[2] - r)) / alpharadbias; - } catch (Exception e) { - } // prevents 1.3 miscompilation - } - if (k > lo) { - p = network[k--]; - try { - p[0] -= (a * (p[0] - b)) / alpharadbias; - p[1] -= (a * (p[1] - g)) / alpharadbias; - p[2] -= (a * (p[2] - r)) / alpharadbias; - } catch (Exception e) { - } - } - } - } - - /* Move neuron i towards biased (b,g,r) by factor alpha - ---------------------------------------------------- */ - protected void altersingle(int alpha, int i, int b, int g, int r) { - - /* alter hit neuron */ - int[] n = network[i]; - n[0] -= (alpha * (n[0] - b)) / initalpha; - n[1] -= (alpha * (n[1] - g)) / initalpha; - n[2] -= (alpha * (n[2] - r)) / initalpha; - } - - /* Search for biased BGR values - ---------------------------- */ - protected int contest(int b, int g, int r) { - - /* finds closest neuron (min dist) and updates freq */ - /* finds best neuron (min dist-bias) and returns position */ - /* for frequently chosen neurons, freq[i] is high and bias[i] is negative */ - /* bias[i] = gamma*((1/netsize)-freq[i]) */ - - int i, dist, a, biasdist, betafreq; - int bestpos, bestbiaspos, bestd, bestbiasd; - int[] n; - - bestd = ~(1 << 31); - bestbiasd = bestd; - bestpos = -1; - bestbiaspos = bestpos; - - for (i = 0; i < netsize; i++) { - n = network[i]; - dist = n[0] - b; - if (dist < 0) { - dist = -dist; - } - a = n[1] - g; - if (a < 0) { - a = -a; - } - dist += a; - a = n[2] - r; - if (a < 0) { - a = -a; - } - dist += a; - if (dist < bestd) { - bestd = dist; - bestpos = i; - } - biasdist = dist - ((bias[i]) >> (intbiasshift - netbiasshift)); - if (biasdist < bestbiasd) { - bestbiasd = biasdist; - bestbiaspos = i; - } - betafreq = (freq[i] >> betashift); - freq[i] -= betafreq; - bias[i] += (betafreq << gammashift); - } - freq[bestpos] += beta; - bias[bestpos] -= betagamma; - return (bestbiaspos); - } -} diff --git a/util/src/main/java/com/zfoo/util/captcha/model/AbstractCaptcha.java b/util/src/main/java/com/zfoo/util/captcha/model/AbstractCaptcha.java deleted file mode 100644 index b14caa4b..00000000 --- a/util/src/main/java/com/zfoo/util/captcha/model/AbstractCaptcha.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright (C) 2020 The zfoo Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and limitations under the License. - */ - -package com.zfoo.util.captcha.model; - -import com.zfoo.protocol.util.IOUtils; -import com.zfoo.protocol.util.StringUtils; -import com.zfoo.util.math.RandomUtils; - -import java.awt.*; -import java.awt.geom.CubicCurve2D; -import java.awt.geom.QuadCurve2D; -import java.io.ByteArrayOutputStream; -import java.io.OutputStream; -import java.util.Base64; - -/** - * 验证码抽象类 - * - * @author godotg - * @version 3.0 - */ -public abstract class AbstractCaptcha { - - /** - * 常用颜色 - */ - private static final int[][] COLOR = new int[][] - { - {0, 135, 255}, - {51, 153, 51}, - {255, 102, 102}, - {255, 153, 0}, - {153, 102, 0}, - {153, 102, 153}, - {51, 153, 153}, - {102, 102, 255}, - {0, 102, 204}, - {204, 51, 51}, - {0, 153, 204}, - {0, 51, 102} - }; - /** - * 验证码随机字符长度 - */ - protected int length = 5; - /** - * 验证码显示宽度 - */ - protected int width = 130; - /** - * 验证码显示高度 - */ - protected int height = 48; - /** - * 当前验证码 - */ - protected String captcha; - private CaptchaCharEnum captchaChar = CaptchaCharEnum.ARAB_ENGLISH; - private CaptchaFontEnum captchaFont = CaptchaFontEnum.actionj; - - public void buildCaptcha() { - switch (captchaChar) { - case ARAB_NUMBER: - captcha = RandomUtils.randomString(StringUtils.ARAB_NUMBER, length); - break; - case ENGLISH_CHAR: - captcha = RandomUtils.randomString(StringUtils.ENGLISH_CHAR, length); - break; - case ARAB_ENGLISH: - captcha = RandomUtils.randomString(length); - break; - default: - } - } - - - /** - * 给定范围获得随机颜色 - * - * @param fc 0-255 - * @param bc 0-255 - * @return 随机颜色 - */ - protected Color color(int fc, int bc) { - if (fc > 255) { - fc = 255; - } - if (bc > 255) { - bc = 255; - } - int r = fc + RandomUtils.randomInt(bc - fc); - int g = fc + RandomUtils.randomInt(bc - fc); - int b = fc + RandomUtils.randomInt(bc - fc); - return new Color(r, g, b); - } - - /** - * 获取随机常用颜色 - * - * @return 随机颜色 - */ - protected Color color() { - int[] color = COLOR[RandomUtils.randomInt(COLOR.length)]; - return new Color(color[0], color[1], color[2]); - } - - /** - * 验证码输出,抽象方法,由子类实现 - */ - public abstract void drawImage(OutputStream os); - - /** - * 输出base64编码 - * - * @return base64编码字符串 - */ - public abstract String toBase64(); - - /** - * 输出base64编码 - * - * @param type 编码头 - * @return base64编码字符串 - */ - public String toBase64(String type) { - var outputStream = new ByteArrayOutputStream(); - drawImage(outputStream); - var base64 = type + Base64.getEncoder().encodeToString(outputStream.toByteArray()); - IOUtils.closeIO(outputStream); - return base64; - } - - /** - * 获取当前的验证码 - */ - public String captcha() { - return captcha; - } - - - /** - * 随机画干扰线 - * - * @param num 数量 - * @param g Graphics2D - */ - public void drawLine(int num, Graphics2D g) { - drawLine(num, null, g); - } - - /** - * 随机画干扰线 - * - * @param num 数量 - * @param color 颜色 - * @param g Graphics2D - */ - public void drawLine(int num, Color color, Graphics2D g) { - for (int i = 0; i < num; i++) { - g.setColor(color == null ? color() : color); - int x1 = RandomUtils.randomInt(-10, width - 10); - int y1 = RandomUtils.randomInt(5, height - 5); - int x2 = RandomUtils.randomInt(10, width + 10); - int y2 = RandomUtils.randomInt(2, height - 2); - g.drawLine(x1, y1, x2, y2); - } - } - - /** - * 随机画干扰圆 - * - * @param num 数量 - * @param g Graphics2D - */ - public void drawOval(int num, Graphics2D g) { - drawOval(num, null, g); - } - - /** - * 随机画干扰圆 - * - * @param num 数量 - * @param color 颜色 - * @param g Graphics2D - */ - public void drawOval(int num, Color color, Graphics2D g) { - for (int i = 0; i < num; i++) { - g.setColor(color == null ? color() : color); - int w = 5 + RandomUtils.randomInt(10); - g.drawOval(RandomUtils.randomInt(width - 25), RandomUtils.randomInt(height - 15), w, w); - } - } - - /** - * 随机画贝塞尔曲线 - * - * @param num 数量 - * @param g Graphics2D - */ - public void drawBesselLine(int num, Graphics2D g) { - drawBesselLine(num, null, g); - } - - /** - * 随机画贝塞尔曲线 - * - * @param num 数量 - * @param color 颜色 - * @param g Graphics2D - */ - public void drawBesselLine(int num, Color color, Graphics2D g) { - for (var i = 0; i < num; i++) { - g.setColor(color == null ? color() : color); - int x1 = 5, y1 = RandomUtils.randomInt(5, height / 2); - int x2 = width - 5, y2 = RandomUtils.randomInt(height / 2, height - 5); - int ctrlx = RandomUtils.randomInt(width / 4, width / 4 * 3), ctrly = RandomUtils.randomInt(5, height - 5); - if (RandomUtils.randomInt(2) == 0) { - int ty = y1; - y1 = y2; - y2 = ty; - } - if (RandomUtils.randomInt(2) == 0) { // 二阶贝塞尔曲线 - QuadCurve2D shape = new QuadCurve2D.Double(); - shape.setCurve(x1, y1, ctrlx, ctrly, x2, y2); - g.draw(shape); - } else { // 三阶贝塞尔曲线 - int ctrlx1 = RandomUtils.randomInt(width / 4, width / 4 * 3), ctrly1 = RandomUtils.randomInt(5, height - 5); - CubicCurve2D shape = new CubicCurve2D.Double(x1, y1, ctrlx, ctrly, ctrlx1, ctrly1, x2, y2); - g.draw(shape); - } - } - } - - - public CaptchaCharEnum getCaptchaChar() { - return captchaChar; - } - - public void setCaptchaChar(CaptchaCharEnum captchaChar) { - this.captchaChar = captchaChar; - } - - public CaptchaFontEnum getCaptchaFont() { - return captchaFont; - } - - public void setCaptchaFont(CaptchaFontEnum captchaFont) { - this.captchaFont = captchaFont; - } - - public int getLength() { - return length; - } - - public void setLength(int length) { - this.length = length; - } - - public int getWidth() { - return width; - } - - public void setWidth(int width) { - this.width = width; - } - - public int getHeight() { - return height; - } - - public void setHeight(int height) { - this.height = height; - } -} diff --git a/util/src/main/java/com/zfoo/util/captcha/model/CaptchaCharEnum.java b/util/src/main/java/com/zfoo/util/captcha/model/CaptchaCharEnum.java deleted file mode 100644 index 4505f22b..00000000 --- a/util/src/main/java/com/zfoo/util/captcha/model/CaptchaCharEnum.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2020 The zfoo Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and limitations under the License. - */ - -package com.zfoo.util.captcha.model; - -/** - * @author godotg - * @version 3.0 - */ -public enum CaptchaCharEnum { - - /** - * 纯数字 - */ - ARAB_NUMBER, - - /** - * 纯英文字母 - */ - ENGLISH_CHAR, - - /** - * 数字和英文结合 - */ - ARAB_ENGLISH, - -} diff --git a/util/src/main/java/com/zfoo/util/captcha/model/CaptchaFontEnum.java b/util/src/main/java/com/zfoo/util/captcha/model/CaptchaFontEnum.java deleted file mode 100644 index 6700283b..00000000 --- a/util/src/main/java/com/zfoo/util/captcha/model/CaptchaFontEnum.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2020 The zfoo Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and limitations under the License. - */ - -package com.zfoo.util.captcha.model; - -import com.zfoo.protocol.util.ClassUtils; -import com.zfoo.protocol.util.IOUtils; -import com.zfoo.protocol.util.StringUtils; - -import java.awt.*; -import java.io.InputStream; - -/** - * @author godotg - * @version 3.0 - */ -public enum CaptchaFontEnum { - - /** - * 默认字体 - */ - actionj, - - epilog, - - fresnel, - - headache, - - lexo, - - prefix, - - progbot, - - ransom, - - robot, - - scandal, - - ; - - private static final Font DEFAULT_FONT = new Font("Arial", Font.BOLD, 32); - - private final Font font; - - CaptchaFontEnum() { - InputStream inputStream = null; - try { - inputStream = ClassUtils.getFileFromClassPath(StringUtils.format("captcha/{}.ttf", this.name())); - font = Font.createFont(Font.TRUETYPE_FONT, inputStream).deriveFont(Font.BOLD, 32f); - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - IOUtils.closeIO(inputStream); - } - } - - public Font getFont() { - return font; - } -} diff --git a/util/src/main/java/com/zfoo/util/math/RandomUtils.java b/util/src/main/java/com/zfoo/util/math/RandomUtils.java index f48377c2..994b7e27 100644 --- a/util/src/main/java/com/zfoo/util/math/RandomUtils.java +++ b/util/src/main/java/com/zfoo/util/math/RandomUtils.java @@ -15,7 +15,6 @@ package com.zfoo.util.math; import com.zfoo.protocol.util.StringUtils; -import java.awt.*; import java.math.BigDecimal; import java.math.RoundingMode; import java.security.NoSuchAlgorithmException; @@ -408,14 +407,4 @@ public abstract class RandomUtils { return baseString.charAt(getRandom().nextInt(baseString.length())); } - /** - * 生成随机颜色 - * - * @return 随机颜色 - */ - public static Color randomColor() { - final Random random = getRandom(); - return new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)); - } - } diff --git a/util/src/main/java/com/zfoo/util/math/lexer/LexicalAnalysis.java b/util/src/main/java/com/zfoo/util/math/lexer/LexicalAnalysis.java deleted file mode 100644 index 4291ff7c..00000000 --- a/util/src/main/java/com/zfoo/util/math/lexer/LexicalAnalysis.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2020 The zfoo Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and limitations under the License. - */ - -package com.zfoo.util.math.lexer; - -import com.zfoo.protocol.util.StringUtils; - -import java.util.Set; - -/** - * 词法分析器 - * - * @author godotg - * @version 3.0 - */ -public class LexicalAnalysis { - private static final Set KEY_WORDS = Set.of("break", "include", "begin", "end", "if", "else", "while", "switch"); - - - //判断是否是关键字 - private boolean isKey(String str) { - return KEY_WORDS.contains(str); - } - - //判断是否是字母 - boolean isLetter(char letter) { - return StringUtils.isEnglishChar(letter); - } - - //判断是否是数字 - boolean isDigit(char digit) { - return Character.isDigit(digit); - } - - //词法分析 - void analyze(char[] chars) { - for (var i = 0; i < chars.length; i++) { - var ch = chars[i]; - if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') { - continue; - } - var arr = ""; - - if (isLetter(ch)) { - while (isLetter(ch) || isDigit(ch)) { - arr += ch; - ch = chars[++i]; - } - //回退一个字符 - i--; - if (isKey(arr)) { - //关键字 - System.out.println(arr + "\t4" + "\t关键字"); - } else { - //标识符 - System.out.println(arr + "\t4" + "\t标识符"); - } - } else if (isDigit(ch) || (ch == '.')) { - while (isDigit(ch) || (ch == '.' && isDigit(chars[++i]))) { - if (ch == '.') { - i--; - } - arr = arr + ch; - ch = chars[++i]; - } - //属于无符号常数 - System.out.println(arr + "\t5" + "\t常数"); - } else { - switch (ch) { - //运算符 - case '+': - System.out.println(ch + "\t2" + "\t运算符"); - break; - case '-': - System.out.println(ch + "\t2" + "\t运算符"); - break; - case '*': - System.out.println(ch + "\t2" + "\t运算符"); - break; - case '/': - System.out.println(ch + "\t2" + "\t运算符"); - break; - //分界符 - case '(': - System.out.println(ch + "\t3" + "\t分界符"); - break; - case ')': - System.out.println(ch + "\t3" + "\t分界符"); - break; - case '[': - System.out.println(ch + "\t3" + "\t分界符"); - break; - case ']': - System.out.println(ch + "\t3" + "\t分界符"); - break; - case ';': - System.out.println(ch + "\t3" + "\t分界符"); - break; - case '{': - System.out.println(ch + "\t3" + "\t分界符"); - break; - case '}': - System.out.println(ch + "\t3" + "\t分界符"); - break; - // 字符串 - case '"': - do { - arr += ch; - ch = chars[++i]; - } while (ch != '"'); - arr += ch; - - System.out.println(arr + "\t3" + "\t字符串"); - break; - //运算符 - case '=': { - ch = chars[++i]; - if (ch == '=') { - System.out.println("==" + "\t2" + "\t运算符"); - } else { - System.out.println("=" + "\t2" + "\t运算符"); - i--; - } - } - break; - case ':': { - ch = chars[++i]; - if (ch == '=') { - System.out.println(":=" + "\t2" + "\t运算符"); - } else { - System.out.println(":" + "\t2" + "\t运算符"); - i--; - } - } - break; - case '>': { - ch = chars[++i]; - if (ch == '=') { - System.out.println(">=" + "\t2" + "\t运算符"); - } else { - System.out.println(">" + "\t2" + "\t运算符"); - i--; - } - } - break; - case '<': { - ch = chars[++i]; - if (ch == '=') { - System.out.println("<=" + "\t2" + "\t运算符"); - } else { - System.out.println("<" + "\t2" + "\t运算符"); - i--; - } - } - break; - //无识别 - default: - System.out.println(ch + "\t6" + "\t无识别符"); - } - } - } - } -} diff --git a/util/src/main/resources/captcha/actionj.ttf b/util/src/main/resources/captcha/actionj.ttf deleted file mode 100644 index 8439247f..00000000 Binary files a/util/src/main/resources/captcha/actionj.ttf and /dev/null differ diff --git a/util/src/main/resources/captcha/epilog.ttf b/util/src/main/resources/captcha/epilog.ttf deleted file mode 100644 index bd9614af..00000000 Binary files a/util/src/main/resources/captcha/epilog.ttf and /dev/null differ diff --git a/util/src/main/resources/captcha/fresnel.ttf b/util/src/main/resources/captcha/fresnel.ttf deleted file mode 100644 index 7e32a510..00000000 Binary files a/util/src/main/resources/captcha/fresnel.ttf and /dev/null differ diff --git a/util/src/main/resources/captcha/headache.ttf b/util/src/main/resources/captcha/headache.ttf deleted file mode 100644 index 26fa0af9..00000000 Binary files a/util/src/main/resources/captcha/headache.ttf and /dev/null differ diff --git a/util/src/main/resources/captcha/lexo.ttf b/util/src/main/resources/captcha/lexo.ttf deleted file mode 100644 index 411af54a..00000000 Binary files a/util/src/main/resources/captcha/lexo.ttf and /dev/null differ diff --git a/util/src/main/resources/captcha/prefix.ttf b/util/src/main/resources/captcha/prefix.ttf deleted file mode 100644 index a1aeed09..00000000 Binary files a/util/src/main/resources/captcha/prefix.ttf and /dev/null differ diff --git a/util/src/main/resources/captcha/progbot.ttf b/util/src/main/resources/captcha/progbot.ttf deleted file mode 100644 index c98d1221..00000000 Binary files a/util/src/main/resources/captcha/progbot.ttf and /dev/null differ diff --git a/util/src/main/resources/captcha/ransom.ttf b/util/src/main/resources/captcha/ransom.ttf deleted file mode 100644 index 779ab8ec..00000000 Binary files a/util/src/main/resources/captcha/ransom.ttf and /dev/null differ diff --git a/util/src/main/resources/captcha/robot.ttf b/util/src/main/resources/captcha/robot.ttf deleted file mode 100644 index 09e2de05..00000000 Binary files a/util/src/main/resources/captcha/robot.ttf and /dev/null differ diff --git a/util/src/main/resources/captcha/scandal.ttf b/util/src/main/resources/captcha/scandal.ttf deleted file mode 100644 index 22e3a7e2..00000000 Binary files a/util/src/main/resources/captcha/scandal.ttf and /dev/null differ diff --git a/util/src/test/java/com/zfoo/util/captcha/CaptchaTest.java b/util/src/test/java/com/zfoo/util/captcha/CaptchaTest.java deleted file mode 100644 index 75cab141..00000000 --- a/util/src/test/java/com/zfoo/util/captcha/CaptchaTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2020 The zfoo Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and limitations under the License. - */ - -package com.zfoo.util.captcha; - -import com.zfoo.util.captcha.model.CaptchaFontEnum; -import org.junit.Ignore; -import org.junit.Test; - -import java.io.File; -import java.io.FileOutputStream; - -/** - * @author godotg - * @version 3.0 - */ -@Ignore -public class CaptchaTest { - - @Test - public void pngTest() throws Exception { - - for (var font : CaptchaFontEnum.values()) { - PngCaptcha pngCaptcha = new PngCaptcha(); - pngCaptcha.setLength(4); - pngCaptcha.setCaptchaFont(font); - pngCaptcha.buildCaptcha(); - System.out.println(pngCaptcha.captcha()); - pngCaptcha.drawImage(new FileOutputStream(new File(font.name() + ".png"))); - } - } - - @Test - public void gifTest() throws Exception { - for (var font : CaptchaFontEnum.values()) { - GifCaptcha gifCaptcha = new GifCaptcha(); - gifCaptcha.setLength(5); - gifCaptcha.setCaptchaFont(font); - gifCaptcha.buildCaptcha(); - System.out.println(gifCaptcha.captcha()); - gifCaptcha.drawImage(new FileOutputStream(new File(font.name() + ".gif"))); - } - } - - @Test - public void arithmeticTest() throws Exception { - for (var font : CaptchaFontEnum.values()) { - ArithmeticCaptcha specCaptcha = new ArithmeticCaptcha(); - specCaptcha.setLength(3); - specCaptcha.setCaptchaFont(font); - specCaptcha.buildCaptcha(); - System.out.println(specCaptcha.getArithmeticString() + " " + specCaptcha.captcha()); - specCaptcha.drawImage(new FileOutputStream(new File(font.name() + ".png"))); - } - } - - @Test - public void base64Test() throws Exception { - for (var i = 0; i < Long.MAX_VALUE; i++) { - GifCaptcha gifCaptcha = new GifCaptcha(); - gifCaptcha.buildCaptcha(); - gifCaptcha.toBase64(); - - PngCaptcha pngCaptcha = new PngCaptcha(); - pngCaptcha.buildCaptcha(); - pngCaptcha.toBase64(); - - ArithmeticCaptcha arithmeticCaptcha = new ArithmeticCaptcha(); - arithmeticCaptcha.buildCaptcha(); - arithmeticCaptcha.toBase64(); - } - } - -} diff --git a/util/src/test/java/com/zfoo/util/math/lexer/LexicalAnalysisTest.java b/util/src/test/java/com/zfoo/util/math/lexer/LexicalAnalysisTest.java deleted file mode 100644 index aebc9ed1..00000000 --- a/util/src/test/java/com/zfoo/util/math/lexer/LexicalAnalysisTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2020 The zfoo Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and limitations under the License. - */ - -package com.zfoo.util.math.lexer; - -import org.junit.Test; - -/** - * @author godotg - * @version 3.0 - */ -public class LexicalAnalysisTest { - private static final String str = " void main() {\n" + - " if (a == 8) {\n" + - " c = c +2;\n" + - " b = 1.2;\n" + - " c = c + 3;\n" + - " d = \"hello world\";\n" + - " }\n" + - " }"; - - @Test - public void test() { - var lexical = new LexicalAnalysis(); - lexical.analyze(str.toCharArray()); - } -}