-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathblockies.ts
132 lines (108 loc) · 3.5 KB
/
blockies.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright © 2022 The Radicle Design System Contributors
//
// This file is part of radicle-design-system, distributed under the GPLv3
// with Radicle Linking Exception. For full terms see the included
// LICENSE file.
// Copyright (c) 2019, Ethereum Name Service
// The random number is a js implementation of the Xorshift PRNG
const randseed = new Array(4); // Xorshift: [x, y, z, w] 32 bit values
function seedrand(seed: string) {
for (let i = 0; i < randseed.length; i++) {
randseed[i] = 0;
}
for (let i = 0; i < seed.length; i++) {
randseed[i % 4] =
(randseed[i % 4] << 5) - randseed[i % 4] + seed.charCodeAt(i);
}
}
function rand(): number {
// Based on Java's String.hashCode(), expanded to 4 32bit values.
const t = randseed[0] ^ (randseed[0] << 11);
randseed[0] = randseed[1];
randseed[1] = randseed[2];
randseed[2] = randseed[3];
randseed[3] = randseed[3] ^ (randseed[3] >> 19) ^ t ^ (t >> 8);
return (randseed[3] >>> 0) / ((1 << 31) >>> 0);
}
function createColor(): string {
// Saturation is the whole color spectrum.
const h = Math.floor(rand() * 360);
// Saturation goes from 40 to 100, it avoids greyish colors.
const s = `${rand() * 60 + 40}%`;
// Lightness can be anything from 0 to 100, but probabilities are a bell curve around 50%.
const l = `${(rand() + rand() + rand() + rand()) * 25}%`;
return `hsl(${h}, ${s}, ${l})`;
}
function createImageData(size: number): number[] {
const width = size;
const height = size;
const dataWidth = Math.ceil(width / 2);
const mirrorWidth = width - dataWidth;
const data = [];
for (let y = 0; y < height; y++) {
let row = [];
for (let x = 0; x < dataWidth; x++) {
// this makes foreground and background color to have a 43% (1/2.3) probability
// spot color has 13% chance
row[x] = Math.floor(rand() * 2.3);
}
const r = row.slice(0, mirrorWidth);
r.reverse();
row = row.concat(r);
for (let i = 0; i < row.length; i++) {
data.push(row[i]);
}
}
return data;
}
function createCanvas(
imageData: number[],
color: string,
scale: number,
bgcolor: string,
spotcolor: string
): HTMLCanvasElement {
const c = document.createElement("canvas");
const width = Math.sqrt(imageData.length);
c.width = c.height = width * scale;
const cc = c.getContext("2d");
if (!cc) {
throw new Error("Can't get 2D context");
}
cc.fillStyle = bgcolor;
cc.fillRect(0, 0, c.width, c.height);
cc.fillStyle = color;
for (let i = 0; i < imageData.length; i++) {
const row = Math.floor(i / width);
const col = i % width;
// if data is 2, choose spot color, if 1 choose foreground
cc.fillStyle = imageData[i] === 1 ? color : spotcolor;
// if data is 0, leave the background
if (imageData[i]) {
cc.fillRect(col * scale, row * scale, scale, scale);
}
}
return c;
}
export interface Options {
seed: string;
size: number;
scale: number;
color?: string;
bgcolor?: string;
spotcolor?: string;
}
export function createIcon(opts: Options): HTMLCanvasElement {
opts = opts || {};
const size = opts.size || 8;
const scale = opts.scale || 4;
const seed =
opts.seed || Math.floor(Math.random() * Math.pow(10, 16)).toString(16);
seedrand(seed);
const color = opts.color || createColor();
const bgcolor = opts.bgcolor || createColor();
const spotcolor = opts.spotcolor || createColor();
const imageData = createImageData(size);
const canvas = createCanvas(imageData, color, scale, bgcolor, spotcolor);
return canvas;
}