[TSM.ID].[11031972] PXE : Platform X Ecosystem I [118 Module -LIVE-]
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* XCU PulsarCodec — TypeScript Port
|
||||
* Delta-based pixel compression codec ported from Rust (xcu-wasm-sdk/pulsar.rs)
|
||||
*
|
||||
* Protocol: 7 bytes per changed pixel [X_hi, X_lo, Y_hi, Y_lo, R, G, B]
|
||||
*
|
||||
* This is the PRIMARY video codec for XCU. H.264 WebCodecs is the fallback.
|
||||
* PulsarCodec sends ONLY pixels that changed since the last frame,
|
||||
* achieving near-zero bandwidth when the scene is static.
|
||||
*/
|
||||
|
||||
const NEUROMORPHIC_THRESHOLD = 25;
|
||||
|
||||
export class XCUPulsarCodec {
|
||||
private lastFrame: Uint8Array;
|
||||
private canvasBuffer: Uint8Array;
|
||||
private width: number;
|
||||
private height: number;
|
||||
|
||||
constructor(width: number, height: number) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.lastFrame = new Uint8Array(width * height * 4);
|
||||
this.canvasBuffer = new Uint8Array(width * height * 4);
|
||||
// Initialize canvas buffer to black with full alpha
|
||||
for (let i = 3; i < this.canvasBuffer.length; i += 4) {
|
||||
this.canvasBuffer[i] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode delta: Compare current RGBA frame to last frame.
|
||||
* Output only changed pixels as 7-byte chunks [X_hi, X_lo, Y_hi, Y_lo, R, G, B]
|
||||
*/
|
||||
encodeDelta(currentFrame: Uint8Array): Uint8Array {
|
||||
const deltaChunks: number[] = [];
|
||||
|
||||
for (let i = 0; i < currentFrame.length; i += 4) {
|
||||
const currR = currentFrame[i];
|
||||
const currG = currentFrame[i + 1];
|
||||
const currB = currentFrame[i + 2];
|
||||
|
||||
const lastR = this.lastFrame[i];
|
||||
const lastG = this.lastFrame[i + 1];
|
||||
const lastB = this.lastFrame[i + 2];
|
||||
|
||||
const diffTotal = Math.abs(currR - lastR) + Math.abs(currG - lastG) + Math.abs(currB - lastB);
|
||||
|
||||
if (diffTotal > NEUROMORPHIC_THRESHOLD) {
|
||||
const pixelIndex = (i / 4) | 0;
|
||||
const x = pixelIndex % this.width;
|
||||
const y = (pixelIndex / this.width) | 0;
|
||||
|
||||
// 7-byte format: [X_hi, X_lo, Y_hi, Y_lo, R, G, B]
|
||||
deltaChunks.push(
|
||||
(x >> 8) & 0xFF, x & 0xFF,
|
||||
(y >> 8) & 0xFF, y & 0xFF,
|
||||
currR, currG, currB,
|
||||
);
|
||||
|
||||
// Update last frame (only changed pixels)
|
||||
this.lastFrame[i] = currR;
|
||||
this.lastFrame[i + 1] = currG;
|
||||
this.lastFrame[i + 2] = currB;
|
||||
}
|
||||
}
|
||||
|
||||
return new Uint8Array(deltaChunks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode truecolor: Render RGB asli dari delta payload ke canvas buffer.
|
||||
* Berbeda dengan decode_xray di Rust yang render hijau neon.
|
||||
*/
|
||||
decodeTruecolor(payload: Uint8Array): Uint8Array {
|
||||
for (let i = 0; i + 6 < payload.length; i += 7) {
|
||||
const x = (payload[i] << 8) | payload[i + 1];
|
||||
const y = (payload[i + 2] << 8) | payload[i + 3];
|
||||
const r = payload[i + 4];
|
||||
const g = payload[i + 5];
|
||||
const b = payload[i + 6];
|
||||
|
||||
const bufIdx = (y * this.width + x) * 4;
|
||||
if (bufIdx >= 0 && bufIdx + 3 < this.canvasBuffer.length) {
|
||||
this.canvasBuffer[bufIdx] = r;
|
||||
this.canvasBuffer[bufIdx + 1] = g;
|
||||
this.canvasBuffer[bufIdx + 2] = b;
|
||||
this.canvasBuffer[bufIdx + 3] = 255; // Full alpha
|
||||
}
|
||||
}
|
||||
return this.canvasBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset codec state (e.g., when switching video source)
|
||||
*/
|
||||
reset(): void {
|
||||
this.lastFrame.fill(0);
|
||||
this.canvasBuffer.fill(0);
|
||||
for (let i = 3; i < this.canvasBuffer.length; i += 4) {
|
||||
this.canvasBuffer[i] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
get bufferWidth(): number { return this.width; }
|
||||
get bufferHeight(): number { return this.height; }
|
||||
}
|
||||
Reference in New Issue
Block a user