GFX update :D
This commit is contained in:
1 parent
05ca0f10a2
commit
c1b4ef2644
70 files changed
+1398
-5174
No files matched your search
@@ -0,0 +1,2 @@
|
||||
Hyperion
|
||||
Astronand
|
||||
@@ -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
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
vector
|
||||
@@ -0,0 +1 @@
|
||||
package providing kernel support for framebuffers and graphics
|
||||
@@ -0,0 +1,19 @@
|
||||
function lib.load(fd)
|
||||
function handle.setPixel(x,y,color24)
|
||||
function handle.getPixel(x,y)
|
||||
function handle.setPixels(x,y,w,framebuffer)
|
||||
function handle.getPixels(x,y,w,h)
|
||||
function handle.frame(x,y,w,h,writeonly)
|
||||
integer id
|
||||
recurse lib.load
|
||||
function handle.framectl(id)
|
||||
function frame.move(x,y)
|
||||
function frame.resize(w,h)
|
||||
function frame.free()
|
||||
function handle.sync()
|
||||
function handle.line(x1,y1,x2,y2)
|
||||
function handle.box(x,y,w,h,c,fill)
|
||||
function handle.filledBox(x,y,w,h,c)
|
||||
function handle.text(x,y,font,text)
|
||||
function handle.free()
|
||||
function handle.size()
|
||||
Whitespace-only changes.
@@ -0,0 +1,256 @@
|
||||
#000000
|
||||
#0b0b0b
|
||||
#222222
|
||||
#444444
|
||||
#555555
|
||||
#777777
|
||||
#888888
|
||||
#aaaaaa
|
||||
#bbbbbb
|
||||
#dddddd
|
||||
#eeeeee
|
||||
#00000b
|
||||
#000022
|
||||
#000044
|
||||
#000055
|
||||
#000077
|
||||
#000088
|
||||
#0000aa
|
||||
#0000bb
|
||||
#0000dd
|
||||
#0000ee
|
||||
#000b00
|
||||
#002200
|
||||
#004400
|
||||
#005500
|
||||
#007700
|
||||
#008800
|
||||
#00aa00
|
||||
#00bb00
|
||||
#00dd00
|
||||
#00ee00
|
||||
#0b0000
|
||||
#220000
|
||||
#440000
|
||||
#550000
|
||||
#770000
|
||||
#880000
|
||||
#aa0000
|
||||
#bb0000
|
||||
#dd0000
|
||||
#ee0000
|
||||
#000033
|
||||
#000066
|
||||
#000099
|
||||
#0000cc
|
||||
#0000ff
|
||||
#003300
|
||||
#003333
|
||||
#003366
|
||||
#003399
|
||||
#0033cc
|
||||
#0033ff
|
||||
#006600
|
||||
#006633
|
||||
#006666
|
||||
#006699
|
||||
#0066cc
|
||||
#0066ff
|
||||
#009900
|
||||
#009933
|
||||
#009966
|
||||
#009999
|
||||
#0099cc
|
||||
#0099ff
|
||||
#00cc00
|
||||
#00cc33
|
||||
#00cc66
|
||||
#00cc99
|
||||
#00cccc
|
||||
#00ccff
|
||||
#00ff00
|
||||
#00ff33
|
||||
#00ff66
|
||||
#00ff99
|
||||
#00ffcc
|
||||
#00ffff
|
||||
#330000
|
||||
#330033
|
||||
#330066
|
||||
#330099
|
||||
#3300cc
|
||||
#3300ff
|
||||
#333300
|
||||
#333333
|
||||
#333366
|
||||
#333399
|
||||
#3333cc
|
||||
#3333ff
|
||||
#336600
|
||||
#336633
|
||||
#336666
|
||||
#336699
|
||||
#3366cc
|
||||
#3366ff
|
||||
#339900
|
||||
#339933
|
||||
#339966
|
||||
#339999
|
||||
#3399cc
|
||||
#3399ff
|
||||
#33cc00
|
||||
#33cc33
|
||||
#33cc66
|
||||
#33cc99
|
||||
#33cccc
|
||||
#33ccff
|
||||
#33ff00
|
||||
#33ff33
|
||||
#33ff66
|
||||
#33ff99
|
||||
#33ffcc
|
||||
#33ffff
|
||||
#660000
|
||||
#660033
|
||||
#660066
|
||||
#660099
|
||||
#6600cc
|
||||
#6600ff
|
||||
#663300
|
||||
#663333
|
||||
#663366
|
||||
#663399
|
||||
#6633cc
|
||||
#6633ff
|
||||
#666600
|
||||
#666633
|
||||
#666666
|
||||
#666699
|
||||
#6666cc
|
||||
#6666ff
|
||||
#669900
|
||||
#669933
|
||||
#669966
|
||||
#669999
|
||||
#6699cc
|
||||
#6699ff
|
||||
#66cc00
|
||||
#66cc33
|
||||
#66cc66
|
||||
#66cc99
|
||||
#66cccc
|
||||
#66ccff
|
||||
#66ff00
|
||||
#66ff33
|
||||
#66ff66
|
||||
#66ff99
|
||||
#66ffcc
|
||||
#66ffff
|
||||
#990000
|
||||
#990033
|
||||
#990066
|
||||
#990099
|
||||
#9900cc
|
||||
#9900ff
|
||||
#993300
|
||||
#993333
|
||||
#993366
|
||||
#993399
|
||||
#9933cc
|
||||
#9933ff
|
||||
#996600
|
||||
#996633
|
||||
#996666
|
||||
#996699
|
||||
#9966cc
|
||||
#9966ff
|
||||
#999900
|
||||
#999933
|
||||
#999966
|
||||
#999999
|
||||
#9999cc
|
||||
#9999ff
|
||||
#99cc00
|
||||
#99cc33
|
||||
#99cc66
|
||||
#99cc99
|
||||
#99cccc
|
||||
#99ccff
|
||||
#99ff00
|
||||
#99ff33
|
||||
#99ff66
|
||||
#99ff99
|
||||
#99ffcc
|
||||
#99ffff
|
||||
#cc0000
|
||||
#cc0033
|
||||
#cc0066
|
||||
#cc0099
|
||||
#cc00cc
|
||||
#cc00ff
|
||||
#cc3300
|
||||
#cc3333
|
||||
#cc3366
|
||||
#cc3399
|
||||
#cc33cc
|
||||
#cc33ff
|
||||
#cc6600
|
||||
#cc6633
|
||||
#cc6666
|
||||
#cc6699
|
||||
#cc66cc
|
||||
#cc66ff
|
||||
#cc9900
|
||||
#cc9933
|
||||
#cc9966
|
||||
#cc9999
|
||||
#cc99cc
|
||||
#cc99ff
|
||||
#cccc00
|
||||
#cccc33
|
||||
#cccc66
|
||||
#cccc99
|
||||
#cccccc
|
||||
#ccccff
|
||||
#ccff00
|
||||
#ccff33
|
||||
#ccff66
|
||||
#ccff99
|
||||
#ccffcc
|
||||
#ccffff
|
||||
#ff0000
|
||||
#ff0033
|
||||
#ff0066
|
||||
#ff0099
|
||||
#ff00cc
|
||||
#ff00ff
|
||||
#ff3300
|
||||
#ff3333
|
||||
#ff3366
|
||||
#ff3399
|
||||
#ff33cc
|
||||
#ff33ff
|
||||
#ff6600
|
||||
#ff6633
|
||||
#ff6666
|
||||
#ff6699
|
||||
#ff66cc
|
||||
#ff66ff
|
||||
#ff9900
|
||||
#ff9933
|
||||
#ff9966
|
||||
#ff9999
|
||||
#ff99cc
|
||||
#ff99ff
|
||||
#ffcc00
|
||||
#ffcc33
|
||||
#ffcc66
|
||||
#ffcc99
|
||||
#ffcccc
|
||||
#ffccff
|
||||
#ffff00
|
||||
#ffff33
|
||||
#ffff66
|
||||
#ffff99
|
||||
#ffffcc
|
||||
#ffffff
|
||||
Whitespace-only changes.
@@ -0,0 +1 @@
|
||||
1.0.0
|
||||
Reference in new issue
Block a user