Skip to content

Instantly share code, notes, and snippets.

@aqua-lzma
Created February 8, 2019 17:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aqua-lzma/3e74763c615b5efaef2c48ffc244b759 to your computer and use it in GitHub Desktop.
Save aqua-lzma/3e74763c615b5efaef2c48ffc244b759 to your computer and use it in GitHub Desktop.
Ascii art with xterm colours, but it takes forever
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
canvas {
max-height: 50px;
/* image-rendering: optimizespeed; */
padding: 5px;
}
</style>
<script>
let xterm = ['#000000','#00005f','#000087','#0000af','#0000d7','#0000ff','#005f00','#005f5f','#005f87','#005faf','#005fd7','#005fff','#008700','#00875f','#008787','#0087af','#0087d7','#0087ff','#00af00','#00af5f','#00af87','#00afaf','#00afd7','#00afff','#00d700','#00d75f','#00d787','#00d7af','#00d7d7','#00d7ff','#00ff00','#00ff5f','#00ff87','#00ffaf','#00ffd7','#00ffff','#5f0000','#5f005f','#5f0087','#5f00af','#5f00d7','#5f00ff','#5f5f00','#5f5f5f','#5f5f87','#5f5faf','#5f5fd7','#5f5fff','#5f8700','#5f875f','#5f8787','#5f87af','#5f87d7','#5f87ff','#5faf00','#5faf5f','#5faf87','#5fafaf','#5fafd7','#5fafff','#5fd700','#5fd75f','#5fd787','#5fd7af','#5fd7d7','#5fd7ff','#5fff00','#5fff5f','#5fff87','#5fffaf','#5fffd7','#5fffff','#870000','#87005f','#870087','#8700af','#8700d7','#8700ff','#875f00','#875f5f','#875f87','#875faf','#875fd7','#875fff','#878700','#87875f','#878787','#8787af','#8787d7','#8787ff','#87af00','#87af5f','#87af87','#87afaf','#87afd7','#87afff','#87d700','#87d75f','#87d787','#87d7af','#87d7d7','#87d7ff','#87ff00','#87ff5f','#87ff87','#87ffaf','#87ffd7','#87ffff','#af0000','#af005f','#af0087','#af00af','#af00d7','#af00ff','#af5f00','#af5f5f','#af5f87','#af5faf','#af5fd7','#af5fff','#af8700','#af875f','#af8787','#af87af','#af87d7','#af87ff','#afaf00','#afaf5f','#afaf87','#afafaf','#afafd7','#afafff','#afd700','#afd75f','#afd787','#afd7af','#afd7d7','#afd7ff','#afff00','#afff5f','#afff87','#afffaf','#afffd7','#afffff','#d70000','#d7005f','#d70087','#d700af','#d700d7','#d700ff','#d75f00','#d75f5f','#d75f87','#d75faf','#d75fd7','#d75fff','#d78700','#d7875f','#d78787','#d787af','#d787d7','#d787ff','#d7af00','#d7af5f','#d7af87','#d7afaf','#d7afd7','#d7afff','#d7d700','#d7d75f','#d7d787','#d7d7af','#d7d7d7','#d7d7ff','#d7ff00','#d7ff5f','#d7ff87','#d7ffaf','#d7ffd7','#d7ffff','#ff0000','#ff005f','#ff0087','#ff00af','#ff00d7','#ff00ff','#ff5f00','#ff5f5f','#ff5f87','#ff5faf','#ff5fd7','#ff5fff','#ff8700','#ff875f','#ff8787','#ff87af','#ff87d7','#ff87ff','#ffaf00','#ffaf5f','#ffaf87','#ffafaf','#ffafd7','#ffafff','#ffd700','#ffd75f','#ffd787','#ffd7af','#ffd7d7','#ffd7ff','#ffff00','#ffff5f','#ffff87','#ffffaf','#ffffd7','#ffffff','#080808','#121212','#1c1c1c','#262626','#303030','#3a3a3a','#444444','#4e4e4e','#585858','#626262','#6c6c6c','#767676','#808080','#8a8a8a','#949494','#9e9e9e','#a8a8a8','#b2b2b2','#bcbcbc','#c6c6c6','#d0d0d0','#dadada','#e4e4e4','#eeeeee']
function getColours (blob) {
let set = new Set()
for (let i = 0; i < blob.data.length; i += 4) {
set.add(`${blob.data[i].toString(16).padStart(2, '0')}${blob.data[i + 1].toString(16).padStart(2, '0')}${blob.data[i + 2].toString(16).padStart(2, '0')}`)
}
let out = new Set()
for (let i of [...set]) {
let best = Infinity
let bC
for (let col of xterm) {
let dif = Math.abs(Number.parseInt(col.slice(1, 3), 16) - Number.parseInt(i.slice(0, 2), 16)) + Math.abs(Number.parseInt(col.slice(3, 5), 16) - Number.parseInt(i.slice(2, 4), 16)) + Math.abs(Number.parseInt(col.slice(5, 7), 16) - Number.parseInt(i.slice(4, 6), 16))
if (dif < best) {
best = dif
bC = col
}
if (dif === 0) break
}
out.add(bC)
}
return ['#00000000', ...out]
}
function drawImage() {
let img = document.createElement('img')
img.src = document.querySelector('#fileName').value
img.style.display = 'none'
document.body.appendChild(img)
img.onload = () => {
let canvas = document.createElement('canvas')
let charWidth = Number(document.querySelector('#charWidth').value)
let charHeight = Number(document.querySelector('#charHeight').value)
let fontHeight = Number(document.querySelector('#fontHeight').value)
let xChars = Math.ceil(img.width / charWidth)
let yChars = Math.ceil(img.height / charHeight)
canvas.width = img.width
canvas.height = img.height
document.body.appendChild(canvas)
let ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0)
let spaced = document.createElement('canvas')
spaced.width = img.width + xChars + 1
spaced.height = img.height + yChars + 1
document.body.appendChild(spaced)
let nctx = spaced.getContext('2d')
nctx.fillStyle = 'red'
nctx.fillRect(0, 0, spaced.width, spaced.height)
for (let yi = 0; yi < yChars; yi++) {
for (let xi = 0; xi < xChars; xi++) {
// nctx.fillRect(xi * (charWidth + 1), yi * (charHeight + 1), charWidth, charHeight)
let blob = ctx.getImageData(xi * charWidth, yi * charHeight, charWidth, charHeight)
nctx.putImageData(blob, xi * (charWidth + 1), yi * (charHeight + 1))
}
}
document.body.appendChild(document.createElement('br'))
let test = document.createElement('canvas')
let best = document.createElement('canvas')
let out = document.createElement('canvas')
test.width = charWidth
best.width = charWidth
out.width = img.width
test.height = charHeight
best.height = charHeight
out.height = img.height
document.body.appendChild(out)
let tctx = test.getContext('2d')
let bctx = best.getContext('2d')
let octx = out.getContext('2d')
tctx.font = document.querySelector('#font').value
bctx.font = document.querySelector('#font').value
for (let yi = 0; yi < yChars; yi++) {
document.querySelector('textarea').textContent += 'echo -e "'
for (let xi = 0; xi < xChars; xi++) {
let blob = ctx.getImageData(xi * charWidth, yi * charHeight, charWidth, charHeight)
let bestVal = Infinity
let bestData = ''
let cols = getColours(blob)
for (let bgCol of cols) {
for (let fgCol of cols) {
for (let cCode = 32; cCode < 127; cCode++) {
tctx.clearRect(0, 0, charWidth, charHeight)
tctx.fillStyle = bgCol
tctx.fillRect(0, 0, charWidth, charHeight)
tctx.fillStyle = fgCol
tctx.fillText(String.fromCharCode(cCode), 0, fontHeight)
let tBlob = tctx.getImageData(0, 0, charWidth, charHeight)
let curBest = 0
for (let i = 0; i < charWidth * charHeight * 4; i++) {
curBest += Math.abs(blob.data[i] - tBlob.data[i])
if (curBest > bestVal) break
}
if (curBest < bestVal) {
bestVal = curBest
let bgCode = ''
if (bgCol !== '#00000000') bgCode = `\\e[48;5;${xterm.indexOf(bgCol) + 16}m`
let fgCode = ''
if (fgCol !== '#00000000') fgCode = `\\e[38;5;${xterm.indexOf(fgCol) + 16}m`
let char = String.fromCharCode(cCode)
if (char === '"') char = '\\"'
if (char === '`') char = '\\`'
bestData = `${bgCode}${fgCode}${char}\\e[0m`
bctx.putImageData(tBlob, 0, 0)
}
if (bestVal === 0) break
}
if (bestVal === 0) break
}
if (bestVal === 0) break
}
let bBlob = bctx.getImageData(0, 0, charWidth, charHeight)
octx.putImageData(bBlob, xi * charWidth, yi * charHeight)
document.querySelector('textarea').textContent += bestData
}
document.querySelector('textarea').textContent += '"\n'
alert()
}
}
}
</script>
</head>
<body style="background-color: grey">
<table>
<tbody>
<tr>
<td>Char width</td>
<td><input id="charWidth"></td>
</tr>
<tr>
<td>Char height</td>
<td><input id="charHeight"></td>
</tr>
<tr>
<td>Font height</td>
<td><input id="fontHeight"></td>
</tr>
<tr>
<td>Font</td>
<td><input id="font"></td>
</tr>
<tr>
<td>File Name</td>
<td><input id="fileName"></td>
<td><button onclick="drawImage()">Load</button></td>
</tr>
</tbody>
</table>
<script>
</script>
<textarea></textarea>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment