mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-05-19 19:27:16 +00:00
del[awt]: delete awt dependency
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <code>finish()</code> flushes all
|
||||
* frames. If <code>setSize</code> 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
|
||||
* <code>setDelay(1000/fps)</code>.
|
||||
*
|
||||
* @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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<String> 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无识别符");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user