2023-07-26 22:31:52 -07:00
|
|
|
/*
|
2024-02-13 07:59:27 -08:00
|
|
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
2023-07-26 22:31:52 -07:00
|
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
2019-04-15 04:37:21 -07:00
|
|
|
/**
|
2022-01-15 17:45:48 -08:00
|
|
|
* Identicon generator
|
|
|
|
* https://en.wikipedia.org/wiki/Identicon
|
2019-04-15 04:37:21 -07:00
|
|
|
*/
|
|
|
|
|
2024-05-04 04:56:14 -07:00
|
|
|
import { createCanvas } from '@napi-rs/canvas';
|
2022-02-26 18:07:39 -08:00
|
|
|
import gen from 'random-seed';
|
2019-04-15 04:37:21 -07:00
|
|
|
|
2022-07-08 21:22:35 -07:00
|
|
|
const size = 128; // px
|
2019-04-15 04:37:21 -07:00
|
|
|
const n = 5; // resolution
|
2022-07-08 21:22:35 -07:00
|
|
|
const margin = (size / 4);
|
2019-04-15 04:37:21 -07:00
|
|
|
const colors = [
|
2022-07-08 21:22:35 -07:00
|
|
|
['#FF512F', '#DD2476'],
|
|
|
|
['#FF61D2', '#FE9090'],
|
|
|
|
['#72FFB6', '#10D164'],
|
|
|
|
['#FD8451', '#FFBD6F'],
|
|
|
|
['#305170', '#6DFC6B'],
|
|
|
|
['#00C0FF', '#4218B8'],
|
|
|
|
['#009245', '#FCEE21'],
|
|
|
|
['#0100EC', '#FB36F4'],
|
|
|
|
['#FDABDD', '#374A5A'],
|
|
|
|
['#38A2D7', '#561139'],
|
|
|
|
['#121C84', '#8278DA'],
|
|
|
|
['#5761B2', '#1FC5A8'],
|
|
|
|
['#FFDB01', '#0E197D'],
|
|
|
|
['#FF3E9D', '#0E1F40'],
|
|
|
|
['#766eff', '#00d4ff'],
|
|
|
|
['#9bff6e', '#00d4ff'],
|
|
|
|
['#ff6e94', '#00d4ff'],
|
|
|
|
['#ffa96e', '#00d4ff'],
|
|
|
|
['#ffa96e', '#ff009d'],
|
|
|
|
['#ffdd6e', '#ff009d'],
|
2019-04-15 04:37:21 -07:00
|
|
|
];
|
|
|
|
|
|
|
|
const actualSize = size - (margin * 2);
|
|
|
|
const cellSize = actualSize / n;
|
|
|
|
const sideN = Math.floor(n / 2);
|
|
|
|
|
|
|
|
/**
|
2022-01-15 17:45:48 -08:00
|
|
|
* Generate buffer of an identicon by seed
|
2019-04-15 04:37:21 -07:00
|
|
|
*/
|
2024-05-04 04:56:14 -07:00
|
|
|
export async function genIdenticon(seed: string): Promise<Buffer> {
|
2019-04-15 04:37:21 -07:00
|
|
|
const rand = gen.create(seed);
|
2024-05-04 04:56:14 -07:00
|
|
|
const canvas = createCanvas(size, size);
|
2019-04-15 04:37:21 -07:00
|
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
|
2022-07-08 21:22:35 -07:00
|
|
|
const bgColors = colors[rand(colors.length)];
|
|
|
|
|
|
|
|
const bg = ctx.createLinearGradient(0, 0, size, size);
|
|
|
|
bg.addColorStop(0, bgColors[0]);
|
|
|
|
bg.addColorStop(1, bgColors[1]);
|
|
|
|
|
2023-02-08 18:02:37 -08:00
|
|
|
ctx.fillStyle = bg as any;
|
2019-04-15 04:37:21 -07:00
|
|
|
ctx.beginPath();
|
|
|
|
ctx.fillRect(0, 0, size, size);
|
|
|
|
|
2022-07-08 21:22:35 -07:00
|
|
|
ctx.fillStyle = '#ffffff';
|
2019-04-15 04:37:21 -07:00
|
|
|
|
|
|
|
// side bitmap (filled by false)
|
|
|
|
const side: boolean[][] = new Array(sideN);
|
|
|
|
for (let i = 0; i < side.length; i++) {
|
|
|
|
side[i] = new Array(n).fill(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 1*n (filled by false)
|
|
|
|
const center: boolean[] = new Array(n).fill(false);
|
|
|
|
|
2021-11-11 17:52:10 -08:00
|
|
|
// eslint:disable-next-line:prefer-for-of
|
2019-04-15 04:37:21 -07:00
|
|
|
for (let x = 0; x < side.length; x++) {
|
|
|
|
for (let y = 0; y < side[x].length; y++) {
|
|
|
|
side[x][y] = rand(3) === 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = 0; i < center.length; i++) {
|
|
|
|
center[i] = rand(3) === 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw
|
|
|
|
for (let x = 0; x < n; x++) {
|
|
|
|
for (let y = 0; y < n; y++) {
|
|
|
|
const isXCenter = x === ((n - 1) / 2);
|
|
|
|
if (isXCenter && !center[y]) continue;
|
|
|
|
|
|
|
|
const isLeftSide = x < ((n - 1) / 2);
|
|
|
|
if (isLeftSide && !side[x][y]) continue;
|
|
|
|
|
|
|
|
const isRightSide = x > ((n - 1) / 2);
|
|
|
|
if (isRightSide && !side[sideN - (x - sideN)][y]) continue;
|
|
|
|
|
|
|
|
const actualX = margin + (cellSize * x);
|
|
|
|
const actualY = margin + (cellSize * y);
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.fillRect(actualX, actualY, cellSize, cellSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-04 04:56:14 -07:00
|
|
|
return await canvas.encode('png');
|
2019-04-15 04:37:21 -07:00
|
|
|
}
|