743 lines
21 KiB
Plaintext
743 lines
21 KiB
Plaintext
-- +--------------------------------------------------------+
|
|
-- | |
|
|
-- | BLittle |
|
|
-- | |
|
|
-- +--------------------------------------------------------+
|
|
|
|
local version = "Version 1.1.6beta"
|
|
|
|
-- By Jeffrey Alexander, aka Bomb Bloke.
|
|
-- Convenience functions to make use of ComputerCraft 1.76's new "drawing" characters.
|
|
-- http://www.computercraft.info/forums2/index.php?/topic/25354-cc-176-blittle-api/
|
|
|
|
-------------------------------------------------------------
|
|
|
|
if shell then
|
|
local arg = {...}
|
|
|
|
if #arg == 0 then
|
|
print("Usage:")
|
|
print("blittle <scriptName> [args]")
|
|
return
|
|
end
|
|
|
|
if not blittle then os.loadAPI(shell.getRunningProgram()) end
|
|
local oldTerm = term.redirect(blittle.createWindow())
|
|
shell.run(unpack(arg))
|
|
term.redirect(oldTerm)
|
|
|
|
return
|
|
end
|
|
|
|
local relations = {[0] = {8, 4, 3, 6, 5}, {4, 14, 8, 7}, {6, 10, 8, 7}, {9, 11, 8, 0}, {1, 14, 8, 0}, {13, 12, 8, 0}, {2, 10, 8, 0}, {15, 8, 10, 11, 12, 14},
|
|
{0, 7, 1, 9, 2, 13}, {3, 11, 8, 7}, {2, 6, 7, 15}, {9, 3, 7, 15}, {13, 5, 7, 15}, {5, 12, 8, 7}, {1, 4, 7, 15}, {7, 10, 11, 12, 14}}
|
|
|
|
local colourNum, exponents, colourChar = {}, {}, {}
|
|
for i = 0, 15 do exponents[2^i] = i end
|
|
do
|
|
local hex = "0123456789abcdef"
|
|
for i = 1, 16 do
|
|
colourNum[hex:sub(i, i)] = i - 1
|
|
colourNum[i - 1] = hex:sub(i, i)
|
|
colourChar[hex:sub(i, i)] = 2 ^ (i - 1)
|
|
colourChar[2 ^ (i - 1)] = hex:sub(i, i)
|
|
|
|
local thisRel = relations[i - 1]
|
|
for i = 1, #thisRel do thisRel[i] = 2 ^ thisRel[i] end
|
|
end
|
|
end
|
|
|
|
local function getBestColourMatch(usage)
|
|
local lastCol = relations[exponents[usage[#usage][1]]]
|
|
|
|
for j = 1, #lastCol do
|
|
local thisRelation = lastCol[j]
|
|
for i = 1, #usage - 1 do if usage[i][1] == thisRelation then return i end end
|
|
end
|
|
|
|
return 1
|
|
end
|
|
|
|
local function colsToChar(pattern, totals)
|
|
if not totals then
|
|
local newPattern = {}
|
|
totals = {}
|
|
for i = 1, 6 do
|
|
local thisVal = pattern[i]
|
|
local thisTot = totals[thisVal]
|
|
totals[thisVal], newPattern[i] = thisTot and (thisTot + 1) or 1, thisVal
|
|
end
|
|
pattern = newPattern
|
|
end
|
|
|
|
local usage = {}
|
|
for key, value in pairs(totals) do usage[#usage + 1] = {key, value} end
|
|
|
|
if #usage > 1 then
|
|
-- Reduce the chunk to two colours:
|
|
while #usage > 2 do
|
|
table.sort(usage, function (a, b) return a[2] > b[2] end)
|
|
local matchToInd, usageLen = getBestColourMatch(usage), #usage
|
|
local matchFrom, matchTo = usage[usageLen][1], usage[matchToInd][1]
|
|
for i = 1, 6 do if pattern[i] == matchFrom then
|
|
pattern[i] = matchTo
|
|
usage[matchToInd][2] = usage[matchToInd][2] + 1
|
|
end end
|
|
usage[usageLen] = nil
|
|
end
|
|
|
|
-- Convert to character. Adapted from oli414's function:
|
|
-- http://www.computercraft.info/forums2/index.php?/topic/25340-cc-176-easy-drawing-characters/
|
|
local data = 128
|
|
for i = 1, #pattern - 1 do if pattern[i] ~= pattern[6] then data = data + 2^(i-1) end end
|
|
return string.char(data), colourChar[usage[1][1] == pattern[6] and usage[2][1] or usage[1][1]], colourChar[pattern[6]]
|
|
else
|
|
-- Solid colour character:
|
|
return "\128", colourChar[pattern[1]], colourChar[pattern[1]]
|
|
end
|
|
end
|
|
|
|
local function snooze()
|
|
local myEvent = tostring({})
|
|
os.queueEvent(myEvent)
|
|
os.pullEvent(myEvent)
|
|
end
|
|
|
|
function shrink(image, bgCol)
|
|
local results, width, height, bgCol = {{}, {}, {}}, 0, #image + #image % 3, bgCol or colours.black
|
|
for i = 1, #image do if #image[i] > width then width = #image[i] end end
|
|
|
|
for y = 0, height - 1, 3 do
|
|
local cRow, tRow, bRow, counter = {}, {}, {}, 1
|
|
|
|
for x = 0, width - 1, 2 do
|
|
-- Grab a 2x3 chunk:
|
|
local pattern, totals = {}, {}
|
|
|
|
for yy = 1, 3 do for xx = 1, 2 do
|
|
pattern[#pattern + 1] = (image[y + yy] and image[y + yy][x + xx]) and (image[y + yy][x + xx] == 0 and bgCol or image[y + yy][x + xx]) or bgCol
|
|
totals[pattern[#pattern]] = totals[pattern[#pattern]] and (totals[pattern[#pattern]] + 1) or 1
|
|
end end
|
|
|
|
cRow[counter], tRow[counter], bRow[counter] = colsToChar(pattern, totals)
|
|
counter = counter + 1
|
|
end
|
|
|
|
results[1][#results[1] + 1], results[2][#results[2] + 1], results[3][#results[3] + 1] = table.concat(cRow), table.concat(tRow), table.concat(bRow)
|
|
end
|
|
|
|
results.width, results.height = #results[1][1], #results[1]
|
|
|
|
return results
|
|
end
|
|
|
|
function shrinkGIF(image, bgCol)
|
|
if not GIF and not os.loadAPI("GIF") then error("blittle.shrinkGIF: Load GIF API first.", 2) end
|
|
|
|
image = GIF.flattenGIF(image)
|
|
snooze()
|
|
|
|
local prev = GIF.toPaintutils(image[1])
|
|
snooze()
|
|
|
|
prev = blittle.shrink(prev, bgCol)
|
|
prev.delay = image[1].delay
|
|
image[1] = prev
|
|
snooze()
|
|
|
|
image.width, image.height = prev.width, prev.height
|
|
|
|
for i = 2, #image do
|
|
local temp = GIF.toPaintutils(image[i])
|
|
snooze()
|
|
|
|
temp = blittle.shrink(temp, bgCol)
|
|
snooze()
|
|
|
|
local newImage = {{}, {}, {}, ["delay"] = image[i].delay, ["width"] = temp.width, ["height"] = 0}
|
|
|
|
local a, b, c, pa, pb, pc = temp[1], temp[2], temp[3], prev[1], prev[2], prev[3]
|
|
for i = 1, temp.height do
|
|
local a1, b1, c1, pa1, pb1, pc1 = a[i], b[i], c[i], pa[i], pb[i], pc[i]
|
|
|
|
if a1 ~= pa1 or b1 ~= pb1 or c1 ~= pc1 then
|
|
local min, max = 1, #a1
|
|
local a2, b2, c2, pa2, pb2, pc2 = {a1:byte(1, max)}, {b1:byte(1, max)}, {c1:byte(1, max)}, {pa1:byte(1, max)}, {pb1:byte(1, max)}, {pc1:byte(1, max)}
|
|
|
|
for j = 1, max do if a2[j] ~= pa2[j] or b2[j] ~= pb2[j] or c2[j] ~= pc2[j] then
|
|
min = j
|
|
break
|
|
end end
|
|
|
|
for j = max, min, -1 do if a2[j] ~= pa2[j] or b2[j] ~= pb2[j] or c2[j] ~= pc2[j] then
|
|
max = j
|
|
break
|
|
end end
|
|
|
|
newImage[1][i], newImage[2][i], newImage[3][i], newImage.height = min > 1 and {min - 1, a1:sub(min, max)} or a1:sub(min, max), b1:sub(min, max), c1:sub(min, max), i
|
|
end
|
|
|
|
snooze()
|
|
end
|
|
|
|
image[i], prev = newImage, temp
|
|
|
|
for j = 1, i - 1 do
|
|
local oldImage = image[j]
|
|
|
|
if type(oldImage[1]) == "table" and oldImage.height == newImage.height then
|
|
local same = true
|
|
|
|
for k = 1, oldImage.height do
|
|
local comp1, comp2 = oldImage[1][k], newImage[1][k]
|
|
|
|
if type(comp1) ~= type(comp2) or
|
|
(type(comp1) == "string" and comp1 ~= comp2) or
|
|
(type(comp1) == "table" and (comp1[1] ~= comp2[1] or comp1[2] ~= comp2[2])) or
|
|
oldImage[2][k] ~= newImage[2][k] or
|
|
oldImage[3][k] ~= newImage[3][k] then
|
|
same = false
|
|
break
|
|
end
|
|
end
|
|
|
|
if same then
|
|
newImage[1], newImage[2], newImage[3] = j
|
|
break
|
|
end
|
|
end
|
|
|
|
snooze()
|
|
end
|
|
end
|
|
|
|
return image
|
|
end
|
|
|
|
local function newLine(width, bCol)
|
|
local line = {}
|
|
for i = 1, width do line[i] = {bCol, bCol, bCol, bCol, bCol, bCol} end
|
|
return line
|
|
end
|
|
|
|
function createWindow(parent, x, y, width, height, visible)
|
|
if parent == term or not parent then
|
|
parent = term.current()
|
|
elseif type(parent) ~= "table" or not parent.write then
|
|
error("blittle.newWindow: \"parent\" does not appear to be a terminal object.", 2)
|
|
end
|
|
|
|
local workBuffer, backBuffer, frontBuffer, window, tCol, bCol, curX, curY, blink, cWidth, cHeight, pal = {}, {}, {}, {}, colours.white, colours.black, 1, 1, false
|
|
if type(visible) ~= "boolean" then visible = true end
|
|
x, y = x and math.floor(x) or 1, y and math.floor(y) or 1
|
|
|
|
do
|
|
local xSize, ySize = parent.getSize()
|
|
cWidth, cHeight = (width or xSize), (height or ySize)
|
|
width, height = cWidth * 2, cHeight * 3
|
|
end
|
|
|
|
if parent.setPaletteColour then
|
|
pal = {}
|
|
|
|
local counter = 1
|
|
for i = 1, 16 do
|
|
pal[counter] = {parent.getPaletteColour(counter)}
|
|
counter = counter * 2
|
|
end
|
|
|
|
window.getPaletteColour = function(colour)
|
|
return unpack(pal[colour])
|
|
end
|
|
|
|
window.setPaletteColour = function(colour, r, g, b)
|
|
pal[colour] = {r, g, b}
|
|
if visible then return parent.setPaletteColour(colour, r, g, b) end
|
|
end
|
|
|
|
window.getPaletteColor, window.setPaletteColor = window.getPaletteColour, window.setPaletteColour
|
|
end
|
|
|
|
window.blit = function(_, _, bC)
|
|
local bClen = #bC
|
|
if curX > width or curX + bClen < 2 or curY < 1 or curY > height then
|
|
curX = curX + bClen
|
|
return
|
|
end
|
|
|
|
if curX < 1 then
|
|
bC = bC:sub(2 - curX)
|
|
curX, bClen = 1, #bC
|
|
end
|
|
|
|
if curX + bClen - 1 > width then bC, bClen = bC:sub(1, width - curX + 1), width - curX + 1 end
|
|
|
|
local colNum, rowNum, thisX, yBump = math.floor((curX - 1) / 2) + 1, math.floor((curY - 1) / 3) + 1, (curX - 1) % 2, ((curY - 1) % 3) * 2
|
|
local firstColNum, lastColNum, thisRow = colNum, math.floor((curX + bClen) / 2), backBuffer[rowNum]
|
|
local thisChar = thisRow[colNum]
|
|
|
|
for i = 1, bClen do
|
|
thisChar[thisX + yBump + 1] = colourChar[bC:sub(i, i)]
|
|
|
|
if thisX == 1 then
|
|
thisX, colNum = 0, colNum + 1
|
|
thisChar = thisRow[colNum]
|
|
if not thisChar then break end
|
|
else thisX = 1 end
|
|
end
|
|
|
|
if visible then
|
|
local chars1, chars2, chars3, count = {}, {}, {}, 1
|
|
|
|
for i = firstColNum, lastColNum do
|
|
chars1[count], chars2[count], chars3[count] = colsToChar(thisRow[i])
|
|
count = count + 1
|
|
end
|
|
|
|
chars1, chars2, chars3 = table.concat(chars1), table.concat(chars2), table.concat(chars3)
|
|
parent.setCursorPos(x + math.floor((curX - 1) / 2), y + math.floor((curY - 1) / 3))
|
|
parent.blit(chars1, chars2, chars3)
|
|
local thisRow = frontBuffer[rowNum]
|
|
frontBuffer[rowNum] = {thisRow[1]:sub(1, firstColNum - 1) .. chars1 .. thisRow[1]:sub(lastColNum + 1), thisRow[2]:sub(1, firstColNum - 1) .. chars2 .. thisRow[2]:sub(lastColNum + 1), thisRow[3]:sub(1, firstColNum - 1) .. chars3 .. thisRow[3]:sub(lastColNum + 1)}
|
|
else
|
|
local thisRow = workBuffer[rowNum]
|
|
|
|
if (not thisRow[firstColNum]) or thisRow[firstColNum] < lastColNum then
|
|
local x, newLastColNum = 1, lastColNum
|
|
|
|
while x <= lastColNum + 1 do
|
|
local thisSpot = thisRow[x]
|
|
|
|
if thisSpot then
|
|
if thisSpot >= firstColNum - 1 then
|
|
if x < firstColNum then firstColNum = x else thisRow[x] = nil end
|
|
if thisSpot > newLastColNum then newLastColNum = thisSpot end
|
|
end
|
|
x = thisSpot + 1
|
|
else x = x + 1 end
|
|
end
|
|
|
|
thisRow[firstColNum] = newLastColNum
|
|
if thisRow.max <= newLastColNum then thisRow.max = firstColNum end
|
|
end
|
|
end
|
|
|
|
curX = curX + bClen
|
|
end
|
|
|
|
window.write = function(text)
|
|
window.blit(nil, nil, string.rep(colourChar[bCol], #tostring(text)))
|
|
end
|
|
|
|
window.clearLine = function()
|
|
local oldX = curX
|
|
curX = 1
|
|
window.blit(nil, nil, string.rep(colourChar[bCol], width))
|
|
curX = oldX
|
|
end
|
|
|
|
window.clear = function()
|
|
local t, fC, bC = string.rep("\128", cWidth), string.rep(colourChar[tCol], cWidth), string.rep(colourChar[bCol], cWidth)
|
|
for y = 1, cHeight do workBuffer[y], backBuffer[y], frontBuffer[y] = {["max"] = 0}, newLine(cWidth, bCol), {t, fC, bC} end
|
|
window.redraw()
|
|
end
|
|
|
|
window.getCursorPos = function()
|
|
return curX, curY
|
|
end
|
|
|
|
window.setCursorPos = function(newX, newY)
|
|
curX, curY = math.floor(newX), math.floor(newY)
|
|
if visible and blink then window.restoreCursor() end
|
|
end
|
|
|
|
window.restoreCursor = function() end
|
|
window.setCursorBlink = window.restoreCursor
|
|
|
|
window.isColour = function()
|
|
return parent.isColour()
|
|
end
|
|
window.isColor = window.isColour
|
|
|
|
window.getSize = function()
|
|
return width, height
|
|
end
|
|
|
|
window.scroll = function(lines)
|
|
lines = math.floor(lines)
|
|
|
|
if lines ~= 0 then
|
|
if lines % 3 == 0 then
|
|
local newWB, newBB, newFB, line1, line2, line3 = {}, {}, {}, string.rep("\128", cWidth), string.rep(colourChar[tCol], cWidth), string.rep(colourChar[bCol], cWidth)
|
|
for y = 1, cHeight do newWB[y], newBB[y], newFB[y] = workBuffer[y + lines] or {["max"] = 0}, backBuffer[y + lines] or newLine(cWidth, bCol), frontBuffer[y + lines] or {line1, line2, line3} end
|
|
workBuffer, backBuffer, frontBuffer = newWB, newBB, newFB
|
|
else
|
|
local newBB, tRowNum, tBump, sRowNum, sBump = {}, 1, 0, math.floor(lines / 3) + 1, (lines % 3) * 2
|
|
local sRow, tRow = backBuffer[sRowNum], {}
|
|
for x = 1, cWidth do tRow[x] = {} end
|
|
|
|
for y = 1, height do
|
|
if sRow then
|
|
for x = 1, cWidth do
|
|
local tChar, sChar = tRow[x], sRow[x]
|
|
tChar[tBump + 1], tChar[tBump + 2] = sChar[sBump + 1], sChar[sBump + 2]
|
|
end
|
|
else
|
|
for x = 1, cWidth do
|
|
local tChar = tRow[x]
|
|
tChar[tBump + 1], tChar[tBump + 2] = bCol, bCol
|
|
end
|
|
end
|
|
|
|
tBump, sBump = tBump + 2, sBump + 2
|
|
|
|
if tBump > 4 then
|
|
tBump, newBB[tRowNum] = 0, tRow
|
|
tRowNum, tRow = tRowNum + 1, {}
|
|
for x = 1, cWidth do tRow[x] = {} end
|
|
end
|
|
|
|
if sBump > 4 then
|
|
sRowNum, sBump = sRowNum + 1, 0
|
|
sRow = backBuffer[sRowNum]
|
|
end
|
|
end
|
|
|
|
for y = 1, cHeight do workBuffer[y] = {["max"] = 1, cWidth} end
|
|
|
|
backBuffer = newBB
|
|
end
|
|
|
|
window.redraw()
|
|
end
|
|
end
|
|
|
|
window.setTextColour = function(newCol)
|
|
tCol = newCol
|
|
end
|
|
window.setTextColor = window.setTextColour
|
|
|
|
window.setBackgroundColour = function(newCol)
|
|
bCol = newCol
|
|
end
|
|
window.setBackgroundColor = window.setBackgroundColour
|
|
|
|
window.getTextColour = function()
|
|
return tCol
|
|
end
|
|
window.getTextColor = window.getTextColour
|
|
|
|
window.getBackgroundColour = function()
|
|
return bCol
|
|
end
|
|
window.getBackgroundColor = window.getBackgroundColour
|
|
|
|
window.redraw = function()
|
|
if visible then
|
|
for i = 1, cHeight do
|
|
local work, front = workBuffer[i], frontBuffer[i]
|
|
local front1, front2, front3 = front[1], front[2], front[3]
|
|
|
|
if work.max > 0 then
|
|
local line1, line2, line3, lineLen, skip, back, count = {}, {}, {}, 1, 0, backBuffer[i], 1
|
|
|
|
while count <= work.max do if work[count] then
|
|
if skip > 0 then
|
|
line1[lineLen], line2[lineLen], line3[lineLen] = front1:sub(count - skip, count - 1), front2:sub(count - skip, count - 1), front3:sub(count - skip, count - 1)
|
|
skip, lineLen = 0, lineLen + 1
|
|
end
|
|
|
|
for i = count, work[count] do
|
|
line1[lineLen], line2[lineLen], line3[lineLen] = colsToChar(back[i])
|
|
lineLen = lineLen + 1
|
|
end
|
|
|
|
count = work[count] + 1
|
|
else skip, count = skip + 1, count + 1 end end
|
|
|
|
if count < cWidth + 1 then line1[lineLen], line2[lineLen], line3[lineLen] = front1:sub(count), front2:sub(count), front3:sub(count) end
|
|
|
|
front1, front2, front3 = table.concat(line1), table.concat(line2), table.concat(line3)
|
|
frontBuffer[i], workBuffer[i] = {front1, front2, front3}, {["max"] = 0}
|
|
end
|
|
|
|
parent.setCursorPos(x, y + i - 1)
|
|
parent.blit(front1, front2, front3)
|
|
end
|
|
|
|
if pal then
|
|
local counter = 1
|
|
for i = 1, 16 do
|
|
parent.setPaletteColour(counter, unpack(pal[counter]))
|
|
counter = counter * 2
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
window.setVisible = function(newVis)
|
|
newVis = newVis and true or false
|
|
|
|
if newVis and not visible then
|
|
visible = true
|
|
window.redraw()
|
|
else visible = newVis end
|
|
end
|
|
|
|
window.getPosition = function()
|
|
return x, y
|
|
end
|
|
|
|
window.reposition = function(newX, newY, newWidth, newHeight)
|
|
x, y = type(newX) == "number" and math.floor(newX) or x, type(newY) == "number" and math.floor(newY) or y
|
|
|
|
if type(newWidth) == "number" then
|
|
newWidth = math.floor(newWidth)
|
|
if newWidth > cWidth then
|
|
local line1, line2, line3 = string.rep("\128", newWidth - cWidth), string.rep(colourChar[tCol], newWidth - cWidth), string.rep(colourChar[bCol], newWidth - cWidth)
|
|
for y = 1, cHeight do
|
|
local bRow, fRow = backBuffer[y], frontBuffer[y]
|
|
for x = cWidth + 1, newWidth do bRow[x] = {bCol, bCol, bCol, bCol, bCol, bCol} end
|
|
frontBuffer[y] = {fRow[1] .. line3, fRow[2] .. line2, fRow[3] .. line3}
|
|
end
|
|
elseif newWidth < cWidth then
|
|
for y = 1, cHeight do
|
|
local wRow, bRow, fRow = workBuffer[y], backBuffer[y], frontBuffer[y]
|
|
for x = newWidth + 1, cWidth do bRow[x] = nil end
|
|
frontBuffer[y] = {fRow[1]:sub(1, newWidth), fRow[2]:sub(1, newWidth), fRow[3]:sub(1, newWidth)}
|
|
|
|
while wRow[wRow.max] and wRow[wRow.max] > newWidth do
|
|
wRow[wRow.max] = nil
|
|
wRow.max = table.maxn(wRow)
|
|
end
|
|
end
|
|
end
|
|
width, cWidth = newWidth * 2, newWidth
|
|
end
|
|
|
|
if type(newHeight) == "number" then
|
|
newHeight = math.floor(newHeight)
|
|
if newHeight > cHeight then
|
|
local line1, line2, line3 = string.rep("\128", cWidth), string.rep(colourChar[tCol], cWidth), string.rep(colourChar[bCol], cWidth)
|
|
for y = cHeight + 1, newHeight do workBuffer[y], backBuffer[y], frontBuffer[y] = {["max"] = 0}, newLine(cWidth, bCol), {line1, line2, line3} end
|
|
elseif newHeight < cHeight then
|
|
for y = newHeight + 1, cHeight do workBuffer[y], backBuffer[y], frontBuffer[y] = nil, nil, nil end
|
|
end
|
|
height, cHeight = newHeight * 3, newHeight
|
|
end
|
|
|
|
window.redraw()
|
|
end
|
|
|
|
window.clear()
|
|
return window
|
|
end
|
|
|
|
function draw(image, x, y, terminal)
|
|
local t, tC, bC = image[1], image[2], image[3]
|
|
x, y, terminal = x or 1, y or 1, terminal or term.current()
|
|
|
|
for i = 1, image.height do
|
|
local tI = t[i]
|
|
if type(tI) == "string" then
|
|
terminal.setCursorPos(x, y + i - 1)
|
|
terminal.blit(tI, tC[i], bC[i])
|
|
elseif type(tI) == "table" then
|
|
terminal.setCursorPos(x + tI[1], y + i - 1)
|
|
terminal.blit(tI[2], tC[i], bC[i])
|
|
end
|
|
end
|
|
end
|
|
|
|
function save(image, filename)
|
|
local output = fs.open(filename, "wb")
|
|
if not output then error("Can't open "..filename.." for output.") end
|
|
|
|
local writeByte = output.write
|
|
|
|
local function writeInt(num)
|
|
writeByte(bit.band(num, 255))
|
|
writeByte(bit.brshift(num, 8))
|
|
end
|
|
|
|
writeByte(66) -- B
|
|
writeByte(76) -- L
|
|
writeByte(84) -- T
|
|
|
|
local animated = image[1].delay ~= nil
|
|
writeByte(animated and 1 or 0)
|
|
|
|
if animated then
|
|
writeInt(#image)
|
|
else
|
|
local tempImage = {image[1], image[2], image[3]}
|
|
image[1], image[2], image[3] = tempImage, nil, nil
|
|
end
|
|
|
|
local width, height = image.width, image.height
|
|
|
|
writeInt(width)
|
|
writeInt(height)
|
|
|
|
for k = 1, #image do
|
|
local thisImage = image[k]
|
|
|
|
if type(thisImage[1]) == "number" then
|
|
writeByte(3)
|
|
writeInt(thisImage[1])
|
|
else
|
|
for i = 1, height do
|
|
if thisImage[1][i] then
|
|
local rowType, len, thisRow = type(thisImage[1][i])
|
|
|
|
if rowType == "string" then
|
|
writeByte(1)
|
|
len = #thisImage[1][i]
|
|
writeInt(len)
|
|
thisRow = {thisImage[1][i]:byte(1, len)}
|
|
elseif rowType == "table" then
|
|
writeByte(2)
|
|
len = #thisImage[1][i][2]
|
|
writeInt(len)
|
|
writeInt(thisImage[1][i][1])
|
|
thisRow = {thisImage[1][i][2]:byte(1, len)}
|
|
else
|
|
error("Malformed row record #"..i.." in frame #"..k.." when attempting to save \""..filename.."\", type is "..rowType..".")
|
|
end
|
|
|
|
for x = 1, len do writeByte(thisRow[x]) end
|
|
|
|
local txt, bg = thisImage[2][i], thisImage[3][i]
|
|
for x = 1, len do writeByte(colourNum[txt:sub(x, x)] + colourNum[bg:sub(x, x)] * 16) end
|
|
else writeByte(0) end
|
|
end
|
|
end
|
|
|
|
if animated then writeInt(thisImage.delay * 20) end
|
|
|
|
snooze()
|
|
end
|
|
|
|
if image.pal then
|
|
writeByte(#image.pal)
|
|
for i = 0, #image.pal do for j = 1, 3 do writeByte(image.pal[i][j]) end end
|
|
end
|
|
|
|
if not animated then
|
|
image[2], image[3] = image[1][2], image[1][3]
|
|
image[1] = image[1][1]
|
|
end
|
|
|
|
output.close()
|
|
end
|
|
|
|
function load(filename)
|
|
local input = fs.open(filename, "rb")
|
|
if not input then error("Can't open "..filename.." for input.") end
|
|
|
|
local read = input.read
|
|
|
|
local function readInt()
|
|
local result = read()
|
|
return result + bit.blshift(read(), 8)
|
|
end
|
|
|
|
if string.char(read(), read(), read()) ~= "BLT" then
|
|
-- Assume legacy format.
|
|
input.close()
|
|
input = fs.open(filename, "rb")
|
|
|
|
read = input.read
|
|
|
|
function readInt()
|
|
local result = input.read()
|
|
return result + bit.blshift(input.read(), 8)
|
|
end
|
|
|
|
local image = {}
|
|
image.width, image.height = readInt(), readInt()
|
|
|
|
for i = 1, 3 do
|
|
local thisSet = {}
|
|
for y = 1, image.height do
|
|
local thisRow = {}
|
|
for x = 1, image.width do thisRow[x] = string.char(input.read()) end
|
|
thisSet[y] = table.concat(thisRow)
|
|
end
|
|
image[i] = thisSet
|
|
end
|
|
|
|
input.close()
|
|
|
|
return image
|
|
end
|
|
|
|
local image, animated, frames = {}, read() == 1
|
|
if animated then frames = readInt() else frames = 1 end
|
|
|
|
local width, height = readInt(), readInt()
|
|
image.width, image.height = width, height
|
|
|
|
for k = 1, frames do
|
|
local thisImage = {["width"] = width, ["height"] = 0}
|
|
local chr, txt, bg = {}, {}, {}
|
|
|
|
for i = 1, height do
|
|
local lineType = read()
|
|
|
|
if lineType == 3 then
|
|
chr, txt, bg = readInt()
|
|
break
|
|
elseif lineType > 0 then
|
|
local l1, l2, len, bump = {}, {}, readInt()
|
|
if lineType == 2 then bump = readInt() end
|
|
|
|
for x = 1, len do l1[x] = read() end
|
|
chr[i] = string.char(unpack(l1))
|
|
if lineType == 2 then chr[i] = {bump, chr[i]} end
|
|
|
|
for x = 1, len do
|
|
local thisVal = read()
|
|
l1[x], l2[x] = colourNum[bit.band(thisVal, 15)], colourNum[bit.brshift(thisVal, 4)]
|
|
end
|
|
|
|
txt[i], bg[i], thisImage.height = table.concat(l1), table.concat(l2), i
|
|
end
|
|
end
|
|
|
|
if animated then thisImage["delay"] = readInt() / 20 end
|
|
thisImage[1], thisImage[2], thisImage[3] = chr, txt, bg
|
|
image[k] = thisImage
|
|
|
|
snooze()
|
|
end
|
|
|
|
local palLength = read()
|
|
if palLength and palLength > 0 then
|
|
image.pal = {}
|
|
for i = 0, palLength do image.pal[i] = {read(), read(), read()} end
|
|
end
|
|
|
|
if not animated then
|
|
image[2], image[3] = image[1][2], image[1][3]
|
|
image[1] = image[1][1]
|
|
end
|
|
|
|
input.close()
|
|
|
|
return image
|
|
end
|
|
|
|
if term.setPaletteColour then
|
|
function applyPalette(image, terminal)
|
|
terminal = terminal or term
|
|
|
|
local col, pal = 1, image.pal
|
|
|
|
for i = 0, #pal do
|
|
local thisCol = pal[i]
|
|
terminal.setPaletteColour(col, thisCol[1] / 255, thisCol[2] / 255, thisCol[3] / 255)
|
|
col = col * 2
|
|
end
|
|
end
|
|
end |