CPIv1 implementation added

This commit is contained in:
Casey 2024-01-18 16:26:57 +03:00
parent ba07886a4f
commit 5872a931e1
Signed by: hkc
GPG Key ID: F0F6CFE11CDB0960
2 changed files with 90 additions and 23 deletions

View File

@ -78,14 +78,35 @@ class Converter:
dark_i, bri_i = bri_i, dark_i
return out, dark_i, bri_i
@staticmethod
def _write_varint(fp: BinaryIO, value: int):
value &= 0xFFFFFFFF
mask: int = 0xFFFFFF80
while True:
if (value & mask) == 0:
fp.write(bytes([value & 0xFF]))
return
fp.write(bytes([(value & 0x7F) | 0x80]))
value >>= 7
def export_binary(self, io: BinaryIO):
io.write(b"CCPI")
io.write(bytes([self._img.width // 2, self._img.height // 3, 0]))
io.write(bytes(self._palette[: 16 * 3]))
if self._img.width <= 510 and self._img.height <= 765:
io.write(b"CCPI") # old format
io.write(bytes([self._img.width // 2, self._img.height // 3, 0]))
io.write(bytes(self._palette[: 16 * 3]))
else:
io.write(b"CPI\x01") # CPIv1
self._write_varint(io, self._img.width // 2)
self._write_varint(io, self._img.height // 3)
io.write(bytes(self._palette[: 16 * 3]))
written = 0
for y in range(0, self._img.height - 2, 3):
line: bytearray = bytearray()
for x in range(0, self._img.width - 1, 2):
ch, bg, fg = self._get_block(x, y)
io.write(bytes([(ch + 0x80) & 0xFF, fg << 4 | bg]))
line.extend([(ch + 0x80) & 0xFF, fg << 4 | bg])
written += io.write(line)
assert written == (self._img.width // 2) * (self._img.height // 3) * 2
def export(self, io: TextIO):
io.write("local m = peripheral.find('monitor')\n")

View File

@ -1,30 +1,20 @@
local function load(path)
local image = { w = 0, h = 0, scale = 1.0, palette = {}, lines = {} }
local decoders = {}
local fp, err = io.open(path, "rb")
if not fp then return nil, err end
local magic = fp:read(4)
if magic ~= "CCPI" then
return nil, "Invalid header: expected CCPI got " .. magic
end
image.w, image.h = string.byte(fp:read(1)), string.byte(fp:read(1))
image.scale = 0.5 + string.byte(fp:read(1)) * 5 / 255
local function read_palette_full(palette, fp)
for i = 1, 16 do
image.palette[i] = bit.blshift(string.byte(fp:read(1)), 16)
image.palette[i] = bit.bor(image.palette[i], bit.blshift(string.byte(fp:read(1)), 8))
image.palette[i] = bit.bor(image.palette[i], string.byte(fp:read(1)))
palette[i] = bit.blshift(string.byte(fp:read(1)), 16)
palette[i] = bit.bor(palette[i], bit.blshift(string.byte(fp:read(1)), 8))
palette[i] = bit.bor(palette[i], string.byte(fp:read(1)))
end
end
print(image.w, image.h)
local function read_pixeldata_v0(image, fp)
for y = 1, image.h do
local line = { s = "", bg = "", fg = "" }
for x = 1, image.w do
local data = fp:read(2)
if #data == 0 then
if data == nil or #data == 0 then
return nil, string.format("Failed to read character at x=%d y=%d", x, y)
end
@ -39,9 +29,65 @@ local function load(path)
end
table.insert(image.lines, line)
end
return true
end
local function read_varint(fp)
local value = 0
local current = 0
local offset = 0
repeat
if offset >= 5 then return nil, "varint too long" end
current = string.byte(fp:read(1))
value = bit.bor(value, bit.blshift(bit.band(current, 0x7f), offset * 7))
offset = offset + 1
until bit.band(current, 0x80) == 0
return value
end
decoders[0] = function(image, fp)
image.w, image.h = string.byte(fp:read(1)), string.byte(fp:read(1))
image.scale = 0.5 + string.byte(fp:read(1)) * 5 / 255
read_palette_full(image.palette, fp)
local success, err = read_pixeldata_v0(image, fp)
if not success then return false, err end
return true
end
decoders[1] = function(image, fp)
image.w = read_varint(fp)
image.h = read_varint(fp)
image.scale = 0.5 -- CPIv1 doesn't have a scale property
read_palette_full(image.palette, fp)
local success, err = read_pixeldata_v0(image, fp)
if not success then return false, err end
return true
end
local function load(path)
local fp, err = io.open(path, "rb")
if not fp then return nil, err end
local res
local image = { w = 0, h = 0, scale = 1.0, palette = {}, lines = {} }
local magic = fp:read(4)
if magic == "CCPI" then
res, err = decoders[0](image, fp)
elseif magic:sub(1, 3) == "CPI" then
local version = magic:byte(4, 4)
if decoders[version] == nil then
fp:close()
return nil, string.format("Invalid CPI version 0x%02x", version)
end
res, err = decoders[version](image, fp)
else
fp:close()
return nil, "Invalid header: expected CCPI got " .. magic
end
fp:close()
if not res then return false, err end
return image
end