mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-05-07 09:54:13 +00:00
Add functions for parsing and drawing nft (#458)
This commit is contained in:
parent
9499654757
commit
b54519d0e6
@ -0,0 +1,107 @@
|
|||||||
|
--- Provides utilities for working with "nft" images.
|
||||||
|
--
|
||||||
|
-- nft ("Nitrogen Fingers Text") is a file format for drawing basic images.
|
||||||
|
-- Unlike the images that @{paintutils.parseImage} uses, nft supports coloured
|
||||||
|
-- text.
|
||||||
|
--
|
||||||
|
-- @module cc.image.nft
|
||||||
|
-- @usage Load an image from `example.nft` and draw it.
|
||||||
|
--
|
||||||
|
-- local nft = require "cc.image.nft"
|
||||||
|
-- local image = assert(nft.load("example.nft"))
|
||||||
|
-- nft.draw(image)
|
||||||
|
|
||||||
|
local expect = require "cc.expect".expect
|
||||||
|
|
||||||
|
--- Parse an nft image from a string.
|
||||||
|
--
|
||||||
|
-- @tparam string image The image contents.
|
||||||
|
-- @return table The parsed image.
|
||||||
|
local function parse(image)
|
||||||
|
expect(1, image, "string")
|
||||||
|
|
||||||
|
local result = {}
|
||||||
|
local line = 1
|
||||||
|
local foreground = "0"
|
||||||
|
local background = "f"
|
||||||
|
|
||||||
|
local i, len = 1, #image
|
||||||
|
while i <= len do
|
||||||
|
local c = image:sub(i, i)
|
||||||
|
if c == "\31" and i < len then
|
||||||
|
i = i + 1
|
||||||
|
foreground = image:sub(i, i)
|
||||||
|
elseif c == "\30" and i < len then
|
||||||
|
i = i + 1
|
||||||
|
background = image:sub(i, i)
|
||||||
|
elseif c == "\n" then
|
||||||
|
if result[line] == nil then
|
||||||
|
result[line] = { text = "", foreground = "", background = "" }
|
||||||
|
end
|
||||||
|
|
||||||
|
line = line + 1
|
||||||
|
else
|
||||||
|
local next = image:find("[\n\30\31]", i) or #image + 1
|
||||||
|
local seg_len = next - i
|
||||||
|
|
||||||
|
local this_line = result[line]
|
||||||
|
if this_line == nil then
|
||||||
|
this_line = { foreground = "", background = "", text = "" }
|
||||||
|
result[line] = this_line
|
||||||
|
end
|
||||||
|
|
||||||
|
this_line.text = this_line.text .. image:sub(i, next - 1)
|
||||||
|
this_line.foreground = this_line.foreground .. foreground:rep(seg_len)
|
||||||
|
this_line.background = this_line.background .. background:rep(seg_len)
|
||||||
|
|
||||||
|
i = next - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Load an nft image from a file.
|
||||||
|
--
|
||||||
|
-- @tparam string path The file to load.
|
||||||
|
-- @treturn[1] table The parsed image.
|
||||||
|
-- @treturn[2] nil If the file does not exist or could not be loaded.
|
||||||
|
-- @treturn[2] string An error message explaining why the file could not be
|
||||||
|
-- loaded.
|
||||||
|
local function load(path)
|
||||||
|
expect(1, path, "string")
|
||||||
|
local file, err = io.open(path, "r")
|
||||||
|
if not file then return nil, err end
|
||||||
|
|
||||||
|
local result = file:read("*a")
|
||||||
|
file:close()
|
||||||
|
return parse(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Draw an nft image to the screen.
|
||||||
|
--
|
||||||
|
-- @tparam table image An image, as returned from @{load} or @{draw}.
|
||||||
|
-- @tparam number xPos The x position to start drawing at.
|
||||||
|
-- @tparam number xPos The y position to start drawing at.
|
||||||
|
-- @tparam[opt] term.Redirect target The terminal redirect to draw to. Defaults to the
|
||||||
|
-- current terminal.
|
||||||
|
local function draw(image, xPos, yPos, target)
|
||||||
|
expect(1, image, "table")
|
||||||
|
expect(2, xPos, "number")
|
||||||
|
expect(3, yPos, "number")
|
||||||
|
expect(4, target, "table", "nil")
|
||||||
|
|
||||||
|
if not target then target = term end
|
||||||
|
|
||||||
|
for y, line in ipairs(image) do
|
||||||
|
target.setCursorPos(xPos, yPos + y - 1)
|
||||||
|
target.blit(line.text, line.foreground, line.background)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
parse = parse,
|
||||||
|
load = load,
|
||||||
|
draw = draw,
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
local helpers = require "test_helpers"
|
||||||
|
|
||||||
|
describe("cc.image.nft", function()
|
||||||
|
local nft = require("cc.image.nft")
|
||||||
|
|
||||||
|
describe("parse", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
nft.parse("")
|
||||||
|
expect.error(nft.parse, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("parses an empty string", function()
|
||||||
|
expect(nft.parse("")):same {}
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("parses a string with no colours", function()
|
||||||
|
expect(nft.parse("Hello")):same { { text = "Hello", foreground = "00000", background = "fffff" } }
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("handles background and foreground colours", function()
|
||||||
|
expect(nft.parse("\30a\31bHello"))
|
||||||
|
:same { { text = "Hello", foreground = "bbbbb", background = "aaaaa" } }
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("parses multi-line files", function()
|
||||||
|
expect(nft.parse("Hello\nWorld")):same {
|
||||||
|
{ text = "Hello", foreground = "00000", background = "fffff" },
|
||||||
|
{ text = "World", foreground = "00000", background = "fffff" },
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("handles empty lines", function()
|
||||||
|
expect(nft.parse("\n\n")):same {
|
||||||
|
{ text = "", foreground = "", background = "" },
|
||||||
|
{ text = "", foreground = "", background = "" },
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("load", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
nft.load("")
|
||||||
|
expect.error(nft.load, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("loads from a file", function()
|
||||||
|
local image = fs.open("/test-files/example.nft", "w")
|
||||||
|
image.write("\30aHello, world!")
|
||||||
|
image.close()
|
||||||
|
|
||||||
|
expect(nft.load("/test-files/example.nft")):same {
|
||||||
|
{ background = "aaaaaaaaaaaaa", foreground = "0000000000000", text = "Hello, world!" },
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("fails on missing files", function()
|
||||||
|
expect({ nft.load("/test-files/not_a_file.nft") })
|
||||||
|
:same { nil, "/test-files/not_a_file.nft: No such file" }
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("draw", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(nft.draw, nil):eq("bad argument #1 (expected table, got nil)")
|
||||||
|
expect.error(nft.draw, {}, nil):eq("bad argument #2 (expected number, got nil)")
|
||||||
|
expect.error(nft.draw, {}, 1, nil):eq("bad argument #3 (expected number, got nil)")
|
||||||
|
expect.error(nft.draw, {}, 1, 1, false):eq("bad argument #4 (expected table, got boolean)")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("draws an image", function()
|
||||||
|
local win = helpers.with_window(7, 3, function()
|
||||||
|
nft.draw({
|
||||||
|
{ background = "aaaaa", foreground = "f000f", text = "Hello" },
|
||||||
|
}, 2, 2)
|
||||||
|
end)
|
||||||
|
|
||||||
|
expect(win.getLine(1)):eq(" ")
|
||||||
|
expect({ win.getLine(2) }):same { " Hello ", "0f000f0", "faaaaaf" }
|
||||||
|
expect(win.getLine(3)):eq(" ")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("draws an image to a custom redirect", function()
|
||||||
|
local win = window.create(term.current(), 1, 1, 5, 1, false)
|
||||||
|
nft.draw({
|
||||||
|
{ background = "aaaaa", foreground = "f000f", text = "Hello" },
|
||||||
|
}, 1, 1, win)
|
||||||
|
|
||||||
|
expect({ win.getLine(1) }):same { "Hello", "f000f", "aaaaa" }
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
Loading…
x
Reference in New Issue
Block a user