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/backup/binfmt.js

165 lines
4.6 KiB

// Binary Framebuffer Encoder
// Converts pixel data to binary format for bus display
const MAGIC = [66, 78, 23, 238];
const DrawEntryType = {
Image: 1,
Animation: 2,
HScroll: 3,
VScroll: 4,
Line: 5
};
function bitmapSizeBytes(width, height) {
return Math.floor((width * height + 7) / 8);
}
function setPixelInBitmap(x, y, memory, width, height, value) {
const index = y * width + x;
const byteIndex = Math.floor(index / 8);
const bitIndex = index % 8;
if (value) {
memory[byteIndex] |= 1 << bitIndex;
} else {
memory[byteIndex] &= ~(1 << bitIndex);
}
}
class BinaryFramebufferEncoder {
entries = [];
addImage(posX, posY, width, height, pixelData) {
const data = new Uint8Array(bitmapSizeBytes(width, height));
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
setPixelInBitmap(x, y, data, width, height, pixelData[y][x]);
}
}
this.entries.push({
type: DrawEntryType.Image,
header: { posX, posY },
width,
height,
data
});
}
addAnimation(posX, posY, width, height, frameCount, updateInterval, frames) {
const frameSize = bitmapSizeBytes(width, height);
const data = new Uint8Array(frameSize * frameCount);
for (let f = 0; f < frameCount; f++) {
const frameData = data.subarray(f * frameSize, (f + 1) * frameSize);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
setPixelInBitmap(x, y, frameData, width, height, frames[f][y][x]);
}
}
}
this.entries.push({
type: DrawEntryType.Animation,
header: { posX, posY },
width,
height,
frameCount,
updateInterval,
data
});
}
encode() {
let totalSize = 5;
for (const entry of this.entries) {
totalSize += 3;
switch (entry.type) {
case DrawEntryType.Image:
totalSize += 2 + entry.data.length;
break;
case DrawEntryType.Animation:
totalSize += 4 + entry.data.length;
break;
}
}
const buffer = new Uint8Array(totalSize);
let pos = 0;
buffer.set(MAGIC, pos);
pos += 4;
buffer[pos++] = this.entries.length;
for (const entry of this.entries) {
buffer[pos++] = entry.type;
buffer[pos++] = entry.header.posX;
buffer[pos++] = entry.header.posY;
switch (entry.type) {
case DrawEntryType.Image:
buffer[pos++] = entry.width;
buffer[pos++] = entry.height;
buffer.set(entry.data, pos);
pos += entry.data.length;
break;
case DrawEntryType.Animation:
buffer[pos++] = entry.width;
buffer[pos++] = entry.height;
buffer[pos++] = entry.frameCount;
buffer[pos++] = entry.updateInterval;
buffer.set(entry.data, pos);
pos += entry.data.length;
break;
}
}
return buffer;
}
}
function parseJSONFrames(text, width = 120, height = 60, maxFrames = 50) {
const json = JSON.parse(text);
if (!Array.isArray(json)) {
throw new Error("JSON must be an array of frames");
}
const frames = [];
for (const frame of json) {
if (!Array.isArray(frame)) {
throw new Error("Each frame must be an array of coordinates");
}
const pixels = Array(height).fill(null).map(() => Array(width).fill(false));
for (const change of frame) {
const [x, y] = change;
if (y < pixels.length && x < pixels[y].length) {
pixels[y][x] = true;
}
}
if (frames.length >= maxFrames) continue;
frames.push(pixels);
}
return frames;
}
function jsonToBinary(jsonString, options = {}) {
const {
startX = 0,
startY = 0,
updateInterval = 3,
width = 120,
height = 60,
maxFrames = 50
} = options;
const encoder = new BinaryFramebufferEncoder();
const frames = parseJSONFrames(jsonString, width, height, maxFrames);
if (frames.length === 1) {
encoder.addImage(startX, startY, width, height, frames[0]);
} else {
encoder.addAnimation(startX, startY, width, height, frames.length, updateInterval, frames);
}
return encoder.encode();
}
// Helper function to drop every nth frame from a frame array
function dropEveryNthFrame(frames, n) {
if (n <= 1 || frames.length <= 1) return frames;
return frames.filter((frame, index) => (index + 1) % n !== 0);
}
// Helper function to keep 1 frame, drop 2 frames in repeating pattern
// Pattern: keep, drop, drop, keep, drop, drop, ...
function keepOneDropTwo(frames) {
if (frames.length <= 1) return frames;
return frames.filter((frame, index) => index % 3 === 0);
}