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 { 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 { 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 { 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; } }