Last active
August 11, 2023 21:29
-
-
Save afonya2/8f2fd053e3a23ff72555fe5bf2c113e3 to your computer and use it in GitHub Desktop.
Bmp image render for CC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
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