-
Notifications
You must be signed in to change notification settings - Fork 0
/
binarize.js
143 lines (120 loc) · 3.35 KB
/
binarize.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
/**
* Converts 128x64 png frames to binary data to store in MCU memory / SD-card
* @license MIT
* @author Yurchenko Ilya
*/
import fs from 'fs'
import { PNG } from 'pngjs'
const folder = './frames';
const frames = fs.readdirSync(folder);
/**
* if Brightness > THR, then consider WHITE
* @description NOTE: The ffmpeg conversion preserves some grayscale glitches, we have to decide each pixel to be black or white explicitly
*/
const THR = 7;
/**
* Converts images with selected indicies to two C-styled flat *uint8t* array
* strings with equal offset between frames (512 bytes)
* @see animationFrom
* @see animationTill
*/
const ANIMATION_MODE = 0;
/**
* Converts a single image to two C-styled uint8t array strings, 512 bytes each
* @see animationFrom
* @see animationTill
*/
const ONESHOT_MODE = 1;
/**
* Converts every frame to two C-styled uint8t array strings, 512 bytes each
*/
const ALL_MODE = 2;
const MODE = ONESHOT_MODE;
/**
* @see ANIMATION_MODE
*/
const animationFrom = 0;
const animationTill = 5;
/**
* @see ONESHOT_MODE
*/
const oneshotFrameIdx = 30;
/**
* Reads a PNG image, returns a Promise with inline Buffer color data [R0 G0 B0 A0], ...
* @param {string} path path to the PNG file
* @returns {Promise<Buffer>}
*/
async function loadPng(path) {
return new Promise((resolve) => {
fs.createReadStream(folder + '/' + path)
.pipe(new PNG({
filterType: 4
}))
.on('parsed', function() {
resolve(this.data);
});
});
}
/**
* Converts PNG data to the WG12864A-like binary format
* The horizontal space of 128 pixels is divided in two halves.
* Each half is splitting vertically by 8 (64 pixels / 8 = 8 bytes per column)
* Each bit in byte identifies the corresponding pixel to be either black or white
* The encoding order is from the top left corner the right bottom one.
* @param {string} path
* @returns
*/
async function convertFrame(path) {
const pixels = await loadPng(path);
const results = [];
for (let halves = 0; halves < 128; halves += 64) {
let halfResult = [];
for (let x = halves; x < (halves + 64); ++x) {
for (let i = 0; i < 8; ++i) {
let byte = 0;
for (let n = 0; n < 8; ++n) {
const y = (i << 3) | n;
const indexInPng = ((y << 7) + x) << 2;
const color = pixels[indexInPng] > THR ? 0 : 1;
byte |= color << n;
}
halfResult.push(byte);
}
}
results.push(halfResult);
}
return results;
}
/**
* Prints a C-styled code to store data in MCU memory
* @param {number[]} first
* @param {number[]} second
*/
function printCode(first, second) {
console.log(`static uint8_t firstHalf[] = {${first.join(',')}};`);
console.log();
console.log(`static uint8_t secondHalf[] = {${second.join(',')}};`);
}
async function run() {
if (MODE == ANIMATION_MODE) {
const oneFlat = [];
const twoFlat = [];
for (let i = animationFrom; i < animationTill; i++) {
const [one, two] = await convertFrame(frames[i]);
oneFlat.push(...one);
twoFlat.push(...two);
}
printCode(oneFlat, twoFlat);
} else if (MODE == ONESHOT_MODE) {
const [one, two] = await convertFrame(frames[oneshotFrameIdx]);
printCode(one, two);
} else if (MODE == ALL_MODE) {
for (let i = 0; i < frames.length; i++) {
console.log(`# Frame ${i+1}:`)
const [one, two] = await convertFrame(frames[i]);
printCode(one, two);
console.log();
}
}
}
run().catch(console.error);