Abfahrtsanzeiger Display Basic Library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
libmonoformat/ts/src/helper.ts

102 lines
3.4 KiB

// ---------------------------------------------------------------------------
// Pixel pack/unpack helpers
// ---------------------------------------------------------------------------
/**
* Pack 1-byte-per-pixel array into 1bpp.
* Excess bits in the last byte are set to 0.
* Size = ceil(width * height / 8).
*/
export function packPixels(pixels: Uint8Array, width: number, height: number): Uint8Array {
const total = width * height;
const packed = new Uint8Array((total + 7) >> 3);
for (let i = 0; i < total; i++) {
if (pixels[i]) packed[i >> 3] = (packed[i >> 3] ?? 0) | (1 << (i & 7));
}
return packed;
}
/**
* Unpack 1bpp data to 1-byte-per-pixel. Excess bits beyond width*height are ignored.
*/
export function unpackPixels(packed: Uint8Array, width: number, height: number): Uint8Array {
const total = width * height;
const pixels = new Uint8Array(total);
for (let i = 0; i < total; i++) {
pixels[i] = ((packed[i >> 3] ?? 0) >> (i & 7)) & 1;
}
return pixels;
}
/** Byte count of packed 1bpp image */
export function packedSize(width: number, height: number): number {
return (width * height + 7) >> 3;
}
/** Padding needed to align `byteCount` to a 4-byte boundary */
export function pad32(byteCount: number): number {
return (4 - (byteCount & 3)) & 3;
}
// ---------------------------------------------------------------------------
// BinaryReader — little-endian cursor over an ArrayBuffer
// ---------------------------------------------------------------------------
export class BinaryReader {
private view: DataView;
private pos: number = 0;
constructor(buffer: ArrayBuffer | ArrayBufferView) {
this.view = buffer instanceof ArrayBuffer
? new DataView(buffer)
: new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
}
get offset(): number { return this.pos; }
get byteLength(): number { return this.view.byteLength; }
get remaining(): number { return this.view.byteLength - this.pos; }
readByte(): number { this.#need(1); return this.view.getUint8(this.pos++); }
readUint16(): number { this.#need(2); const v = this.view.getUint16(this.pos, true); this.pos += 2; return v; }
readShort(): number { return this.readUint16(); }
readInt16(): number { this.#need(2); const v = this.view.getInt16(this.pos, true); this.pos += 2; return v; }
readUint24(): number {
this.#need(3);
const v = this.view.getUint8(this.pos) | (this.view.getUint8(this.pos+1) << 8) | (this.view.getUint8(this.pos+2) << 16);
this.pos += 3;
return v;
}
readUint32(): number { this.#need(4); const v = this.view.getUint32(this.pos, true); this.pos += 4; return v; }
readUint64(): bigint {
this.#need(8);
const lo = BigInt(this.view.getUint32(this.pos, true));
const hi = BigInt(this.view.getUint32(this.pos+4, true));
this.pos += 8;
return (hi << 32n) | lo;
}
readBytes(n: number): Uint8Array {
this.#need(n);
const s = new Uint8Array(this.view.buffer, this.pos, n);
this.pos += n;
return s;
}
readUtf8(n: number): string { return new TextDecoder().decode(this.readBytes(n)); }
seek(pos: number): void {
if (pos < 0 || pos > this.view.byteLength) {
throw new RangeError(`seek(${pos}) out of [0, ${this.view.byteLength}]`);
}
this.pos = pos;
}
#need(n: number): void {
if (this.remaining < n) throw new RangeError(
`BinaryReader: need ${n} byte(s) at offset ${this.pos}, ${this.remaining} remain`
);
}
}