效果图:




代码实现:
php
class ImageIdenticon
{
public static function generateBase64Avatar(string $address, int $size = 64, int $grid = 8): string
{
if (!extension_loaded('gd')) {
throw new \RuntimeException('GD 扩展未启用');
}
$address = trim($address) ?: 'default';
$hash = hash('sha256', $address);
$img = imagecreatetruecolor($size, $size);
// 关键:不要透明通道,避免任何"透明合成黑边"
imagesavealpha($img, false);
imagealphablending($img, true);
// 赛博霓虹统一调色板:主/辅/高亮/背景(深色)
$palettes = [
[[165, 90, 255], [0, 255, 240], [255, 230, 90], [18, 20, 40]],
[[90, 170, 255], [175, 80, 255], [150, 255, 90], [10, 16, 34]],
[[140, 255, 90], [0, 255, 220], [255, 120, 255], [10, 24, 20]],
[[255, 90, 210], [90, 170, 255], [255, 230, 90], [22, 10, 30]],
[[0, 255, 240], [150, 255, 90], [175, 80, 255], [6, 26, 28]],
[[255, 230, 90], [255, 90, 210], [0, 255, 240], [8, 12, 24]],
];
$paletteIndex = self::stableIndex('palette:' . $address, count($palettes));
$p = $palettes[$paletteIndex];
$colA = imagecolorallocate($img, $p[0][0], $p[0][1], $p[0][2]); // 主色
$colB = imagecolorallocate($img, $p[1][0], $p[1][1], $p[1][2]); // 辅色
$colC = imagecolorallocate($img, $p[2][0], $p[2][1], $p[2][2]); // 高亮
$colBG = imagecolorallocate($img, $p[3][0], $p[3][1], $p[3][2]); // 背景深色
$colEye = imagecolorallocate($img, 6, 6, 10); // 五官近黑
// 整张图先铺满背景色(彻底杜绝透明导致的黑边)
imagefilledrectangle($img, 0, 0, $size, $size, $colBG);
$grid = max(6, $grid);
$cell = (int)floor($size / $grid);
if ($cell < 1) $cell = 1;
$drawSize = $cell * $grid;
$offset = (int)floor(($size - $drawSize) / 2);
// 1) 底纹
$full = self::buildCyberCircuitPattern($address, $hash, $grid);
// 2) 脸型模板
$mask = self::buildStyleMask($address, $grid);
// 3) 覆盖合成
for ($y = 0; $y < $grid; $y++) {
for ($x = 0; $x < $grid; $x++) {
if (($mask[$y][$x] ?? 0) !== 0) {
$full[$y][$x] = $mask[$y][$x];
}
}
}
// 4) 只在圆内绘制像素(圆外保持背景色 colBG)
$r = $size / 2;
$cxPix = $size / 2;
$cyPix = $size / 2;
// 边缘厚度:1=最外一圈像素块。用于"去暗/去黑",避免看起来像描边
$edgeThickness = 1;
for ($y = 0; $y < $grid; $y++) {
for ($x = 0; $x < $grid; $x++) {
$x1 = $offset + $x * $cell;
$y1 = $offset + $y * $cell;
$x2 = $x1 + $cell - 1;
$y2 = $y1 + $cell - 1;
// 格子中心点
$px = $x1 + $cell / 2;
$py = $y1 + $cell / 2;
$dx = $px - $cxPix;
$dy = $py - $cyPix;
$d2 = ($dx * $dx + $dy * $dy);
// 圆外不画(保持 colBG)
if ($d2 > ($r * $r)) {
continue;
}
$v = $full[$y][$x] ?? 5;
// 边缘"去暗/去黑":避免圆边缘出现深色圈感
$dist = sqrt($d2);
$nearEdge = (($r - $dist) <= ($edgeThickness * $cell));
if ($nearEdge) {
// 最激进:边缘永远不允许 v=4(黑五官) 和 v=5(深背景)
if ($v === 4) $v = 3; // 黑 -> 高亮
if ($v === 5) $v = 2; // 深背景 -> 辅色
}
if ($v === 4) $color = $colEye;
elseif ($v === 3) $color = $colC;
elseif ($v === 2) $color = $colB;
elseif ($v === 1) $color = $colA;
else $color = $colBG;
imagefilledrectangle($img, $x1, $y1, $x2, $y2, $color);
}
}
ob_start();
imagepng($img);
$png = ob_get_clean();
imagedestroy($img);
return base64_encode($png);
}
private static function stableIndex(string $key, int $mod): int
{
$n = sprintf('%u', crc32($key));
return $n % $mod;
}
/**
* 赛博电路板底纹:返回值 1主 2辅 3高亮 5背景
* 核心:对称 + 线路条纹 + 接点高亮
*/
private static function buildCyberCircuitPattern(string $address, string $hash, int $grid): array
{
$m = array_fill(0, $grid, array_fill(0, $grid, 5));
$seedA = self::stableIndex('lineA:' . $address, 1000);
$seedB = self::stableIndex('lineB:' . $address, 1000);
// 1) 深色背景基底 + 少量"噪点电流"
for ($y = 0; $y < $grid; $y++) {
for ($x = 0; $x < $grid; $x++) {
$n = hexdec($hash[($x + $y * $grid) % strlen($hash)]) % 100;
// 背景为主:70%,主色/辅色做电流:25%,高亮接点:5%
if ($n < 5) $m[$y][$x] = 3;
elseif ($n < 18) $m[$y][$x] = 2;
elseif ($n < 30) $m[$y][$x] = 1;
else $m[$y][$x] = 5;
}
}
// 2) 画"电路线条"(横竖两组),用主/辅色
$row1 = ($seedA % ($grid - 2)) + 1;
$row2 = ($seedB % ($grid - 2)) + 1;
$col1 = (self::stableIndex('col1:' . $address, $grid - 2)) + 1;
$col2 = (self::stableIndex('col2:' . $address, $grid - 2)) + 1;
for ($x = 0; $x < $grid; $x++) {
$m[$row1][$x] = 1;
if ($grid >= 8) $m[$row2][$x] = 2;
}
for ($y = 0; $y < $grid; $y++) {
$m[$y][$col1] = 2;
if ($grid >= 8) $m[$y][$col2] = 1;
}
// 3) 线路"接点"高亮(交叉点 + 随机点)
$m[$row1][$col1] = 3;
if ($grid >= 8) {
$m[$row2][$col2] = 3;
$m[$row1][$col2] = 3;
$m[$row2][$col1] = 3;
}
// 4) 对称(让头像更像 identicon 且更统一)
$half = (int)ceil($grid / 2);
for ($y = 0; $y < $grid; $y++) {
for ($x = 0; $x < $half; $x++) {
$m[$y][$grid - 1 - $x] = $m[$y][$x];
}
}
return $m;
}
private static function buildStyleMask(string $address, int $grid): array
{
// 统一赛博风:robot / monster / cat / dog / frog / alien
$styles = ['robot', 'monster', 'cat', 'dog', 'frog', 'alien'];
$idx = self::stableIndex('style:' . $address, count($styles));
$style = $styles[$idx];
switch ($style) {
case 'monster':
return self::buildMonsterFaceMask($address, $grid);
case 'cat':
return self::buildCatFaceMask($address, $grid);
case 'dog':
return self::buildDogFaceMask($address, $grid);
case 'frog':
return self::buildFrogFaceMask($address, $grid);
case 'alien':
return self::buildAlienFaceMask($address, $grid);
default:
return self::buildRobotFaceMask($address, $grid);
}
}
// -------------------- 模板:全部"酷感"统一:更硬朗的线条/眼睛/表情 --------------------
private static function buildMonsterFaceMask(string $address, int $grid): array
{
$mask = array_fill(0, $grid, array_fill(0, $grid, 0));
$cx = (int)floor(($grid - 1) / 2);
$cy = (int)floor(($grid - 1) / 2);
$left = 1;
$right = $grid - 2;
$top = 1;
$bottom = $grid - 2;
for ($y = $top; $y <= $bottom; $y++) {
for ($x = $left; $x <= $right; $x++) $mask[$y][$x] = 1;
}
// 角:高亮
if ($top - 1 >= 0) {
$mask[$top - 1][max(0, $left + 1)] = 3;
$mask[$top - 1][min($grid - 1, $right - 1)] = 3;
}
// 眼:三眼更"异形"
$eyeY = max($top + 2, $cy - 1);
$threeEye = self::stableIndex('eye:' . $address, 2) === 1;
if ($threeEye) $mask[$eyeY][$cx] = 4;
$mask[$eyeY][max($left + 1, $cx - 2)] = 4;
$mask[$eyeY][min($right - 1, $cx + 2)] = 4;
// 大嘴 + 獠牙
$mouthY = min($bottom - 1, $cy + 2);
for ($x = $cx - 2; $x <= $cx + 2; $x++) if ($x > $left && $x < $right) $mask[$mouthY][$x] = 4;
if ($mouthY + 1 <= $bottom) {
$mask[$mouthY + 1][$cx - 1] = 2;
$mask[$mouthY + 1][$cx + 1] = 2;
}
// 颧骨阴影
$mask[$cy][max($left + 1, $cx - 3)] = 2;
$mask[$cy][min($right - 1, $cx + 3)] = 2;
return $mask;
}
private static function buildCatFaceMask(string $address, int $grid): array
{
$mask = array_fill(0, $grid, array_fill(0, $grid, 0));
$cx = (int)floor(($grid - 1) / 2);
$cy = (int)floor(($grid - 1) / 2);
$left = 1;
$right = $grid - 2;
$top = 1;
$bottom = $grid - 2;
// 脸
for ($y = $top + 1; $y <= $bottom; $y++) {
for ($x = $left + 1; $x <= $right - 1; $x++) $mask[$y][$x] = 1;
}
// 耳朵(高亮更赛博)
$mask[$top][$left + 1] = 3;
$mask[$top + 1][$left + 2] = 2;
$mask[$top][$right - 1] = 3;
$mask[$top + 1][$right - 2] = 2;
// 眼(冷酷)
$eyeY = max($top + 2, $cy - 1);
$mask[$eyeY][max($left + 1, $cx - 2)] = 4;
$mask[$eyeY][min($right - 1, $cx + 2)] = 4;
// 鼻/嘴
$noseY = $cy + 1;
if ($noseY < $grid) $mask[$noseY][$cx] = 3;
$mouthY = $cy + 2;
if ($mouthY < $grid) {
if ($cx - 1 >= 0) $mask[$mouthY][$cx - 1] = 4;
if ($cx + 1 < $grid) $mask[$mouthY][$cx + 1] = 4;
}
return $mask;
}
private static function buildDogFaceMask(string $address, int $grid): array
{
$mask = array_fill(0, $grid, array_fill(0, $grid, 0));
$cx = (int)floor(($grid - 1) / 2);
$cy = (int)floor(($grid - 1) / 2);
$left = 1;
$right = $grid - 2;
$top = 1;
$bottom = $grid - 2;
for ($y = $top + 1; $y <= $bottom; $y++) {
for ($x = $left; $x <= $right; $x++) $mask[$y][$x] = 1;
}
// 立耳(更酷)为主,偶尔垂耳
$ear = self::stableIndex('ear:' . $address, 4);
if ($ear !== 0) {
$mask[$top][$left + 1] = 3;
$mask[$top][$right - 1] = 3;
$mask[$top + 1][$left + 2] = 2;
$mask[$top + 1][$right - 2] = 2;
} else {
$mask[$top + 1][$left] = 2;
$mask[$top + 2][$left] = 2;
$mask[$top + 1][$right] = 2;
$mask[$top + 2][$right] = 2;
}
// 眉骨
$browY = max($top + 2, $cy - 2);
$mask[$browY][max($left + 1, $cx - 2)] = 2;
$mask[$browY][min($right - 1, $cx + 2)] = 2;
// 眼
$eyeY = max($top + 3, $cy - 1);
$mask[$eyeY][max($left + 1, $cx - 2)] = 4;
$mask[$eyeY][min($right - 1, $cx + 2)] = 4;
// 鼻/嘴(更硬)
$noseY = min($bottom - 2, $cy + 1);
$mask[$noseY][$cx] = 4;
if ($cx - 1 >= 0) $mask[$noseY][$cx - 1] = 4;
if ($cx + 1 < $grid) $mask[$noseY][$cx + 1] = 4;
$mouthY = min($bottom - 1, $cy + 2);
$mask[$mouthY][$cx] = 4;
return $mask;
}
private static function buildFrogFaceMask(string $address, int $grid): array
{
$mask = array_fill(0, $grid, array_fill(0, $grid, 0));
$cx = (int)floor(($grid - 1) / 2);
$cy = (int)floor(($grid - 1) / 2);
$left = 1;
$right = $grid - 2;
$top = 1;
$bottom = $grid - 2;
for ($y = $top + 1; $y <= $bottom; $y++) {
for ($x = $left; $x <= $right; $x++) $mask[$y][$x] = 1;
}
// 护目镜带
$eyeY = max($top + 2, $cy - 1);
for ($x = $left + 1; $x <= $right - 1; $x++) $mask[$eyeY][$x] = 2;
// 眼点
$mask[$eyeY][max($left + 1, $cx - 2)] = 4;
$mask[$eyeY][min($right - 1, $cx + 2)] = 4;
// 宽嘴
$mouthY = min($bottom - 1, $cy + 2);
for ($x = $cx - 3; $x <= $cx + 3; $x++) if ($x > $left && $x < $right) $mask[$mouthY][$x] = 4;
// 额头霓虹点
if ($top + 1 < $grid) {
$mask[$top + 1][$cx] = 3;
if ($cx - 2 >= 0) $mask[$top + 1][$cx - 2] = 3;
if ($cx + 2 < $grid) $mask[$top + 1][$cx + 2] = 3;
}
return $mask;
}
private static function buildAlienFaceMask(string $address, int $grid): array
{
$mask = array_fill(0, $grid, array_fill(0, $grid, 0));
$cx = (int)floor(($grid - 1) / 2);
$cy = (int)floor(($grid - 1) / 2);
$left = 1;
$right = $grid - 2;
$top = 1;
$bottom = $grid - 2;
// 梯形头(更"异形")
for ($y = $top; $y <= $bottom; $y++) {
$shrink = (int)max(0, floor(($y - $top) / 2));
$l = min($right, $left + $shrink);
$r = max($left, $right - $shrink);
for ($x = $l; $x <= $r; $x++) $mask[$y][$x] = 1;
}
// 大眼(2x2)
$eyeY = max($top + 2, $cy - 1);
$lx = max($left + 1, $cx - 2);
$rx = min($right - 1, $cx + 2);
$mask[$eyeY][$lx] = 4;
$mask[$eyeY][$lx + 1] = 4;
$mask[$eyeY + 1][$lx] = 4;
$mask[$eyeY + 1][$lx + 1] = 4;
$mask[$eyeY][$rx - 1] = 4;
$mask[$eyeY][$rx] = 4;
$mask[$eyeY + 1][$rx - 1] = 4;
$mask[$eyeY + 1][$rx] = 4;
// 冷嘴
$mouthY = min($bottom - 1, $cy + 2);
$mask[$mouthY][$cx] = 4;
if ($cx - 1 >= 0) $mask[$mouthY][$cx - 1] = 4;
if ($cx + 1 < $grid) $mask[$mouthY][$cx + 1] = 4;
// 光环/天线
if ($top - 1 >= 0) {
$mask[$top - 1][$cx] = 3;
if ($cx - 2 >= 0) $mask[$top - 1][$cx - 2] = 3;
if ($cx + 2 < $grid) $mask[$top - 1][$cx + 2] = 3;
}
// 颧骨阴影
$cheekY = $cy;
$mask[$cheekY][max($left + 1, $cx - 3)] = 2;
$mask[$cheekY][min($right - 1, $cx + 3)] = 2;
return $mask;
}
private static function buildRobotFaceMask(string $address, int $grid): array
{
$mask = array_fill(0, $grid, array_fill(0, $grid, 0));
$cx = (int)floor(($grid - 1) / 2);
$cy = (int)floor(($grid - 1) / 2);
$left = 1;
$right = $grid - 2;
$top = 1;
$bottom = $grid - 2;
// 硬朗外壳
for ($y = $top; $y <= $bottom; $y++) {
for ($x = $left; $x <= $right; $x++) $mask[$y][$x] = 1;
}
for ($x = $left; $x <= $right; $x++) {
$mask[$top][$x] = 2;
$mask[$bottom][$x] = 2;
}
for ($y = $top; $y <= $bottom; $y++) {
$mask[$y][$left] = 2;
$mask[$y][$right] = 2;
}
// 眼:更"激光"
$eyeY = max($top + 2, $cy - 1);
$mask[$eyeY][max($left + 1, $cx - 2)] = 4;
$mask[$eyeY][min($right - 1, $cx + 2)] = 4;
// 眉:更凶
$browY = $eyeY - 1;
if ($browY >= 0) {
$mask[$browY][max($left + 1, $cx - 2)] = 2;
$mask[$browY][min($right - 1, $cx + 2)] = 2;
}
// 嘴:短、冷
$mouthY = min($bottom - 1, $cy + 2);
$mask[$mouthY][$cx] = 4;
if ($cx - 1 >= 0) $mask[$mouthY][$cx - 1] = 4;
if ($cx + 1 < $grid) $mask[$mouthY][$cx + 1] = 4;
// 天线:霓虹高亮
$ant = self::stableIndex('ant:' . $address, 3);
if ($top - 1 >= 0) {
if ($ant === 0) $mask[$top - 1][$cx] = 3;
if ($ant === 1) {
if ($cx - 1 >= 0) $mask[$top - 1][$cx - 1] = 3;
if ($cx + 1 < $grid) $mask[$top - 1][$cx + 1] = 3;
}
if ($ant === 2) {
if ($cx - 2 >= 0) $mask[$top - 1][$cx - 2] = 3;
if ($cx + 2 < $grid) $mask[$top - 1][$cx + 2] = 3;
}
}
return $mask;
}
}
php
$imageBase64 = ImageIdenticon::generateBase64Avatar($address);
我这里生成的是base64数据,如果是pc或者app做渲染只需要拼接前缀就行:
php
$img = 'data:image/png;base64,' . $imageBase64