spm works maybe?

This commit is contained in:
2026-08-14 20:44:11 -04:00
parent 56646625c0
commit 10aa0c62ff
82 changed files with 1706 additions and 535 deletions
+84
View File
@@ -0,0 +1,84 @@
--:Minify:--
local lib = {}
--[[
Example framebuffer object
fb = {
r={
"","" -- repeat for # of rows length of string is the # of collums
},
g = {
"",""
},
b = {
"",""
}
}
Example font object
font = {
width=int,
hight=int,
data={
A={
"Ga","gs","Gk" -- length of string is byte alligned with the string being a binary bitmap
}
}
}
]]
function lib.newFbObj(fb)
local handle = {}
function handle.setPixel(x,y,color24)
end
function handle.line(x1,y1,x2,y2,color24)
end
function handle.box(x1,y1,x2,y2,color24)
end
function handle.filledBox(x1,y1,x2,y2,color24)
end
function handle.tri(x1,y1,x2,y2,x3,y3,color24)
end
function handle.filledTri(x1,y1,x2,y2,x3,y3,color24)
end
function handle.window(id,x,y,w,h,writeonly)
end
function handle.editWindow(id,x,y,w,h)
end
function handle.text(x,y,font,color24)
end
function handle.draw()
end
function handle.getPixel(x,y)
end
function handle.getPixels(x1y1,x2,y2)
end
return handle
end
+232
View File
@@ -0,0 +1,232 @@
-- psf.lua
-- NeetComputers PSF1 & PSF2 font renderer
-- Copyright 2026 SpartanSoftware
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
-- MODULE API
-- psf.open(path, disk = 0) Returns a Font object, or nil + errmsg
--
-- FONT OBJECT API
-- font:print(x, y, text) Draw text in white
-- font:print(x, y, text, r, g, b) Draw text in given color
-- font:measure(text) Width of string in pixels
-- font:lineHeight() Font height in pixels
-- font:setSpacing(w) Set px width of glyph spacing
-- font:close() Free all layers
local psf = {}
local function readUInt(header, n)
local bytes = header.read(n)
if not bytes or #bytes < n then return nil end
local value = 0
for i = 1, n do
value = value + string.byte(bytes, i) * (256 ^ (i - 1))
end
return math.floor(value)
end
local function parseGlyph(bytes, w, h)
local bytesPerRow = math.floor((w + 7) / 8)
local pixels = {}
for y = 1, h do
pixels[y] = {}
for x = 1, w do
local byteIdx = (y - 1) * bytesPerRow + math.floor((x - 1) / 8) + 1
local bitPos = 7 - ((x - 1) % 8)
local b = string.byte(bytes, byteIdx) or 0
pixels[y][x] = (math.floor(b / (2 ^ bitPos)) % 2 == 1)
end
end
return pixels
end
local OFF_PIXEL = "\0\0\0\0"
local function getGlyphBuffer(glyph, r, g, b)
local cache = glyph._bufCache
if not cache then
cache = {}
glyph._bufCache = cache
end
local key = r * 65536 + g * 256 + b
local buf = cache[key]
if buf then return buf end
local onPixel = string.char(r, g, b, 255)
local pixels = glyph.pixels
local chunks = {}
local n = 0
for y = 1, glyph.height do
local row = pixels[y]
for x = 1, glyph.width do
n = n + 1
chunks[n] = row[x] and onPixel or OFF_PIXEL
end
end
buf = table.concat(chunks)
cache[key] = buf
return buf
end
local function parsePSF1(header, glyphs)
local mode = readUInt(header, 1)
local charsize = readUInt(header, 1)
if not charsize then return false, "truncated PSF1 header" end
local count = (mode and (mode % 4 >= 2)) and 512 or 256
for i = 0, count - 1 do
local raw = header.read(charsize)
if not raw or #raw < charsize then break end
glyphs[i] = { width = 8, height = charsize,
pixels = parseGlyph(raw, 8, charsize) }
end
return true, 8, charsize
end
local function parsePSF2(header, glyphs)
readUInt(header, 4)
local headersize = readUInt(header, 4)
readUInt(header, 4)
local numglyph = readUInt(header, 4)
local bytesperglyph = readUInt(header, 4)
local h = readUInt(header, 4)
local w = readUInt(header, 4)
if not w or not h then return false, "truncated PSF2 header" end
local consumed = 32
if headersize > consumed then
header.seek('cur', headersize - consumed)
end
for i = 0, numglyph - 1 do
local raw = header.read(bytesperglyph)
if not raw or #raw < bytesperglyph then break end
glyphs[i] = { width = w, height = h,
pixels = parseGlyph(raw, w, h) }
end
return true, w, h
end
local Font = {}
Font.__index = Font
function Font:print(x, y, text, r, g, b)
assert(not self._closed, "psf: font has been closed")
r = r or 255
g = g or 255
b = b or 255
local sw = screen.getSize()
local cur = x
local spacing = self._spacing
for i = 1, #text do
if cur >= sw then break end
local code = string.byte(text, i)
local glyph = self._glyphs[code]
if glyph then
local buf = getGlyphBuffer(glyph, r, g, b)
screen.drawPixels(cur, y, buf, glyph.width, glyph.height)
cur = cur + glyph.width + spacing
end
end
end
function Font:measure(text)
local w = 0
local count = 0
for i = 1, #text do
local g = self._glyphs[string.byte(text, i)]
if g then
w = w + g.width
count = count + 1
end
end
if count > 1 then
w = w + self._spacing * (count - 1)
end
return w
end
function Font:lineHeight()
return self._height
end
function Font:close()
if self._closed then return end
for _, glyph in pairs(self._glyphs) do
glyph._bufCache = nil
end
self._glyphs = {}
self._closed = true
end
function Font:setSpacing(px)
self._spacing = px
end
function psf.open(path, disk)
disk = disk or 0
if not files.exists(path, disk) then
return nil, "file not found: " .. path
end
local ok, header = pcall(files.open, path, 'rb', disk)
if not ok or not header then
return nil, "could not open: " .. path
end
local magic = header.read(2)
if not magic or #magic < 2 then
header.close()
return nil, "could not read magic bytes"
end
local b1, b2 = string.byte(magic, 1), string.byte(magic, 2)
local glyphs = {}
local ok, w, h
if b1 == 0x36 and b2 == 0x04 then
ok, w, h = parsePSF1(header, glyphs)
elseif b1 == 0x72 and b2 == 0xb5 then
header.read(2)
ok, w, h = parsePSF2(header, glyphs)
else
header.close()
return nil, string.format("not a PSF file (magic: %02x %02x)", b1, b2)
end
header.close()
if not ok then
return nil, w
end
return setmetatable({
_glyphs = glyphs,
_width = w,
_height = h,
_spacing = 0,
_closed = false,
}, Font)
end
return psf