721c919c7b
Changes: - kernel 6.0.2 - Broken init scripts - Switch to bun for better peroformance an easier ffi - Removed grup - Efi support without graphics (at least graphics werent tested) - Suppression de toutes les dépendance non libc - new splash - broken audio - typescript support - ls rewritten to be more reliable
182 lines
4.9 KiB
JavaScript
182 lines
4.9 KiB
JavaScript
//@ts-check
|
|
import * as fs from 'fs'
|
|
import * as jimp from 'jimp'
|
|
|
|
const screen = getScreenDimensions()
|
|
const buffer = Buffer.alloc(screen.width * screen.height * screen.byteDepth)
|
|
//TODO: implement automatic cache cleanup
|
|
/**
|
|
* @type {{ [key: string]: import('@jimp/core').Bitmap }}
|
|
*/
|
|
const imageCache = {}
|
|
|
|
function getScreenDimensions() {
|
|
const vsize = fs.readFileSync('/sys/class/graphics/fb0/virtual_size', { encoding: 'utf-8' }).split(',')
|
|
const byteDepth = parseInt(fs.readFileSync('/sys/class/graphics/fb0/bits_per_pixel').toString()) / 8
|
|
|
|
return {
|
|
width: parseInt(vsize[0]),
|
|
height: parseInt(vsize[1]),
|
|
byteDepth
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param { Buffer } data
|
|
*/
|
|
export function flip(data = buffer) {
|
|
try {
|
|
fs.writeFileSync('/dev/fb0', data, { encoding: 'binary' })
|
|
} catch(e) {
|
|
console.log("could not write to /dev/fb0")
|
|
console.log(e)
|
|
}
|
|
}
|
|
|
|
export function clear() {
|
|
buffer.fill(0)
|
|
}
|
|
|
|
/**
|
|
* @param {string} filepath
|
|
*/
|
|
export function removeFromCache(filepath) {
|
|
delete imageCache[filepath]
|
|
}
|
|
|
|
/**
|
|
* @param {string} filepath
|
|
* @returns { Promise<import('@jimp/core').Bitmap> } BGRA buffer
|
|
*/
|
|
export async function readImgFile(filepath) {
|
|
const img = await jimp.read(filepath)
|
|
const bitmap = img.bitmap
|
|
bitmap.data = rgbaTObgra(bitmap.data)
|
|
return bitmap
|
|
}
|
|
|
|
/**
|
|
* @param { number } x
|
|
* @param { number } y
|
|
* @param { import('@jimp/core').Bitmap | Pencil } bitmap
|
|
*/
|
|
export function drawBitmap(x, y, bitmap, handleTransparency = true) {
|
|
for(let dy = 0; dy < bitmap.height; dy++) {
|
|
let bitmapLineEnd = bitmap.width * screen.byteDepth * (dy + 1)
|
|
let bitmapLineStart = bitmap.width * screen.byteDepth * dy
|
|
let dx = x
|
|
|
|
const offscreenRight = dx + bitmap.width - screen.width
|
|
if(offscreenRight > 0) {
|
|
bitmapLineEnd -= offscreenRight*4
|
|
}
|
|
|
|
const offscreenLeft = -dx
|
|
if(offscreenLeft > 0) {
|
|
bitmapLineStart += offscreenLeft*4
|
|
dx = 0
|
|
}
|
|
|
|
const offscreenTop = -y
|
|
if(offscreenTop >= dy) {
|
|
dy = offscreenTop
|
|
continue
|
|
}
|
|
|
|
const offscreenBottom = bitmap.height + y - screen.height
|
|
if(offscreenBottom > 0 && bitmap.height - offscreenBottom <= dy) {
|
|
break
|
|
}
|
|
|
|
if(bitmapLineEnd < bitmapLineStart) {
|
|
continue
|
|
}
|
|
|
|
|
|
const line = bitmap.data.subarray(bitmapLineStart, bitmapLineEnd)
|
|
|
|
const bufferPlacement = dx * screen.byteDepth + screen.width * (dy + y) * screen.byteDepth
|
|
|
|
if(handleTransparency) {
|
|
const currentLine = buffer.subarray(bufferPlacement, bufferPlacement + line.length)
|
|
for(let i=0; i < line.length; i += 4) {
|
|
const transparency = line.readUInt8(i+3) / 255
|
|
if(transparency < 1) {
|
|
/** @type { Uint8Array } */
|
|
const newLine = new Uint8Array(3)
|
|
for(let j=0; j < 3; j++) {
|
|
newLine[j] = (currentLine.readUInt8(j+i)*1-transparency + line.readUInt8(j+i)*transparency)
|
|
}
|
|
line.set(newLine, i)
|
|
}
|
|
}
|
|
}
|
|
|
|
buffer.set(Array.from(line), bufferPlacement)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param { number } x
|
|
* @param { number } y
|
|
* @param { string } filepath
|
|
* @description this function work fine but will use caching. to clear the cache you can use removeFromCache(filepath) or you can keep the cache on the
|
|
* client side and use readImgFile and drawBitmap methods instead
|
|
*/
|
|
export async function drawImgFile(x, y, filepath) {
|
|
//not caching would imply reading the image each time we want to write it to the screen even for small movement
|
|
let bitmap;
|
|
if(!imageCache[filepath]) {
|
|
bitmap = await readImgFile(filepath)
|
|
imageCache[filepath] = bitmap
|
|
} else {
|
|
bitmap = imageCache[filepath]
|
|
}
|
|
drawBitmap(x, y, bitmap)
|
|
}
|
|
|
|
/**
|
|
* @param { Buffer } buff
|
|
*/
|
|
function rgbaTObgra(buff) {
|
|
for(let i=0; i < buff.length; i += 4) {
|
|
const r = buff.readUInt8(i)
|
|
const b = buff.readUInt8(i+2)
|
|
|
|
buff.set([b], i)
|
|
buff.set([r], i+2)
|
|
}
|
|
return buff
|
|
}
|
|
|
|
/**
|
|
* @param { string } color
|
|
* @returns { number[] }
|
|
*/
|
|
export function parseHexColor(color) {
|
|
if(!color.startsWith('#') || color.length !== 7) {
|
|
throw new Error('invalid hex color code')
|
|
}
|
|
const r = parseInt(color.substring(1,2), 16)
|
|
const v = parseInt(color.substring(3,4), 16)
|
|
const b = parseInt(color.substring(5,6), 16)
|
|
return [b, v, r, 0]
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param { number } x
|
|
* @param { number } y
|
|
* @param { string | number[] } colorCode
|
|
*/
|
|
export function setPixel(x, y, colorCode) {
|
|
if (screen.byteDepth !== 4) {
|
|
throw new Error('Depth not supported yet')
|
|
}
|
|
|
|
if(typeof colorCode === 'string') {
|
|
colorCode = parseHexColor(colorCode)
|
|
}
|
|
buffer.set(colorCode, (x + y * screen.width) * screen.byteDepth)
|
|
}
|