CPIv1 implementation added
This commit is contained in:
parent
ba07886a4f
commit
5872a931e1
29
cc-pic.py
29
cc-pic.py
|
@ -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")
|
||||
|
|
84
ccpi.lua
84
ccpi.lua
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue