Add Javascript implementation
Add a simple JS port of colorhash for easier demonstration purposes. You don't need to open a command line to create a colorhash, you can just use the webpage. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
@@ -1,42 +1,42 @@
|
|||||||
<svg width="256" height="160" xmlns="http://www.w3.org/2000/svg">
|
<svg width="256" height="160" xmlns="http://www.w3.org/2000/svg">
|
||||||
<rect x="0" y="0" width="32" height="32" fill="hsl(0.00,100.00%,83.33%)" />
|
<rect x="0" y="0" width="32" height="32" fill="hsl(180.00,100.00%,20.00%)" />
|
||||||
<rect x="32" y="0" width="32" height="32" fill="hsl(0.00,100.00%,96.67%)" />
|
<rect x="32" y="0" width="32" height="32" fill="hsl(180.00,100.00%,0.00%)" />
|
||||||
<rect x="64" y="0" width="32" height="32" fill="hsl(0.00,100.00%,56.67%)" />
|
<rect x="64" y="0" width="32" height="32" fill="hsl(180.00,100.00%,30.00%)" />
|
||||||
<rect x="96" y="0" width="32" height="32" fill="hsl(0.00,100.00%,76.67%)" />
|
<rect x="96" y="0" width="32" height="32" fill="hsl(180.00,100.00%,3.33%)" />
|
||||||
<rect x="128" y="0" width="32" height="32" fill="hsl(0.00,100.00%,76.67%)" />
|
<rect x="128" y="0" width="32" height="32" fill="hsl(180.00,100.00%,3.33%)" />
|
||||||
<rect x="160" y="0" width="32" height="32" fill="hsl(0.00,100.00%,86.67%)" />
|
<rect x="160" y="0" width="32" height="32" fill="hsl(180.00,100.00%,23.33%)" />
|
||||||
<rect x="192" y="0" width="32" height="32" fill="hsl(0.00,100.00%,50.00%)" />
|
<rect x="192" y="0" width="32" height="32" fill="hsl(180.00,100.00%,0.00%)" />
|
||||||
<rect x="224" y="0" width="32" height="32" fill="hsl(0.00,100.00%,70.00%)" />
|
<rect x="224" y="0" width="32" height="32" fill="hsl(180.00,100.00%,46.67%)" />
|
||||||
<rect x="0" y="32" width="32" height="32" fill="hsl(0.00,100.00%,93.33%)" />
|
<rect x="0" y="32" width="32" height="32" fill="hsl(180.00,100.00%,43.33%)" />
|
||||||
<rect x="32" y="32" width="32" height="32" fill="hsl(0.00,100.00%,100.00%)" />
|
<rect x="32" y="32" width="32" height="32" fill="hsl(180.00,100.00%,13.33%)" />
|
||||||
<rect x="64" y="32" width="32" height="32" fill="hsl(0.00,100.00%,63.33%)" />
|
<rect x="64" y="32" width="32" height="32" fill="hsl(180.00,100.00%,46.67%)" />
|
||||||
<rect x="96" y="32" width="32" height="32" fill="hsl(0.00,100.00%,83.33%)" />
|
<rect x="96" y="32" width="32" height="32" fill="hsl(180.00,100.00%,10.00%)" />
|
||||||
<rect x="128" y="32" width="32" height="32" fill="hsl(0.00,100.00%,63.33%)" />
|
<rect x="128" y="32" width="32" height="32" fill="hsl(180.00,100.00%,40.00%)" />
|
||||||
<rect x="160" y="32" width="32" height="32" fill="hsl(0.00,100.00%,70.00%)" />
|
<rect x="160" y="32" width="32" height="32" fill="hsl(180.00,100.00%,43.33%)" />
|
||||||
<rect x="192" y="32" width="32" height="32" fill="hsl(0.00,100.00%,50.00%)" />
|
<rect x="192" y="32" width="32" height="32" fill="hsl(180.00,100.00%,46.67%)" />
|
||||||
<rect x="224" y="32" width="32" height="32" fill="hsl(0.00,100.00%,90.00%)" />
|
<rect x="224" y="32" width="32" height="32" fill="hsl(180.00,100.00%,10.00%)" />
|
||||||
<rect x="0" y="64" width="32" height="32" fill="hsl(0.00,100.00%,60.00%)" />
|
<rect x="0" y="64" width="32" height="32" fill="hsl(180.00,100.00%,36.67%)" />
|
||||||
<rect x="32" y="64" width="32" height="32" fill="hsl(0.00,100.00%,73.33%)" />
|
<rect x="32" y="64" width="32" height="32" fill="hsl(180.00,100.00%,6.67%)" />
|
||||||
<rect x="64" y="64" width="32" height="32" fill="hsl(0.00,100.00%,90.00%)" />
|
<rect x="64" y="64" width="32" height="32" fill="hsl(180.00,100.00%,40.00%)" />
|
||||||
<rect x="96" y="64" width="32" height="32" fill="hsl(0.00,100.00%,76.67%)" />
|
<rect x="96" y="64" width="32" height="32" fill="hsl(180.00,100.00%,30.00%)" />
|
||||||
<rect x="128" y="64" width="32" height="32" fill="hsl(0.00,100.00%,60.00%)" />
|
<rect x="128" y="64" width="32" height="32" fill="hsl(180.00,100.00%,26.67%)" />
|
||||||
<rect x="160" y="64" width="32" height="32" fill="hsl(0.00,100.00%,70.00%)" />
|
<rect x="160" y="64" width="32" height="32" fill="hsl(180.00,100.00%,10.00%)" />
|
||||||
<rect x="192" y="64" width="32" height="32" fill="hsl(0.00,100.00%,96.67%)" />
|
<rect x="192" y="64" width="32" height="32" fill="hsl(180.00,100.00%,36.67%)" />
|
||||||
<rect x="224" y="64" width="32" height="32" fill="hsl(0.00,100.00%,56.67%)" />
|
<rect x="224" y="64" width="32" height="32" fill="hsl(180.00,100.00%,6.67%)" />
|
||||||
<rect x="0" y="96" width="32" height="32" fill="hsl(0.00,100.00%,73.33%)" />
|
<rect x="0" y="96" width="32" height="32" fill="hsl(180.00,100.00%,46.67%)" />
|
||||||
<rect x="32" y="96" width="32" height="32" fill="hsl(0.00,100.00%,50.00%)" />
|
<rect x="32" y="96" width="32" height="32" fill="hsl(180.00,100.00%,13.33%)" />
|
||||||
<rect x="64" y="96" width="32" height="32" fill="hsl(0.00,100.00%,100.00%)" />
|
<rect x="64" y="96" width="32" height="32" fill="hsl(180.00,100.00%,20.00%)" />
|
||||||
<rect x="96" y="96" width="32" height="32" fill="hsl(0.00,100.00%,80.00%)" />
|
<rect x="96" y="96" width="32" height="32" fill="hsl(180.00,100.00%,26.67%)" />
|
||||||
<rect x="128" y="96" width="32" height="32" fill="hsl(0.00,100.00%,96.67%)" />
|
<rect x="128" y="96" width="32" height="32" fill="hsl(180.00,100.00%,26.67%)" />
|
||||||
<rect x="160" y="96" width="32" height="32" fill="hsl(0.00,100.00%,93.33%)" />
|
<rect x="160" y="96" width="32" height="32" fill="hsl(180.00,100.00%,3.33%)" />
|
||||||
<rect x="192" y="96" width="32" height="32" fill="hsl(0.00,100.00%,83.33%)" />
|
<rect x="192" y="96" width="32" height="32" fill="hsl(180.00,100.00%,0.00%)" />
|
||||||
<rect x="224" y="96" width="32" height="32" fill="hsl(0.00,100.00%,83.33%)" />
|
<rect x="224" y="96" width="32" height="32" fill="hsl(180.00,100.00%,20.00%)" />
|
||||||
<rect x="0" y="128" width="32" height="32" fill="hsl(0.00,100.00%,86.67%)" />
|
<rect x="0" y="128" width="32" height="32" fill="hsl(180.00,100.00%,43.33%)" />
|
||||||
<rect x="32" y="128" width="32" height="32" fill="hsl(0.00,100.00%,100.00%)" />
|
<rect x="32" y="128" width="32" height="32" fill="hsl(180.00,100.00%,6.67%)" />
|
||||||
<rect x="64" y="128" width="32" height="32" fill="hsl(0.00,100.00%,100.00%)" />
|
<rect x="64" y="128" width="32" height="32" fill="hsl(180.00,100.00%,26.67%)" />
|
||||||
<rect x="96" y="128" width="32" height="32" fill="hsl(0.00,100.00%,86.67%)" />
|
<rect x="96" y="128" width="32" height="32" fill="hsl(180.00,100.00%,30.00%)" />
|
||||||
<rect x="128" y="128" width="32" height="32" fill="hsl(0.00,100.00%,73.33%)" />
|
<rect x="128" y="128" width="32" height="32" fill="hsl(180.00,100.00%,33.33%)" />
|
||||||
<rect x="160" y="128" width="32" height="32" fill="hsl(0.00,100.00%,93.33%)" />
|
<rect x="160" y="128" width="32" height="32" fill="hsl(180.00,100.00%,3.33%)" />
|
||||||
<rect x="192" y="128" width="32" height="32" fill="hsl(0.00,100.00%,70.00%)" />
|
<rect x="192" y="128" width="32" height="32" fill="hsl(180.00,100.00%,36.67%)" />
|
||||||
<rect x="224" y="128" width="32" height="32" fill="hsl(0.00,100.00%,93.33%)" />
|
<rect x="224" y="128" width="32" height="32" fill="hsl(180.00,100.00%,10.00%)" />
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.3 KiB |
420
js/colorhash.js
Normal file
420
js/colorhash.js
Normal file
@@ -0,0 +1,420 @@
|
|||||||
|
/**
|
||||||
|
* Simplified colorhash web frontend.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO - need a way to share palettes between Python and JS
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Colors
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class Color {
|
||||||
|
constructor() {
|
||||||
|
if(new.target === Color) {
|
||||||
|
throw new Error("Cannot instantiate abstract class Color");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() { return self.toHTMLColor(); }
|
||||||
|
|
||||||
|
toHTMLColor() { throw new Error("Cannot call abstract method toHTMLColor"); }
|
||||||
|
|
||||||
|
toRGB() { throw new Error("Cannot call abstract method toRGB"); }
|
||||||
|
|
||||||
|
toHSL() { throw new Error("Cannot call abstract method toHSL"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
class RGBColor extends Color {
|
||||||
|
constructor(r, g, b) {
|
||||||
|
super();
|
||||||
|
this.r = r;
|
||||||
|
this.g = g;
|
||||||
|
this.b = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
toHTMLColor() {
|
||||||
|
return `rgb(${this.r}, ${this.g}, ${this.b})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
toRGB() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
toHSL() {
|
||||||
|
// NOTE - this isn't perfect, but it should be OK for our uses.
|
||||||
|
// We don't really use the toHSL at all anyway, this is really just here for completeness.
|
||||||
|
// example of floating point biting us in the ass:
|
||||||
|
// new HSLColor(120, 50, 25).toRGB().toHSL()
|
||||||
|
// { h: 120, s: 50, l: 25.098039215686274 }
|
||||||
|
let r = this.r / 255;
|
||||||
|
let g = this.g / 255;
|
||||||
|
let b = this.b / 255;
|
||||||
|
|
||||||
|
let max = Math.max(r, g, b);
|
||||||
|
let min = Math.min(r, g, b);
|
||||||
|
|
||||||
|
let h, s;
|
||||||
|
let l = (max + min) / 2;
|
||||||
|
|
||||||
|
if (max === min) {
|
||||||
|
h = s = 0; // achromatic
|
||||||
|
} else {
|
||||||
|
let d = max - min;
|
||||||
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||||
|
switch (max) {
|
||||||
|
// not sure if braces are necessary here, this is mostly just a
|
||||||
|
// habit from C/C++ where the `switch` scope is shared
|
||||||
|
case r: {
|
||||||
|
h = (g - b) / d + (g < b ? 6 : 0);
|
||||||
|
}; break;
|
||||||
|
case g: {
|
||||||
|
h = (b - r) / d + 2;
|
||||||
|
}; break;
|
||||||
|
case b: {
|
||||||
|
h = (r - g) / d + 4;
|
||||||
|
}; break;
|
||||||
|
}
|
||||||
|
h /= 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HSLColor(h * 360, s * 100, l * 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HSLColor extends Color {
|
||||||
|
constructor(h, s, l) {
|
||||||
|
super();
|
||||||
|
this.h = h;
|
||||||
|
this.s = s;
|
||||||
|
this.l = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
toHTMLColor() {
|
||||||
|
return `hsl(${this.h}, ${this.s}%, ${this.l}%)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
toRGB() {
|
||||||
|
// nice little hack we can use to convert to RGB
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.style.backgroundColor = this.toHTMLColor();
|
||||||
|
const [_, r, g, b] = div
|
||||||
|
.style
|
||||||
|
.backgroundColor
|
||||||
|
.match(/rgb\((\d+), (\d+), (\d+)\)/)
|
||||||
|
.map(Number);
|
||||||
|
return new RGBColor(r, g, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
toHSL() {
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utilities
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// hahahahahahahaha javascript doesn't even have a zip() function
|
||||||
|
// HAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA "JUST INSTALL A DEPENDENCY"
|
||||||
|
// HAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA
|
||||||
|
const zip = (a, b) => a.map((k, i) => [k, b[i]]);
|
||||||
|
|
||||||
|
function quantize(min, max, steps) {
|
||||||
|
const dist = max - min;
|
||||||
|
return Array.from(
|
||||||
|
{length: steps},
|
||||||
|
(_, i) => min + (i * dist / (steps - 1))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a hash string to Uint8Array.
|
||||||
|
*/
|
||||||
|
function hash2array(hash) {
|
||||||
|
return Array.from(
|
||||||
|
new Uint8Array(
|
||||||
|
hash.match(/../g)
|
||||||
|
.map(e => parseInt(e, 16))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to detect the type of hash or algorithm that has been supplied.
|
||||||
|
* Examples:
|
||||||
|
*
|
||||||
|
* 'sha1' => 'sha1'
|
||||||
|
* 'ae288b06df4a460c37c836e270f9edaabffb7d6d' => 'sha1'
|
||||||
|
* 'e575e0af2fbe9d8cc3a2c13542aa1375d2f75875e55529d0889f9b46' => 'sha224'
|
||||||
|
* 'md5' => 'md5'
|
||||||
|
* 'crc32' => error, not supported
|
||||||
|
*
|
||||||
|
* etc.
|
||||||
|
*/
|
||||||
|
function detectAlgorithm(hashOrAlgo) {
|
||||||
|
const dimensions = {
|
||||||
|
md5: 32,
|
||||||
|
32: "md5",
|
||||||
|
sha1: 40,
|
||||||
|
40: "sha1",
|
||||||
|
sha224: 56,
|
||||||
|
56: "sha224",
|
||||||
|
sha256: 64,
|
||||||
|
64: "sha256",
|
||||||
|
sha384: 96,
|
||||||
|
96: "sha384",
|
||||||
|
sha512: 128,
|
||||||
|
128: "sha512",
|
||||||
|
};
|
||||||
|
|
||||||
|
hashOrAlgo = hashOrAlgo.toLowerCase();
|
||||||
|
|
||||||
|
if(hashOrAlgo.match(/^([0-9a-fA-F]{2})+$/)) {
|
||||||
|
if(dimensions.hasOwnProperty(hashOrAlgo.length)) {
|
||||||
|
return dimensions[hashOrAlgo.length];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else if(dimensions.hasOwnProperty(hashOrAlgo)) {
|
||||||
|
return dimensions[hashOrAlgo];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Palettes
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a list of HSL
|
||||||
|
*/
|
||||||
|
function hslPalette(start, end) {
|
||||||
|
if(!(start instanceof HSLColor)) {
|
||||||
|
throw new TypeError("expected param 'start' to be instance of HSLColor");
|
||||||
|
}
|
||||||
|
if(!(end instanceof HSLColor)) {
|
||||||
|
throw new TypeError("expected param 'end' to be instance of HSLColor");
|
||||||
|
}
|
||||||
|
const hues = quantize(start.h, end.h, 16);
|
||||||
|
const sats = quantize(start.s, end.s, 16);
|
||||||
|
const lights = quantize(start.l, end.l, 16);
|
||||||
|
|
||||||
|
return zip(zip(hues, sats), lights).map(
|
||||||
|
([[h, s], l]) => new HSLColor(h, s, l)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const GRADIENT_PALETTES = [
|
||||||
|
["red-light", hslPalette(new HSLColor(0, 100, 50), new HSLColor(0, 100, 100))],
|
||||||
|
["red-dark", hslPalette(new HSLColor(0, 100, 0), new HSLColor(0, 100, 50))],
|
||||||
|
//
|
||||||
|
["orange-light", hslPalette(new HSLColor(30, 100, 50), new HSLColor(30, 100, 100))],
|
||||||
|
["orange-dark", hslPalette(new HSLColor(30, 100, 0), new HSLColor(30, 100, 50))],
|
||||||
|
//
|
||||||
|
//["yellow-light", hslPalette(new HSLColor(60, 100, 50), new HSLColor(60, 100, 100))],
|
||||||
|
["yellow-dark", hslPalette(new HSLColor(60, 100, 0), new HSLColor(60, 100, 50))],
|
||||||
|
//
|
||||||
|
//["lime-light", hslPalette(new HSLColor(90, 100, 50), new HSLColor(90, 100, 100))],
|
||||||
|
//["lime-dark", hslPalette(new HSLColor(90, 100, 0), new HSLColor(90, 100, 50))],
|
||||||
|
//
|
||||||
|
["green-light", hslPalette(new HSLColor(120, 100, 50), new HSLColor(120, 100, 100))],
|
||||||
|
["green-dark", hslPalette(new HSLColor(120, 100, 0), new HSLColor(120, 100, 50))],
|
||||||
|
//
|
||||||
|
//["seafoam-light", hslPalette(new HSLColor(150, 100, 50), new HSLColor(150, 100, 100))],
|
||||||
|
//["seafoam-dark", hslPalette(new HSLColor(150, 100, 0), new HSLColor(150, 100, 50))],
|
||||||
|
//
|
||||||
|
["cyan-light", hslPalette(new HSLColor(180, 100, 50), new HSLColor(180, 100, 100))],
|
||||||
|
["cyan-dark", hslPalette(new HSLColor(180, 100, 0), new HSLColor(180, 100, 50))],
|
||||||
|
//
|
||||||
|
//["teal-light", hslPalette(new HSLColor(210, 100, 50), new HSLColor(210, 100, 100))],
|
||||||
|
//["teal-dark", hslPalette(new HSLColor(210, 100, 0), new HSLColor(210, 100, 50))],
|
||||||
|
//
|
||||||
|
["blue-light", hslPalette(new HSLColor(240, 100, 50), new HSLColor(240, 100, 100))],
|
||||||
|
["blue-dark", hslPalette(new HSLColor(240, 100, 0), new HSLColor(240, 100, 50))],
|
||||||
|
//
|
||||||
|
["purple-light", hslPalette(new HSLColor(270, 100, 50), new HSLColor(270, 100, 100))],
|
||||||
|
["purple-dark", hslPalette(new HSLColor(270, 100, 0), new HSLColor(270, 100, 50))],
|
||||||
|
//
|
||||||
|
["magenta-light", hslPalette(new HSLColor(300, 100, 50), new HSLColor(300, 100, 100))],
|
||||||
|
["magenta-dark", hslPalette(new HSLColor(300, 100, 0), new HSLColor(300, 100, 50))],
|
||||||
|
//
|
||||||
|
["pink-light", hslPalette(new HSLColor(330, 100, 50), new HSLColor(330, 100, 100))],
|
||||||
|
["pink-dark", hslPalette(new HSLColor(330, 100, 0), new HSLColor(330, 100, 50))],
|
||||||
|
//
|
||||||
|
["gray-light", hslPalette(new HSLColor(0, 0, 50), new HSLColor(0, 0, 100))],
|
||||||
|
["gray-dark", hslPalette(new HSLColor(0, 0, 0), new HSLColor(0, 0, 50))],
|
||||||
|
];
|
||||||
|
|
||||||
|
const MULTICOLOR_PALETTES = [
|
||||||
|
["rainbow", hslPalette(new HSLColor(0, 100, 50), new HSLColor(360, 0, 50))],
|
||||||
|
["rainbow-reverse", hslPalette(new HSLColor(360, 100, 50), new HSLColor(0, 0, 50))],
|
||||||
|
];
|
||||||
|
|
||||||
|
DEFAULT_PALETTES = [].concat(
|
||||||
|
GRADIENT_PALETTES,
|
||||||
|
MULTICOLOR_PALETTES,
|
||||||
|
);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Matricizer
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class Matricizer {
|
||||||
|
constructor() {
|
||||||
|
if(new.target === Matricizer) {
|
||||||
|
throw new Error("Cannot instantiate abstract class Matricizer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matricize(hash) {
|
||||||
|
throw new Error("Cannot call abstract method matricize");
|
||||||
|
}
|
||||||
|
|
||||||
|
choosePalette(hash, palettes = null) {
|
||||||
|
if(!palettes) {
|
||||||
|
palettes = DEFAULT_PALETTES;
|
||||||
|
}
|
||||||
|
const total = hash2array(hash).reduce((acc, curr) => acc + curr, 0);
|
||||||
|
|
||||||
|
return palettes[total % palettes.length][1]
|
||||||
|
}
|
||||||
|
|
||||||
|
static chooseDimensions(hashOrAlgo) {
|
||||||
|
throw new Error("Cannot call abstract method chooseDimensions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NibbleMatricizer extends Matricizer {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
matricize(hash) {
|
||||||
|
const dimensions = {
|
||||||
|
md5: [8, 4],
|
||||||
|
sha1: [8, 5],
|
||||||
|
sha224: [8, 7],
|
||||||
|
sha256: [8, 8],
|
||||||
|
sha384: [12, 8],
|
||||||
|
sha512: [16, 8],
|
||||||
|
};
|
||||||
|
|
||||||
|
const hashAlgo = detectAlgorithm(hash);
|
||||||
|
if(hashAlgo === null) {
|
||||||
|
throw new Error(`unable to determine hash algorithm`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [w, h] = dimensions[hashAlgo];
|
||||||
|
|
||||||
|
const nibbles = hash2array(hash)
|
||||||
|
.map(b => [(b & 0xf0) >> 4, b & 0x0f])
|
||||||
|
.flat();
|
||||||
|
|
||||||
|
if(nibbles.length != w * h) {
|
||||||
|
throw new Error(`invalid hash length: ${hash.length} (${nibbles.length} nibbles)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows = []
|
||||||
|
let cols = []
|
||||||
|
for(const b of nibbles) {
|
||||||
|
cols.push(b);
|
||||||
|
if(cols.length === w) {
|
||||||
|
rows.push(cols);
|
||||||
|
cols = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
choosePalette(data, palettes = null) {
|
||||||
|
return super.choosePalette(data, palettes || GRADIENT_PALETTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RandomartMatricizer extends Matricizer {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
matricize(hash) {
|
||||||
|
const dimensions = {
|
||||||
|
md5: [7, 6],
|
||||||
|
sha1: [7, 6],
|
||||||
|
sha224: [8, 7],
|
||||||
|
sha256: [8, 7],
|
||||||
|
sha384: [11, 10],
|
||||||
|
sha512: [11, 10],
|
||||||
|
}
|
||||||
|
|
||||||
|
const hashAlgo = detectAlgorithm(hash);
|
||||||
|
if(hashAlgo === null) {
|
||||||
|
throw new Error(`unable to determine hash algorithm`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [w, h] = dimensions[hashAlgo];
|
||||||
|
const bytes = hash2array(hash);
|
||||||
|
|
||||||
|
// create an empty 2d array
|
||||||
|
const rows = Array.from(
|
||||||
|
{ length: h },
|
||||||
|
() => Array.from({ length: w }, () => 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
let c = Math.floor(w / 2);
|
||||||
|
let r = Math.floor(h / 2);
|
||||||
|
|
||||||
|
// do the randomart algorithm
|
||||||
|
for(let value of bytes) {
|
||||||
|
for(let i = 0; i < 4; i++) {
|
||||||
|
if(value & 1) {
|
||||||
|
c += 1;
|
||||||
|
} else {
|
||||||
|
c -= 1;
|
||||||
|
}
|
||||||
|
if(value & 2) {
|
||||||
|
r += 1;
|
||||||
|
} else {
|
||||||
|
r -= 1;
|
||||||
|
}
|
||||||
|
c = Math.min(Math.max(c, 0), w - 1);
|
||||||
|
r = Math.min(Math.max(r, 0), h - 1);
|
||||||
|
if(rows[r][c] < 0xf) {
|
||||||
|
rows[r][c] += 1;
|
||||||
|
}
|
||||||
|
value >>= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
choosePalette(data, palettes = null) {
|
||||||
|
return super.choosePalette(data, palettes || MULTICOLOR_PALETTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// SVG output
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function genSVG(matrix, palette, squareSize = 32) {
|
||||||
|
const colors = matrix.map(
|
||||||
|
row => row.map(v => palette[v])
|
||||||
|
);
|
||||||
|
|
||||||
|
const w = colors[0].length;
|
||||||
|
const h = colors.length;
|
||||||
|
let svg = `<svg width="${w * squareSize}" height="${h * squareSize}" xmlns="http://www.w3.org/2000/svg">\n`;
|
||||||
|
|
||||||
|
for(let r = 0; r < h; r++) {
|
||||||
|
for(let c = 0; c < w; c++) {
|
||||||
|
const x = c * squareSize;
|
||||||
|
const y = r * squareSize;
|
||||||
|
const color = colors[r][c];
|
||||||
|
svg += `<rect x="${x}" y="${y}" width="${squareSize}" height="${squareSize}" fill="${color.toHTMLColor()}" />\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
svg += "</svg>";
|
||||||
|
return svg;
|
||||||
|
}
|
||||||
105
js/index.html
Normal file
105
js/index.html
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Colorhash</title>
|
||||||
|
<style>
|
||||||
|
#error {
|
||||||
|
color: #ff0000;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Create a colorhash image by typing in a hash here
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="text" style="width:400px;" autocomplete="off" id="inputhash"/><br/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="radio" id="nibble" name="art" checked="checked" />
|
||||||
|
<label for="nibble">Nibbles</label>
|
||||||
|
<input type="radio" id="randomart" name="art" />
|
||||||
|
<label for="randomart">Randomart</label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button id="createbutton" onclick="createArt()">Create</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div id="error">
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
<div id="output">
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<script src="colorhash.js" defer></script>
|
||||||
|
<script>
|
||||||
|
qs = document.querySelector.bind(document);
|
||||||
|
|
||||||
|
function clearError() {
|
||||||
|
qs("#error").innerHTML = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function setError(error) {
|
||||||
|
qs("#error").innerHTML = `<p>${error}</p>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearOutput() {
|
||||||
|
qs("#output").innerHTML = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function setOutput(output) {
|
||||||
|
qs("#output").innerHTML = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDownloadLink(svgData) {
|
||||||
|
return "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svgData);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createArt() {
|
||||||
|
clearError();
|
||||||
|
clearOutput();
|
||||||
|
|
||||||
|
const inputHash = qs("#inputhash").value;
|
||||||
|
let matricizer;
|
||||||
|
if(qs("#nibble").checked) {
|
||||||
|
matricizer = new NibbleMatricizer();
|
||||||
|
} else if(qs("#randomart").checked) {
|
||||||
|
matricizer = new RandomartMatricizer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// get matrix
|
||||||
|
let matrix;
|
||||||
|
try {
|
||||||
|
matrix = matricizer.matricize(inputHash);
|
||||||
|
} catch(e) {
|
||||||
|
setError(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get colors
|
||||||
|
const palette = matricizer.choosePalette(inputHash);
|
||||||
|
|
||||||
|
// create SVG
|
||||||
|
const svg = "<p>" + genSVG(matrix, palette) + "</p>";
|
||||||
|
|
||||||
|
const detectedHash = `<p>Detected hash: ${detectAlgorithm(inputHash)}</p>`;
|
||||||
|
const downloadLink = `<p><a href="${createDownloadLink(svg).toString()}">"Save link as" to download</a></p>`
|
||||||
|
setOutput(detectedHash + downloadLink + svg);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user