icydraw/src/server/object/imager.ts

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