icydraw/src/server/object/canvas.ts

78 lines
1.7 KiB
TypeScript

import { storeHex, to1D } from '../../common/convert';
import { CanvasRecord } from '../../common/types/canvas';
import { History } from './history';
import { Imager } from './imager';
import { config } from '../config';
export class Canvas {
private _fn?: (change: CanvasRecord) => void;
private _canvas!: Uint32Array;
private _imager = new Imager(
this.size,
config.persistence?.filename,
config.persistence?.debounce,
config.persistence?.forcedInterval,
);
public history = new History(config.persistence?.placeStorage);
constructor(public size = 1000) {}
public async initialize(): Promise<void> {
this._imager.registerGetCanvas(() => this._canvas);
await this.history.initialize();
const image = await this._imager.load();
if (!image) {
this._canvas = new Uint32Array(this.size * this.size).fill(0xffffff);
return;
}
this._canvas = image;
}
public setPixelAt(
pixel: string | number,
x: number,
y: number,
user: string,
) {
if (x > this.size || y > this.size || x < 0 || y < 0) {
return;
}
const color = typeof pixel === 'string' ? storeHex(pixel) : pixel;
const index = to1D(x, y, this.size);
// prevent duplicate writes
if (this._canvas[index] === color) {
return;
}
const record = {
user,
color,
x,
y,
ts: Date.now(),
};
this.history.insert(record);
this._canvas[index] = color;
if (this._fn) {
this._fn(record);
}
this._imager.save();
}
public getFullPlane(): Uint32Array {
return this._canvas;
}
public registerChangeHandler(handler: (change: CanvasRecord) => void) {
this._fn = handler;
}
}