Skip to content

Instantly share code, notes, and snippets.

@afonya2
Last active August 11, 2023 21:29
Show Gist options
  • Save afonya2/8f2fd053e3a23ff72555fe5bf2c113e3 to your computer and use it in GitHub Desktop.
Save afonya2/8f2fd053e3a23ff72555fe5bf2c113e3 to your computer and use it in GitHub Desktop.
Bmp image render for CC
--[[
BMP Api
Made by: afonya2@github
]]
local bmpapi = {}
bmpapi.monitors = {}
local function loadCache(filename)
local fa = fs.open(filename, "r")
local fi = fa.readAll()
fi = fi:gsub("BMPAPI CACHE, DO NOT EDIT!","")
fa.close()
return textutils.unserialise(fi)
end
local function saveCache(filename, data)
local fa = fs.open(filename, "w")
fa.write("BMPAPI CACHE, DO NOT EDIT!"..textutils.serialise(data))
fa.close()
end
function bmpapi.setup(mw, mh)
if fs.exists("bmpapi.cache") then
local ca = loadCache("bmpapi.cache")
local lastW = 0
local lastH = 0
bmpapi.mwidth = 0
bmpapi.mheight = 0
for k,v in ipairs(ca) do
table.insert(bmpapi.monitors, {})
bmpapi.mheight = bmpapi.mheight + 1
for kk,vv in ipairs(v) do
bmpapi.mwidth = bmpapi.mwidth + 1
local monii = peripheral.wrap(vv)
monii.setTextScale(0.5)
local www, hhh = monii.getSize()
monii.setCursorPos(1,1)
monii.setBackgroundColor(colors.black)
monii.setTextColor(colors.white)
monii.clear()
monii.write("Calibrated!")
table.insert(bmpapi.monitors[k], {
id = vv,
wrap = monii,
mx = kk,
my = k,
x = lastW + 1,
y = lastH + 1,
w = lastW + www,
h = lastW + hhh
})
lastW = lastW + www
if mx == bmpapi.mwidth then
lastH = lastH + hhh
lastW = 0
end
end
end
return
end
local peps = peripheral.getNames()
local mons = {}
for k,v in ipairs(peps) do
local t = peripheral.getType(v)
if t == "monitor" then
table.insert(mons, {
id = v,
wrap = peripheral.wrap(v)
})
end
end
if (mw == nil) or (mh == nil) then
print("Width?")
bmpapi.mwidth = tonumber(io.read())
print("Height?")
bmpapi.mheight = tonumber(io.read())
else
bmpapi.mwidth = mw
bmpapi.mheight = mh
end
for k,v in ipairs(mons) do
v.wrap.setTextScale(0.5)
v.wrap.setCursorPos(1,1)
v.wrap.setBackgroundColor(colors.black)
v.wrap.setTextColor(colors.white)
v.wrap.clear()
v.wrap.write("Click to calibrate")
end
local lastW = 0
local lastH = 0
for my=1,bmpapi.mheight do
table.insert(bmpapi.monitors, {})
for mx=1,bmpapi.mwidth do
local event, side, x, y = os.pullEvent("monitor_touch")
local monii = peripheral.wrap(side)
local www, hhh = monii.getSize()
monii.setCursorPos(1,1)
monii.clear()
monii.write("Calibrated!")
table.insert(bmpapi.monitors[my], {
id = side,
wrap = monii,
mx = mx,
my = my,
x = lastW + 1,
y = lastH + 1,
w = lastW + www,
h = lastW + hhh
})
lastW = lastW + www
if mx == bmpapi.mwidth then
lastH = lastH + hhh
lastW = 0
end
end
end
local toCache = {}
for k,v in ipairs(bmpapi.monitors) do
table.insert(toCache, {})
for kk,vv in ipairs(v) do
table.insert(toCache[k], vv.id)
end
end
saveCache("bmpapi.cache", toCache)
end
function bmpapi.getSize()
local w = 0
local h = 0
for k,v in ipairs(bmpapi.monitors) do
for kk,vv in ipairs(v) do
local lw,lh = vv.wrap.getSize()
if kk == 1 then
h = h + lh
end
w = w + lw
end
end
return {
monitorsW = bmpapi.mwidth,
monitorsH = bmpapi.mheight,
imageW = w,
imageH = h
}
end
local function isInMonitor(mon, x, y)
return (x >= mon.x) and (x <= mon.w) and (y >= mon.y) and (y <= mon.h)
end
local function getMonitorPos(x,y)
for k,v in ipairs(bmpapi.monitors) do
for kk,vv in ipairs(v) do
if isInMonitor(vv,x,y) then
return vv
end
end
end
end
local function executeOnAllMonitors(com, ...)
for k,v in ipairs(bmpapi.monitors) do
for kk,vv in ipairs(v) do
vv.wrap[com](...)
end
end
end
function bmpapi.readBMPImage(handle)
local function lnumRead(count)
local out = 0
for i=1,count do
out = out + handle.read(1):byte()
end
return out
end
local header = handle.read(2)
if header ~= "BM" then
error("Invalid bmp file")
end
local size = lnumRead(4)
handle.seek("cur", 4)
local offset = lnumRead(4)
local diblen = lnumRead(4)
local width = lnumRead(4)
local height = lnumRead(4)
handle.seek("cur", 2)
local bpp = lnumRead(2)
local rowSize = math.ceil(bpp * width / 32) * 4
handle.seek("set",offset)
local pixels = handle.read(rowSize * height)
local pixelArray = {}
for y = height-1,0,-1 do
table.insert(pixelArray, {})
for x = 0,width-1 do
local index = y * rowSize + x * bpp / 8
local b = pixels:byte(index+1)
local g = pixels:byte(index+2)
local r = pixels:byte(index+3)
if r == nil then
r = 0
end
if g == nil then
g = 0
end
if b == nil then
b = 0
end
table.insert(pixelArray[#pixelArray], {r,g,b})
end
end
local palette = {}
local ipalette = {}
for k,v in ipairs(pixelArray) do
for kk,vv in ipairs(v) do
if ipalette[vv[1]..","..vv[2]..","..vv[3]] == nil then
table.insert(palette, vv)
ipalette[vv[1]..","..vv[2]..","..vv[3]] = true
end
end
end
return {
width = width,
height = height,
pixels = pixelArray,
palette = palette
}
end
function mediana(cols)
local highest = -1
local function fh()
local rh = 0
local gh = 0
local bh = 0
local rl = 255
local gl = 255
local bl = 255
for k,v in ipairs(cols) do
if v[1] > rh then
rh = v[1]
end
if v[1] < rl then
rl = v[1]
end
if v[2] > gh then
gh = v[2]
end
if v[2] < gl then
gl = v[2]
end
if v[3] > bh then
bh = v[3]
end
if v[3] < bl then
bl = v[3]
end
end
local rd = rh - rl
local gd = gh - gl
local bd = bh - bl
if rd >= gd and rd >= bd then
highest = 1
end
if gd >= rd and gd >= bd then
highest = 2
end
if bd >= rd and bd >= gd then
highest = 3
end
end
fh()
table.sort(cols, function(a,b)
return a[highest] < b[highest]
end)
local function cutter(tab)
local mid = math.floor(#tab / 2)
local a = {}
for i=1,mid do
table.insert(a, tab[i])
end
local b = {}
for i=mid+1,#tab do
table.insert(b, tab[i])
end
return {a,b}
end
local c = cutter(cols)
return c
end
function medianb(cols)
local tabs = {cols}
while #tabs < 16 do
local eat = {}
for k,v in ipairs(tabs) do
local med = mediana(v)
table.insert(eat, med[1])
table.insert(eat, med[2])
end
tabs = eat
end
return tabs
end
function averageColor(cols)
local out = {}
local ra = 0
local ga = 0
local ba = 0
for k,v in ipairs(cols) do
ra = ra + v[1]
ga = ga + v[2]
ba = ba + v[3]
end
out[1] = ra / #cols
out[2] = ga / #cols
out[3] = ba / #cols
return out
end
function bmpapi.renderImage(handle, perMonitorColorizing)
local img = bmpapi.readBMPImage(handle)
local raw_colors = {
colors.white,
colors.orange,
colors.magenta,
colors.lightBlue,
colors.yellow,
colors.lime,
colors.pink,
colors.gray,
colors.lightGray,
colors.cyan,
colors.purple,
colors.blue,
colors.brown,
colors.green,
colors.red,
colors.black
}
if (not perMonitorColorizing) or (#img.palette <= 16) then
local newColors = {}
if #img.palette <= 16 then
for k,v in ipairs(raw_colors) do
if img.palette[k] == nil then
break
end
executeOnAllMonitors("setPaletteColor", v, img.palette[k][1]/255, img.palette[k][2]/255, img.palette[k][3]/255)
newColors[img.palette[k][1]..","..img.palette[k][2]..","..img.palette[k][3]] = v
end
else
local newPalette = medianb(img.palette)
for k,v in ipairs(raw_colors) do
local avg = averageColor(newPalette[k])
executeOnAllMonitors("setPaletteColor", v, avg[1]/255, avg[2]/255, avg[3]/255)
for kk,vv in ipairs(newPalette[k]) do
newColors[vv[1]..","..vv[2]..","..vv[3]] = v
end
end
end
local ms = bmpapi.getSize()
for y,v in ipairs(img.pixels) do
for x,vv in ipairs(v) do
local r = vv[1]
local g = vv[2]
local b = vv[3]
local newColor = newColors[r..","..g..","..b]
if (newColor ~= nil) and (x <= ms.imageW) and (y <= ms.imageH) then
local cmon = getMonitorPos(x,y)
cmon.wrap.setBackgroundColor(newColor)
cmon.wrap.setCursorPos(x-cmon.x+1,y-cmon.y+1)
cmon.wrap.write(" ")
end
end
end
else
local monPalette = {}
local imonPalette = {}
for y,v in ipairs(img.pixels) do
for x,vv in ipairs(v) do
local r = vv[1]
local g = vv[2]
local b = vv[3]
local cmon = getMonitorPos(x,y)
if monPalette[cmon.id] == nil then
monPalette[cmon.id] = {}
imonPalette[cmon.id] = {}
end
if imonPalette[cmon.id][r..","..g..","..b] == nil then
table.insert(monPalette[cmon.id], vv)
imonPalette[cmon.id][r..","..g..","..b] = true
end
end
end
for k,v in ipairs(bmpapi.monitors) do
for kk,vv in ipairs(v) do
if monPalette[vv.id] ~= nil then
local newColors = {}
if #monPalette[vv.id] <= 16 then
for kkk,vvv in ipairs(raw_colors) do
if monPalette[vv.id][kkk] == nil then
break
end
vv.wrap.setPaletteColor(vvv, monPalette[vv.id][kkk][1]/255, monPalette[vv.id][kkk][2]/255, monPalette[vv.id][kkk][3]/255)
newColors[monPalette[vv.id][kkk][1]..","..monPalette[vv.id][kkk][2]..","..monPalette[vv.id][kkk][3]] = vvv
end
else
local newPalette = medianb(monPalette[vv.id])
for kkk,vvv in ipairs(raw_colors) do
local avg = averageColor(newPalette[kkk])
vv.wrap.setPaletteColor(vvv, avg[1]/255, avg[2]/255, avg[3]/255)
for fkk,fvv in ipairs(newPalette[kkk]) do
newColors[fvv[1]..","..fvv[2]..","..fvv[3]] = vvv
end
end
end
local ms = bmpapi.getSize()
for y,mv in ipairs(img.pixels) do
for x,mvv in ipairs(mv) do
local r = mvv[1]
local g = mvv[2]
local b = mvv[3]
local newColor = newColors[r..","..g..","..b]
if (newColor ~= nil) and (x <= ms.imageW) and (y <= ms.imageH) then
local cmon = getMonitorPos(x,y)
if cmon.id == vv.id then
cmon.wrap.setBackgroundColor(newColor)
cmon.wrap.setCursorPos(x-cmon.x+1,y-cmon.y+1)
cmon.wrap.write(" ")
end
end
end
end
end
end
end
end
end
return bmpapi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment