basic graphics stack
everything is defined in graphics.js it is curently very basic et poorly performing but png/jpg files can be displayed
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
syscall.node
|
||||
ioctl.node
|
||||
mount.node
|
||||
file.node
|
||||
file.node
|
||||
node_modules
|
||||
package-lock.json
|
||||
@@ -0,0 +1,150 @@
|
||||
//@ts-check
|
||||
const fs = require('fs')
|
||||
const jimp = require('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
|
||||
*/
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
function clear() {
|
||||
buffer.fill(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} filepath
|
||||
*/
|
||||
function removeFromCache(filepath) {
|
||||
delete imageCache[filepath]
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { number } x
|
||||
* @param { number } y
|
||||
* @param { string } filepath
|
||||
* //!\\ THE CACHE DOESN'T CLEAN ITSELF UP //!\\
|
||||
* once you finished displaying a image use removeFromCache to free the associated memory
|
||||
*/
|
||||
async function drawImgFile(x, y, filepath) {
|
||||
let bitmap
|
||||
//not caching would imply reading the image each time we want to write it to the screen even for small movement
|
||||
if(!imageCache[filepath]) {
|
||||
const img = await jimp.read(filepath)
|
||||
bitmap = img.bitmap
|
||||
bitmap.data = rgbaTObgra(bitmap.data)
|
||||
imageCache[filepath] = bitmap
|
||||
} else {
|
||||
bitmap = imageCache[filepath]
|
||||
}
|
||||
|
||||
|
||||
|
||||
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)
|
||||
buffer.set(Array.from(line), dx * screen.byteDepth + screen.width * (dy + y) * screen.byteDepth)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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[] }
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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)
|
||||
}
|
||||
|
||||
module.exports = { flip, clear, getScreenDimensions, drawImgFile, parseHexColor, setPixel }
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.10"
|
||||
"@types/node": "^16.11.21"
|
||||
},
|
||||
"dependencies": {
|
||||
"jimp": "^0.16.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
//@ts-check
|
||||
const graphics = require('../../chibrax/graphics')
|
||||
|
||||
graphics.clear()
|
||||
drawChibrax()
|
||||
|
||||
graphics.flip()
|
||||
|
||||
function drawChibrax() {
|
||||
//LA LETTRE C
|
||||
for(let i = 0; i < 100; i++) {
|
||||
for(let j = 0; j < 10; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
for(let i = 0; i < 20; i++) {
|
||||
for(let j = 10; j < 110; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
for(let i = 0; i < 100; i++) {
|
||||
for(let j = 110; j < 120; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//LA LETTRE H
|
||||
for(let i = 110; i < 130; i++) {
|
||||
for(let j = 0; j < 100; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
for(let i = 110; i < 210; i++) {
|
||||
for(let j = 50; j < 60; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
for(let i = 210; i < 230; i++) {
|
||||
for(let j = 0; j < 100; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
//LA LETTRE I
|
||||
for(let i = 240; i < 260; i++) {
|
||||
for(let j = 0; j < 100; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
//LA LETTRE B
|
||||
for(let i = 270; i < 280; i++) {
|
||||
for(let j = 0; j < 100; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
for(let i = 380; i < 390; i++) {
|
||||
for(let j = 0; j < 100; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
for(let i = 270; i < 390; i++) {
|
||||
for(let j = 0; j < 10; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
for(let i = 270; i < 390; i++) {
|
||||
for(let j = 50; j < 60; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
for(let i = 270; i < 390; i++) {
|
||||
for(let j = 90; j < 100; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
//LA LETTRE R
|
||||
for(let i = 400; i < 410; i++) {
|
||||
for(let j = 0; j < 100; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
for(let i = 510; i < 520; i++) {
|
||||
for(let j = 0; j < 100; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
for(let i = 400; i < 520; i++) {
|
||||
for(let j = 0; j < 10; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
for(let i = 400; i < 520; i++) {
|
||||
for(let j = 50; j < 60; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
//LA LETTRE E
|
||||
for(let i = 530; i < 630; i++) {
|
||||
for(let j = 0; j < 10; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
for(let i = 530; i < 550; i++) {
|
||||
for(let j = 10; j < 110; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
for(let i = 530; i < 630; i++) {
|
||||
for(let j = 50; j < 60; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
for(let i = 530; i < 630; i++) {
|
||||
for(let j = 110; j < 120; j++) {
|
||||
graphics.setPixel(i,j, [0,0,255,0])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user