TypeScript: a grayscale image class with threshold and flip
A grayscale image is a grid of brightness values. Storing it in one flat Uint8Array of width × height bytes is compact and fast, and the pixel at column x and row y lives at index y × width + x. A class hides this index arithmetic and checks that coordinates are inside the image.
TypeScript
class GrayImage {
readonly width: number;
readonly height: number;
private data: Uint8Array;
constructor(width: number, height: number, data?: Uint8Array) {
this.width = width;
this.height = height;
this.data = data ?? new Uint8Array(width * height);
}
static fromRows(rows: number[][]): GrayImage {
const image = new GrayImage(rows[0].length, rows.length);
rows.forEach((row, y) => row.forEach((value, x) => image.set(x, y, value)));
return image;
}
get(x: number, y: number): number {
if (x < 0 || y < 0 || x >= this.width || y >= this.height) {
throw new RangeError("pixel (" + x + "," + y + ") is outside the image");
}
return this.data[y * this.width + x];
}
set(x: number, y: number, value: number): void {
this.get(x, y);
this.data[y * this.width + x] = value;
}
threshold(cutoff: number): GrayImage {
return new GrayImage(this.width, this.height, this.data.map((v) => (v > cutoff ? 255 : 0)));
}
flipHorizontal(): GrayImage {
const out = new GrayImage(this.width, this.height);
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
out.set(this.width - 1 - x, y, this.get(x, y));
}
}
return out;
}
toText(): string {
const lines: string[] = [];
for (let y = 0; y < this.height; y++) {
let line = "";
for (let x = 0; x < this.width; x++) {
line += this.get(x, y) > 127 ? "#" : ".";
}
lines.push(line);
}
return lines.join("\n");
}
}
const image = GrayImage.fromRows([
[10, 200, 30, 220],
[240, 20, 250, 40],
[60, 180, 70, 190],
]);
console.log(image.threshold(128).toText());
console.log("--");
console.log(image.threshold(128).flipHorizontal().toText());
try {
image.get(4, 0);
} catch (error) {
console.log((error as Error).message);
}