local tchar, bchar = string.char(31), string.char(30) local deepCopy = function(tbl) local output = {} for k,v in pairs(tbl) do output[k] = v end return output end local function stringWrite(str,pos,ins,exc) str, ins = tostring(str), tostring(ins) local output, fn1, fn2 = str:sub(1,pos-1)..ins..str:sub(pos+#ins) if exc then repeat fn1, fn2 = str:find(exc,fn2 and fn2+1 or 1) if fn1 then output = stringWrite(output,fn1,str:sub(fn1,fn2)) end until not fn1 end return output end local checkValid = function(image) if type(image) == "table" then if #image == 3 then if #image[1] + #image[2] + #image[3] >= 3 then return (#image[1] == #image[2] and #image[2] == #image[3]) end end end return false end local bl = { [' '] = 0, ['0'] = 1, ['1'] = 2, ['2'] = 4, ['3'] = 8, ['4'] = 16, ['5'] = 32, ['6'] = 64, ['7'] = 128, ['8'] = 256, ['9'] = 512, ['a'] = 1024, ['b'] = 2048, ['c'] = 4096, ['d'] = 8192, ['e'] = 16384, ['f'] = 32768, } local lb = {} for k,v in pairs(bl) do lb[v] = k end local ldchart = { --it stands for light/dark chart ["0"] = "0", ["1"] = "4", ["2"] = "6", ["3"] = "0", ["4"] = "0", ["5"] = "0", ["6"] = "0", ["7"] = "8", ["8"] = "0", ["9"] = "3", ["a"] = "2", ["b"] = "9", ["c"] = "1", ["d"] = "5", ["e"] = "2", ["f"] = "7" } local getSizeNFP = function(image) local xsize = 0 if type(image) ~= "table" then return 0,0 end for y = 1, #image do xsize = math.max(xsize,#image[y]) end return xsize, #image end getSize = function(image) assert(checkValid(image), "Invalid image.") local x, y = 0, #image[1] for y = 1, #image[1] do x = math.max(x, #image[1][y]) end return x, y end crop = function(image, x1, y1, x2, y2) assert(checkValid(image), "Invalid image.") local output = {{},{},{}} for y = y1, y2 do output[1][#output[1]+1] = image[1][y]:sub(x1,x2) output[2][#output[2]+1] = image[2][y]:sub(x1,x2) output[3][#output[3]+1] = image[3][y]:sub(x1,x2) end return output end loadImageData = function(image, background) --string image local output = {{},{},{}} --char, text, back local y = 1 local text, back = " ", background or " " local doSkip, c1, c2 = false local maxX = 0 for i = 1, #image do if doSkip then doSkip = false else output[1][y] = output[1][y] or "" output[2][y] = output[2][y] or "" output[3][y] = output[3][y] or "" c1, c2 = image:sub(i,i), image:sub(i+1,i+1) if c1 == tchar then text = c2 doSkip = true elseif c1 == bchar then back = c2 doSkip = true elseif c1 == "\n" then maxX = math.max(maxX, #output[1][y] + 1) y = y + 1 text, back = " ", background or " " else output[1][y] = output[1][y]..c1 output[2][y] = output[2][y]..text output[3][y] = output[3][y]..back end end end for y = 1, #output[1] do output[1][y] = output[1][y] .. (" "):rep(maxX - #output[1][y]) output[2][y] = output[2][y] .. (" "):rep(maxX - #output[2][y]) output[3][y] = output[3][y] .. (" "):rep(maxX - #output[3][y]) end return output end convertFromNFP = function(image) local output = {{},{},{}} local imageX, imageY = getSizeNFP(image) for y = 1, imageY do output[1][y] = "" output[2][y] = "" output[3][y] = "" for x = 1, imageX do output[1][y] = output[1][y]..lb[image[y][x] or " "] output[2][y] = output[2][y]..lb[image[y][x] or " "] output[3][y] = output[3][y]..lb[image[y][x] or " "] end end return output end loadImage = function(path, background) local file = fs.open(path,"r") local output = loadImageData(file.readAll(), background) file.close() return output end unloadImage = function(image) assert(checkValid(image), "Invalid image.") local output = "" local text, back = " ", " " local c, t, b for y = 1, #image[1] do for x = 1, #image[1][y] do c, t, b = image[1][y]:sub(x,x), image[2][y]:sub(x,x), image[3][y]:sub(x,x) if (t ~= text) or (x + y == 2) then output = output..tchar..t end if (b ~= back) or (x + y == 2) then output = output..bchar..b end output = output..c end if y ~= #image[1] then output = output.."\n" text, back = " ", " " end end return output end drawImage = function(image, x, y) assert(checkValid(image), "Invalid image.") local cx, cy = term.getCursorPos() for iy = 1, #image[1] do term.setCursorPos(x,y+(iy-1)) term.blit(image[1][iy], image[2][iy], image[3][iy]) end term.setCursorPos(cx,cy) end drawImageTransparent = function(image, x, y) assert(checkValid(image), "Invalid image. (" .. textutils.serialize(image) .. ")") local cx, cy = term.getCursorPos() local c, t, b for iy = 1, #image[1] do for ix = 1, #image[1][iy] do c, t, b = image[1][iy]:sub(ix,ix), image[2][iy]:sub(ix,ix), image[3][iy]:sub(ix,ix) if not (b == " " and c == " ") then term.setCursorPos(x+(ix-1),y+(iy-1)) term.blit(c, t, b) end end end term.setCursorPos(cx,cy) end drawImageCenter = function(image, x, y) local scr_x, scr_y = term.getSize() local imageX, imageY = getSize(image) return drawImage(image, (x and x or (scr_x/2)) - (imageX/2), (y and y or (scr_y/2)) - (imageY/2)) end drawImageCenterTransparent = function(image, x, y) local scr_x, scr_y = term.getSize() local imageX, imageY = getSize(image) return drawImageTransparent(image, (x and x or (scr_x/2)) - (imageX/2), (y and y or (scr_y/2)) - (imageY/2)) end colorSwap = function(image, tbl) local output = {{},{},{}} for y = 1, #image[1] do output[1][y] = image[1][y] output[2][y] = image[2][y]:gsub(".", tbl) output[3][y] = image[3][y]:gsub(".", tbl) end return output end local xflippable = { ["\129"] = "\130", ["\131"] = "\131", ["\132"] = "\136", ["\133"] = "\138", ["\134"] = "\137", ["\135"] = "\139", ["\140"] = "\140", ["\141"] = "\142", ["\143"] = "\143", } local xinvertable = { ["\144"] = "\159", ["\145"] = "\157", ["\146"] = "\158", ["\147"] = "\156", ["\148"] = "\151", ["\152"] = "\155" } for k,v in pairs(xflippable) do xflippable[v] = k end for k,v in pairs(xinvertable) do xinvertable[v] = k end flipX = function(image) assert(checkValid(image), "Invalid image.") local output = {{},{},{}} for y = 1, #image[1] do output[1][y] = image[1][y]:reverse():gsub(".", xflippable):gsub(".", xinvertable) output[2][y] = "" output[3][y] = "" for x = 1, #image[1][y] do if (not xflippable[image[1][y]:sub(x,x)]) or xinvertable[image[1][y]:sub(x,x)] then output[2][y] = image[3][y]:sub(x,x) .. output[2][y] output[3][y] = image[2][y]:sub(x,x) .. output[3][y] else output[2][y] = image[2][y]:sub(x,x) .. output[2][y] output[3][y] = image[3][y]:sub(x,x) .. output[3][y] end end end return output end flipY = function(image) assert(checkValid(image), "Invalid image.") local output = {{},{},{}} for y = #image[1], 1, -1 do output[1][#output[1]+1] = image[1][y] output[2][#output[2]+1] = image[2][y] output[3][#output[3]+1] = image[3][y] end return output end grayOut = function(image) assert(checkValid(image), "Invalid image.") local output = {{},{},{}} local chart = { ["0"] = "0", ["1"] = "8", ["2"] = "8", ["3"] = "8", ["4"] = "8", ["5"] = "8", ["6"] = "8", ["7"] = "7", ["8"] = "8", ["9"] = "7", ["a"] = "7", ["b"] = "7", ["c"] = "7", ["d"] = "7", ["e"] = "7", ["f"] = "f" } for k,v in pairs(chart) do for y = 1, #image[1] do output[1][y] = image[1][y]:gsub(k,v) output[2][y] = image[2][y]:gsub(k,v) output[3][y] = image[3][y]:gsub(k,v) end end return output end greyOut = grayOut lighten = function(image) assert(checkValid(image), "Invalid image.") local output = {{},{},{}} for k,v in pairs(ldchart) do for y = 1, #image[1] do output[1][y] = image[1][y]:gsub(k,v) output[2][y] = image[2][y]:gsub(k,v) output[3][y] = image[3][y]:gsub(k,v) end end return output end darken = function(image) assert(checkValid(image), "Invalid image.") local output = {{},{},{}} for k,v in pairs(ldchart) do for y = 1, #image[1] do output[1][y] = image[1][y]:gsub(v,k) output[2][y] = image[2][y]:gsub(v,k) output[3][y] = image[3][y]:gsub(v,k) end end return output end stretchImage = function(_image, sx, sy, noRepeat) assert(checkValid(_image), "Invalid image.") local output = {{},{},{}} local image = deepCopy(_image) if sx < 0 then image = flipX(image) end if sy < 0 then image = flipY(image) end sx, sy = math.abs(sx), math.abs(sy) local imageX, imageY = getSize(image) local tx, ty for y = 1, sy do for x = 1, sx do tx = math.ceil((x / sx) * imageX) ty = math.ceil((y / sy) * imageY) if not noRepeat then output[1][y] = (output[1][y] or "")..image[1][ty]:sub(tx,tx) else output[1][y] = (output[1][y] or "").." " end output[2][y] = (output[2][y] or "")..image[2][ty]:sub(tx,tx) output[3][y] = (output[3][y] or "")..image[3][ty]:sub(tx,tx) end end if noRepeat then for y = 1, imageY do for x = 1, imageX do if image[1][y]:sub(x,x) ~= " " then tx = math.ceil(((x / imageX) * sx) - ((0.5 / imageX) * sx)) ty = math.ceil(((y / imageY) * sy) - ((0.5 / imageY) * sx)) output[1][ty] = stringWrite(output[1][ty], tx, image[1][y]:sub(x,x)) end end end end return output end pixelateImage = function(image,amntX, amntY) assert(checkValid(image), "Invalid image.") local imageX, imageY = getSize(image) return stretchImage(stretchImage(image,imageX/math.max(amntX,1), imageY/math.max(amntY,1)), imageX, imageY) end merge = function(...) local images = {...} local output = {{},{},{}} local imageX, imageY = 0, 0 for i = 1, #images do imageY = math.max(imageY, #images[i][1][1]+(images[i][3]-1)) for y = 1, #images[i][1][1] do imageX = math.max(imageX, #images[i][1][1][y]+(images[i][2]-1)) end end --will later add code to adjust X/Y positions if negative values are given local image, xadj, yadj local tx, ty for y = 1, imageY do output[1][y] = {} output[2][y] = {} output[3][y] = {} for x = 1, imageX do for i = 1, #images do image, xadj, yadj = images[i][1], images[i][2], images[i][3] tx, ty = x-(xadj-1), y-(yadj-1) output[1][y][x] = output[1][y][x] or " " output[2][y][x] = output[2][y][x] or " " output[3][y][x] = output[3][y][x] or " " if image[1][ty] then if (image[1][ty]:sub(tx,tx) ~= "") and (tx >= 1) then output[1][y][x] = (image[1][ty]:sub(tx,tx) == " " and output[1][y][x] or image[1][ty]:sub(tx,tx)) output[2][y][x] = (image[2][ty]:sub(tx,tx) == " " and output[2][y][x] or image[2][ty]:sub(tx,tx)) output[3][y][x] = (image[3][ty]:sub(tx,tx) == " " and output[3][y][x] or image[3][ty]:sub(tx,tx)) end end end end output[1][y] = table.concat(output[1][y]) output[2][y] = table.concat(output[2][y]) output[3][y] = table.concat(output[3][y]) end return output end rotateImage = function(image, angle) local output = {{},{},{}} local realOutput = {{},{},{}} local tx, ty local imageX, imageY = getSize(image) local originX, originY = imageX / 2, imageY / 2 local adjX, adjY = 1, 1 for y = 1, #image[1] do for x = 1, #image[1][y] do if not (image[1][y]:sub(x,x) == " " and image[2][y]:sub(x,x) == " " and image[3][y]:sub(x,x) == " ") then tx = math.floor( (x-originX) * math.cos(angle) - (originY-y) * math.sin(angle) ) ty = math.floor( (x-originX) * math.sin(angle) + (originY-y) * math.cos(angle) ) adjX, adjY = math.min(adjX, tx), math.min(adjY, ty) output[1][ty] = output[1][ty] or {} output[2][ty] = output[2][ty] or {} output[3][ty] = output[3][ty] or {} output[1][ty][tx] = image[1][y]:sub(x,x) output[2][ty][tx] = image[2][y]:sub(x,x) output[3][ty][tx] = image[3][y]:sub(x,x) end end end for y = adjY, #output[1] do realOutput[1][y+1-adjY] = {} realOutput[2][y+1-adjY] = {} realOutput[3][y+1-adjY] = {} for x = adjX, #output[1][y] do realOutput[1][y+1-adjY][x+1-adjX] = output[1][y][x] or " " realOutput[2][y+1-adjY][x+1-adjX] = output[2][y][x] or " " realOutput[3][y+1-adjY][x+1-adjX] = output[3][y][x] or " " end end for y = 1, #realOutput[1] do realOutput[1][y] = table.concat(realOutput[1][y]) realOutput[2][y] = table.concat(realOutput[2][y]) realOutput[3][y] = table.concat(realOutput[3][y]) end return realOutput, math.ceil(adjX-1+originX), math.ceil(adjY-1+originY) end help = function(input) local helpOut = { loadImageData = "Loads an NFT image from a string input.", loadImage = "Loads an NFT image from a file path.", convertFromNFP = "Loads a table NFP image into a table NFT image, same as what loadImage outputs.", drawImage = "Draws an image. Does not support transparency, sadly.", drawImageTransparent = "Draws an image. Supports transparency, but not as fast as drawImage.", drawImageCenter = "Draws an image centered around the inputted coordinates. Does not support transparency, sadly.", drawImageCenterTransparent = "Draws an image centered around the inputted coordinates. Supports transparency, but not as fast as drawImageCenter.", flipX = "Returns the inputted image, but with the X inverted.", flipY = "Returns the inputted image, but with the Y inverted.", grayOut = "Returns the inputted image, but with the colors converted into grayscale as best I could.", lighten = "Returns the inputted image, but with the colors lightened.", darken = "Returns the inputted image, but with the colors darkened.", stretchImage = "Returns the inputted image, but it's been stretched to the inputted size. If the fourth argument is true, it will spread non-space characters evenly in the image.", pixelateImage = "Returns the inputted image, but pixelated to a variable degree.", merge = "Merges two or more images together.", crop = "Crops an image between points (X1,Y1) and (X2,Y2).", rotateImage = "Rotates an image, and also returns how much the image center's X and Y had been adjusted.", colorSwap = "Swaps the colors of a given image with another color, according to an inputted table." } return helpOut[input] or "No such function." end