/
index.js
executable file
·144 lines (127 loc) · 3.68 KB
/
index.js
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
133
134
135
136
137
138
139
140
141
142
143
144
#!/usr/bin/env node
let {promisify} = require("util")
let {existsSync, promises: fs} = require("fs")
let resolvePath = require("path").resolve
let findColor = require("./find-color").find
let readDataUri = require("data-uri-to-buffer")
let createSlices = promisify(require("image-to-slices"))
let {EOL} = require("os")
let getImageSize = require("image-size")
let Canvas = require("canvas")
let Colorthief = require("color-thief")
let colors = require("blee").all
function getColorFromName(name) {
let color = colors.get(name)
if (!color) {
throw `couldn't turn ${name} into a color :(`
}
let {r, g, b} = color
return [r, g, b]
}
let {argv} = require("yargs").command({
command: "$0 <image>",
describe: "make an emoji mosaic from an image",
builder: yargs => {
yargs
.options({
tilesize: {
alias: ["t"],
description: "the number of pixels an emoji should represent",
type: "number",
default: 32,
},
background: {
alias: ["b"],
description:
'a colour like "white" or "#ff2a50" to use in place of transparent tiles',
type: "string",
default: "white",
coerce: getColorFromName,
},
})
.positional("image", {
describe: "the image to turn into an emoji mosaic",
validate: existsSync,
})
},
})
createSlices.configure({
clipperOptions: {
canvas: require("canvas"),
},
})
let print = thing => process.stdout.write(thing.toString())
function parseLine(line, lineno) {
// TODO: stick mathias's emoji regex in here for bants
let match = line.match(/(.*)\t([0-9]+)\t([0-9]+)\t([0-9]+)\t([0-9]+)/)
if (!match) {
throw new Error(`bad line: ${line} (${lineno + 1})`)
}
let [, emoji, red, green, blue, alpha] = match
// ignoring alpha here
return {
emoji,
color: [red, green, blue],
}
}
async function readDatabase(databasePath) {
let contents = await fs.readFile(databasePath, "utf-8")
let lines = contents.trim().split("\n")
return lines.map(parseLine)
}
let databasePath = resolvePath(__dirname, "database")
function createRange(from, to, step) {
let output = [from]
while (from + step < to) {
output.push((from = from + step))
}
return output
}
function createLines(n) {
return createRange(argv.tilesize, n, argv.tilesize)
}
async function getImageContext(imageData, size = getImageSize(imageData)) {
let canvas = Canvas.createCanvas(size.width, size.height)
let context = canvas.getContext("2d")
let imageElement = new Canvas.Image()
return new Promise((resolve, reject) => {
imageElement.onload = () => {
context.drawImage(imageElement, 0, 0)
resolve(context)
}
imageElement.onerror = reject
imageElement.src = imageData
})
}
let colorthief = new Colorthief()
function getDominantColor(buffer) {
return colorthief.getColor(buffer)
}
;(async function beepBoopBork() {
let database = await readDatabase(databasePath)
let emojiColors = database.map(line => line.color)
let imageData = await fs.readFile(argv.image)
let context = await getImageContext(imageData)
let {width, height} = getImageSize(imageData)
let lines = [createLines(height), createLines(width)]
// this always throws? ^_^ what
let slices = await createSlices(argv.image, ...lines, {
saveToDataUrl: true,
}).catch(what => what)
// all in one big loop !!
let previousSlice
for (let slice of slices) {
if (previousSlice && slice.y > previousSlice.y) {
print(EOL)
}
let buffer = readDataUri(slice.dataURI)
let dominantColor = await getDominantColor(buffer)
let color = dominantColor || argv.background
let closest = findColor(color, emojiColors)
let match = database.find(line => closest === line.color)
let emoji = match && match.emoji
print(emoji + " ")
previousSlice = slice
}
print(EOL)
})().catch(console.error)