114 lines
2.8 KiB
TypeScript
114 lines
2.8 KiB
TypeScript
import Jimp from 'jimp';
|
|
import { to1D } from '../../common/convert';
|
|
import fs from 'fs/promises';
|
|
import { join } from 'path';
|
|
|
|
export class Imager {
|
|
private _saveTimer?: any;
|
|
private _saving = false;
|
|
private _lastSave = 0;
|
|
|
|
private _getCanvas?: () => Uint32Array;
|
|
|
|
constructor(
|
|
private _size: number,
|
|
private _store = join(__dirname, '..', '..', '..', 'canvas.png'),
|
|
private _debounce = 5000,
|
|
) {}
|
|
|
|
async toImage(array: Uint32Array): Promise<Buffer> {
|
|
return new Promise((resolve, reject) => {
|
|
new Jimp(this._size, this._size, (err, image) => {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
|
|
// Read data from array
|
|
for (let x = 0; x < this._size; x++) {
|
|
for (let y = 0; y < this._size; y++) {
|
|
const pixel = array[to1D(x, y, this._size)];
|
|
image.setPixelColor(pixel * 256 + 255, x, y);
|
|
}
|
|
}
|
|
|
|
image.getBuffer(Jimp.MIME_PNG, (err, buffer) => {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
|
|
resolve(buffer);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
async fromImage(buffer: Buffer): Promise<Uint32Array> {
|
|
const image = await Jimp.read(buffer);
|
|
const uint = new Uint32Array(this._size * this._size);
|
|
for (let x = 0; x < this._size; x++) {
|
|
for (let y = 0; y < this._size; y++) {
|
|
const id = to1D(x, y, this._size);
|
|
uint[id] = (image.getPixelColor(x, y) - 255) / 256;
|
|
}
|
|
}
|
|
return uint;
|
|
}
|
|
|
|
save(force = false): void {
|
|
if (!this._getCanvas) {
|
|
return;
|
|
}
|
|
|
|
clearTimeout(this._saveTimer);
|
|
|
|
if (this._saving) {
|
|
this._saveTimer = setTimeout(() => this.save(force), 1000);
|
|
return;
|
|
}
|
|
|
|
const saveFn = async () => {
|
|
this._saving = true;
|
|
console.log('Saving canvas to disk');
|
|
const start = Date.now();
|
|
|
|
const array = this._getCanvas!();
|
|
const save = await this.toImage(array);
|
|
await fs.writeFile(this._store, save);
|
|
|
|
const end = (Date.now() - start) / 1000;
|
|
console.log(`saved in ${end}s`);
|
|
this._saving = false;
|
|
this._lastSave = Date.now();
|
|
};
|
|
|
|
if (force || this._lastSave < Date.now() - this._debounce * 2) {
|
|
saveFn().catch((e) => console.error(e));
|
|
return;
|
|
}
|
|
|
|
this._saveTimer = setTimeout(
|
|
() => saveFn().catch((e) => console.error(e)),
|
|
this._debounce,
|
|
);
|
|
}
|
|
|
|
async load(): Promise<Uint32Array | null> {
|
|
try {
|
|
await fs.stat(this._store);
|
|
console.log('loading canvas from disk');
|
|
const start = Date.now();
|
|
const loadFile = await fs.readFile(this._store);
|
|
const imgData = await this.fromImage(loadFile);
|
|
const end = (Date.now() - start) / 1000;
|
|
console.log(`loaded in ${end}s`);
|
|
return imgData;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public registerGetCanvas(fn: () => Uint32Array) {
|
|
this._getCanvas = fn;
|
|
}
|
|
}
|