前言
我想通过文件的md5生成关于这个md5的图像,类似于GitHub的随机像素头像,用处是让这个md5更加直观,也能用于生成各种用户头像,跟GitHub一样。
网上搜了一下,没有现成的方法,只能有一篇类似的文章可以借鉴一下,但是那篇是随机的字符串,而我的是文件,是固定的字符串,且不要改变列的数量,那我以此为基础,改一下就行了。
参考的内容:实现类似于Github的随机形状、随机颜色 像素风格头像_github像素头像_LLH_Durian的博客-CSDN博客
算法原理
由于md5是一个32位字符组成的字符串,那就可以再次上面大做文章了,我的计算方式为:
0~9位取平均值作为r(red),10~19位取平均值作为g(green),20~31位取平均值作为b(blue),那么头像的颜色就已经决定下来了。
接下来就是确定图像的像素数量,经过我的深思熟虑后,最终确定下来为8*16,即一共有8列像素,每列16行,由于头像是对称的,因此镜像一下,就是16*16的一张图片。
也就是如图所示的情况:
由于4个十六进制字符串正好是二进制的16位长度,正好可以铺满一列,我这边将1填充,0不填充,然后图片就能由此绘制出来了。
代码实现
代码有点长,不过很多都是注释,需要引入Hutool即可运行
package com.itdct.md5pic;
import org.apache.commons.lang3.StringUtils;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.Charset;
import javax.imageio.ImageIO;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.digest.MD5;
/**
* @author DCTANT
* @version 1.0
* @date 2023/4/28 15:55:23
* @description 用于生成文件MD5图片的方法
*/
public class GenerateMd5Pic {
private MD5 md5 = new MD5();
/**
* 每个格子占据的像素大小
*/
private int blockSize = 250;
/**
* 内边距,默认为两倍的格子宽度
*/
private int padding = blockSize * 2;
/**
* 背景颜色,默认为白色
*/
private Color backgroundColor = new Color(255, 255, 255);
/**
* 输出路径
*/
private String outputPath;
/**
* 输入文件路径
*/
private String filePath;
/**
* 是否输出md5文件
*/
private boolean writeMd5File;
/**
* 通过32位长度的字符串生成图片
*
* @param hexString
*/
public void string32Img(String hexString) {
if (hexString.length() != 32) {
throw new RuntimeException("输入字符串参数长度不是32");
}
// INFO: DCTANT: 2023/4/28 取RGB的总值
String redTotal = hexString.substring(0, 10);
String greenTotal = hexString.substring(10, 20);
String blueTotal = hexString.substring(20, 32);
// INFO: DCTANT: 2023/4/28 获取到平均后的rgb的值
int r = getAverage256Value(redTotal);
int g = getAverage256Value(greenTotal);
int b = getAverage256Value(blueTotal);
// INFO: DCTANT: 2023/4/28 定义每个格子的颜色
Color blockColor = new Color(r, g, b);
// INFO: DCTANT: 2023/4/28 计算图片的总像素宽度
int picSize = 2 * padding + blockSize * 16;
BufferedImage bufferedImage = new BufferedImage(picSize, picSize, BufferedImage.TYPE_INT_RGB);
// INFO: DCTANT: 2023/4/28 获取图片画笔
Graphics2D graphics2D = (Graphics2D) bufferedImage.getGraphics();
// INFO: DCTANT: 2023/4/28 设置背景颜色
graphics2D.setColor(backgroundColor);
// INFO: DCTANT: 2023/4/28 画出整个背景
graphics2D.fillRect(0, 0, picSize, picSize);
boolean[][] blockArray = calculateBlockArray(hexString);
graphics2D.setColor(blockColor);
// INFO: DCTANT: 2023/4/28 绘制每个格子
for (int column = 0; column < blockArray.length; column++) {
boolean[] rows = blockArray[column];
for (int row = 0; row < rows.length; row++) {
boolean isBlock = rows[row];
if (!isBlock) {
continue;
}
// INFO: DCTANT: 2023/4/28 数值为1的,画出方格
int x = padding + column * blockSize;
int y = padding + row * blockSize;
graphics2D.fillRect(x, y, blockSize, blockSize);
}
}
if (StringUtils.isBlank(outputPath)) {
if (StringUtils.isBlank(filePath)) {
outputPath = "./" + hexString + ".jpg";
} else {
outputPath = filePath + ".md5.jpg";
}
}
try {
File file = new File(outputPath);
System.out.println("输出路径为:" + file.getAbsolutePath());
ImageIO.write(bufferedImage, "JPG", new FileOutputStream(outputPath));//保存图片 JPEG表示保存格式
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 通过十六进制字符串,计算出16*16的像素数组
*
* @param hexString
* @return
*/
private boolean[][] calculateBlockArray(String hexString) {
boolean[][] blockArray = new boolean[16][16];
for (int column = 0; column < 8; column++) {
// INFO: DCTANT: 2023/4/28 将32位的md5,每4位切一个下来
String fourHexString = hexString.substring(column * 4, (column + 1) * 4);
// INFO: DCTANT: 2023/4/28 转为十进制
int decimal = HexUtil.hexToInt(fourHexString);
// INFO: DCTANT: 2023/4/28 十进制转二进制
StringBuilder binaryBuilder = new StringBuilder(Integer.toBinaryString(decimal));
// INFO: DCTANT: 2023/4/28 补零
if (binaryBuilder.length() < 16) {
int addZeroCount = 16 - binaryBuilder.length();
for (int i = 0; i < addZeroCount; i++) {
binaryBuilder.insert(0, "0");
}
}
// INFO: DCTANT: 2023/4/28 转为字符数组,用于判断是0还是1
char[] chars = binaryBuilder.toString().toCharArray();
for (int row = 0; row < chars.length; row++) {
char theOneOrZero = chars[row];
if (theOneOrZero == '1') {
blockArray[column][row] = true;
// INFO: DCTANT: 2023/4/28 对称点赋值
blockArray[15 - column][row] = true;
} else {
blockArray[column][row] = false;
// INFO: DCTANT: 2023/4/28 对称点赋值
blockArray[15 - column][row] = false;
}
}
}
return blockArray;
}
/**
* 通过文件生成其MD5图像
*
* @param filePath
*/
public void fileImg(String filePath) {
File file = new File(filePath);
if (!file.exists()) {
throw new RuntimeException("文件不存在");
}
String md5String = md5.digestHex(filePath);
System.out.println("file md5 is " + md5String);
if (writeMd5File) {
FileUtil.writeString(md5String, filePath + ".md5", Charset.defaultCharset());
}
this.filePath = filePath;
string32Img(md5String);
}
/**
* 计算整个十六进制字符串的其中两位的平均值,并四舍五入
*
* @param hex 该十六进制字符串每两位的平均值
* @return
*/
public int getAverage256Value(String hex) {
int loopCount = hex.length() / 2;
if (hex.length() % 2 == 1) {
throw new RuntimeException("hex长度必须为偶数");
}
double total = 0.0;
for (int i = 0; i < loopCount; i++) {
String twoHex = hex.substring(i * 2, (i + 1) * 2);
int value = HexUtil.hexToInt(twoHex);
total += value;
}
double value = total / loopCount;
int result = new BigDecimal(value).setScale(0, RoundingMode.HALF_UP).intValue();
return result;
}
public static void main(String[] args) {
GenerateMd5Pic generateMd5Pic = new GenerateMd5Pic().setWriteMd5File(true);
generateMd5Pic.fileImg("C:\\Tmp\test\\jenkins.war");
}
public String getFilePath() {
return filePath;
}
public GenerateMd5Pic setFilePath(String filePath) {
this.filePath = filePath;
return this;
}
public int getBlockSize() {
return blockSize;
}
public GenerateMd5Pic setBlockSize(int blockSize) {
this.blockSize = blockSize;
return this;
}
public int getPadding() {
return padding;
}
public GenerateMd5Pic setPadding(int padding) {
this.padding = padding;
return this;
}
public String getOutputPath() {
return outputPath;
}
public GenerateMd5Pic setOutputPath(String outputPath) {
this.outputPath = outputPath;
return this;
}
public GenerateMd5Pic setBackgroundColor(Color backgroundColor) {
this.backgroundColor = backgroundColor;
return this;
}
public boolean isWriteMd5File() {
return writeMd5File;
}
public GenerateMd5Pic setWriteMd5File(boolean writeMd5File) {
this.writeMd5File = writeMd5File;
return this;
}
}
实现结果
我就拿Jenkins的war包来举例吧,生成的效果如下:
如果你的Jenkins和我同一个版本的话,那么生成的图片应该是一模一样的,当然这个也可以用作头像