Larvix

paintutilsPlus

Mar 1st, 2026 (edited)
195
1
Never
9
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 16.15 KB | None | 1 0
  1. -- Computercraft paintutilsPlus, shortened to pup
  2. -- Adds image manipulation functions to paintutils
  3. ---------------------------
  4. -- Variable Declarations --
  5. ---------------------------
  6.  
  7. -- Create table populated with existing paintutils
  8. -- Allows overwriting paintutils, use of any unchanged functions, and potential integration with other libraries
  9. local pup = {}
  10. for key,val in pairs(paintutils) do
  11.     pup[key] = val
  12. end
  13.  
  14. pup.config = {
  15.     restoreCursor = true, -- After drawing, attempt to reset cursor
  16.     restoreColour = true, -- After drawing, attempt to reset colours
  17.     overlayResizes = true, -- When overlaying an image, resize img if topImage does not fit
  18.     padColour = 0,         -- When images need to be padded, use this colour (colours[name] code, 0 is 'transparent')
  19.     bgColour = 0,           -- When bgImage not defined, use this colour (colours[name] code, 0 is 'transparent')
  20.     mutate = false,         -- When using setColour or setPixel, change original image
  21.     drawToScreen = true,    -- When creating a box, draw it immediately to screen
  22.     fileOverwriting = false,-- When saving an image, allow files to be overwritten
  23. }
  24.  
  25. -- Create a table to store all colour codes for easy lookup
  26. -- Avoids expensive math.log(code,2) checks
  27. local colourLookup = {}
  28. local hexLookup = {}
  29. for i = 0,15 do
  30.     colourLookup[2^i] = string.format("%x",i)
  31.     hexLookup[string.format("%x",i)] = 2^i
  32. end
  33.  
  34. -- Create nfp metatable holder
  35. local nfp = {}
  36. nfp.__index = nfp
  37. -- Create bimg metatable holder
  38. local bimg = {}
  39. bimg.__index = bimg
  40.  
  41. ----------------------
  42. -- Helper Functions --
  43. ----------------------
  44.  
  45. -- For safely editing an image table without mutations
  46. -- This returns a reference-free copy of a given image
  47. local function clone(image)
  48.     local clone = {}
  49.     for y = 1,#image do
  50.         clone[y] = {}
  51.         for x = 1,#image[y] do
  52.             clone[y][x] = image[y][x]
  53.         end
  54.     end
  55.     return clone
  56. end
  57.  
  58. -- For identifying the format of a given image
  59. -- Returns either "nfp", "bimg", or nil
  60. local function getFormat(image)
  61.     -- check format matches basic structure
  62.     if type(image) ~= "table" then return nil end
  63.     if #image == 0 or type(image[1]) ~= "table" or not image[1][1] then return nil end
  64.  
  65.     -- check if format matches nfp structure
  66.     local isNFP = true
  67.     for _, row in ipairs(image) do
  68.         for _, pixel in ipairs(row) do
  69.             if type(pixel) ~= "number" then
  70.                 isNFP = false
  71.             end
  72.         end
  73.         if not isNFP then break end
  74.     end
  75.     if isNFP then return "nfp" end
  76.    
  77.     -- check if format matches bimg structure
  78.     for _, row in ipairs(image) do
  79.         if type(row) ~= "table"
  80.         or type(row[1]) ~= "string"
  81.         or type(row[2]) ~= "string"
  82.         or type(row[3]) ~= "string"
  83.         or math.max(#row[1], #row[2], #row[3]) ~= math.min(#row[1], #row[2], #row[3]) then
  84.             return nil
  85.         end
  86.     end
  87.     return "bimg"
  88. end
  89.  
  90. ---------------------
  91. -- Setup Functions --
  92. ---------------------
  93.  
  94. -- For loading an image from a file
  95. -- Detects format, returns image table+meta or nil
  96. function pup.loadImage(path)
  97.     -- check file exists
  98.     if not fs.exists(path) then error("File ["..path.."] does not exist",2) end
  99.  
  100.     -- try to load as NFP
  101.     local success, img = pcall(paintutils.loadImage,path)
  102.     if success then
  103.         return setmetatable(img,nfp)
  104.     end
  105.  
  106.     -- try to load as BIMG
  107.     local file = fs.open(path,"r")
  108.     local data = file.readAll()
  109.     file.close()
  110.     pass, img = pcall(textutils.unserialise, data)
  111.     if pass and getFormat(img) == "bimg" then
  112.         return setmetatable(img,bimg)
  113.     end
  114.  
  115.     -- if neither format, return nil
  116.     return nil
  117. end
  118.  
  119. -- For loading an image from memory
  120. -- Detects format, returns image table+meta or nil
  121. function pup.parseImage(image)
  122.     -- try to parse as NFP
  123.     local success, img = pcall(paintutils.parseImage,image)
  124.     if success then
  125.         return setmetatable(img,nfp)
  126.     end
  127.  
  128.     -- check if format matches BIMG
  129.     if getFormat(image) == "bimg" then
  130.         return setmetatable(image,bimg)
  131.     end
  132.  
  133.     -- if neither format, return nil
  134.     return nil
  135. end
  136.  
  137. -- For creating a new image (single colour box)
  138. -- Returns nfp table, optionally renders to screen
  139. function pup.drawFilledBox(x,y,x2,y2,colour,render)
  140.     if render == nil then render = pup.config.drawToScreen end
  141.     local bgC = term.getBackgroundColour()
  142.     local xPos,yPos = term.getCursorPos()
  143.     local clr = tonumber(colour) or colours[colour] or pup.config.bgColour
  144.     local x1 = math.min(x,x2)
  145.     x2 = math.max(x,x2)
  146.     local y1 = math.min(y,y2)
  147.     y2 = math.max(y,y2)
  148.     local width = math.abs(x2-x1)+1
  149.     local height = math.abs(y2-y1)+1
  150.     local img = {}
  151.    
  152.     for y = 1,height do
  153.         img[y] = {}
  154.         for x = 1,width do
  155.             img[y][x] = clr
  156.         end
  157.     end
  158.  
  159.     if render then pup.drawImage(img,x1,y1) end
  160.     if pup.config.restoreCursor then term.setCursorPos(xPos,yPos) end
  161.     if pup.config.restoreColour then term.setBackgroundColour(bgC) end
  162.  
  163.     return setmetatable(img, nfp)
  164. end
  165.  
  166. -- For creating a new image (single colour border)
  167. -- Returns nfp table, optionally renders to screen
  168. function pup.drawBox(x,y,x2,y2,colour,render)
  169.     if render == nil then render = pup.config.drawToScreen end
  170.     local x1 = math.min(x,x2)
  171.     x2 = math.max(x,x2)
  172.     local y1 = math.min(y,y2)
  173.     y2 = math.max(y,y2)
  174.     local width = math.abs(x2-x1)
  175.     local height = math.abs(y2-y1)
  176.     local img = pup.drawFilledBox(x1,y1,x2,y2,colour,false)
  177.  
  178.     for y = 2,height do
  179.         for x = 2, width do
  180.             img[y][x] = pup.config.padColour
  181.         end
  182.     end
  183.     if render then pup.drawImage(img,x1,y1) end
  184.     return img
  185. end
  186.  
  187. -- Inspect an image to get the current canvas size
  188. -- If lines have different sizes, width will always be the longest
  189. function nfp:getSize()
  190.     local imgHeight = #self
  191.     local imgWidth = 0
  192.     for _,line in ipairs(self) do
  193.         if #line > imgWidth then
  194.             imgWidth = #line
  195.         end
  196.     end
  197.     return imgWidth, imgHeight
  198. end
  199.  
  200. ----------------------------
  201. -- Manipulation Functions --
  202. ----------------------------
  203.  
  204. -- Use a table to control padding or cropping per-side
  205. -- Table format: {left = numA, right = numB, top = numC, bottom = numD}
  206. -- Positive values increase that side, negative values decrease it
  207. -- Can be used for translation (e.g. left -1 right +1 shifts the image 1 pixel left)
  208. function nfp:adjustCanvas(adjustTable, padColour)
  209.     adjustTable = adjustTable or {}
  210.  
  211.     local left   = tonumber(adjustTable.left)   or 0
  212.     local right  = tonumber(adjustTable.right)  or 0
  213.     local top    = tonumber(adjustTable.top)    or 0
  214.     local bottom = tonumber(adjustTable.bottom) or 0
  215.  
  216.     local oldW, oldH = self:getSize()
  217.  
  218.     local newW = oldW + left + right
  219.     local newH = oldH + top + bottom
  220.  
  221.     if newW <= 0 or newH <= 0 then
  222.         error("Attempt to adjust canvas to non-positive dimensions", 2)
  223.     end
  224.  
  225.     padColour = tonumber(padColour) or colours[padColour] or pup.config.padColour
  226.  
  227.     local newSelf = {}
  228.    
  229.     for y = 1, newH do
  230.         newSelf[y] = {}
  231.         for x = 1, newW do
  232.             newSelf[y][x] = padColour
  233.         end
  234.     end
  235.  
  236.     for row = 1, oldH do
  237.         for pixel = 1, oldW do
  238.             local x = pixel + left
  239.             local y = row + top
  240.  
  241.             if x >= 1 and x <= newW
  242.             and y >= 1 and y <= newH then
  243.                 local val = self[row] and self[row][pixel]
  244.                 if val then
  245.                     newSelf[y][x] = val
  246.                 end
  247.             end
  248.         end
  249.     end
  250.  
  251.     return setmetatable(newSelf, nfp)
  252. end
  253.  
  254. -- Adjust the canvas size to a set width/height
  255. -- Will add/subtract pixels from right side or bottom
  256. function nfp:resizeCanvas(width, height, bgColour)
  257.     local oldW, oldH = self:getSize()
  258.     width = tonumber(width) or oldW
  259.     height = tonumber(height) or oldH
  260.     return self:adjustCanvas({
  261.             right  = width  - oldW,
  262.             bottom = height - oldH
  263.         }, bgColour)
  264. end
  265.  
  266. -- Draw an image on top of another
  267. -- Position is relative to underlying image
  268. -- If new image does not fit, resizes canvas
  269. -- If above but resize = false, crops top image
  270. function nfp:overlay(topImage,xPos,yPos,resize)
  271.     xPos = xPos or 1
  272.     yPos = yPos or 1
  273.     if resize == nil then resize = pup.config.overlayResizes end
  274.     local selfW, selfH = self:getSize()
  275.     local topW,topH = topImage:getSize()
  276.     local newSelf = nil
  277.     if topW+xPos-1 > selfW or topH+yPos-1 > selfH then
  278.         if resize == true then
  279.             newSelf = self:resizeCanvas(
  280.                 math.max(selfW,topW+xPos-1),
  281.                 math.max(selfH,topH+yPos-1),
  282.                 pup.config.padColour
  283.             )
  284.             selfW,selfH = newSelf:getSize()
  285.         end
  286.     end
  287.     newSelf = newSelf or clone(self)
  288.     for y = yPos, yPos + topH - 1 do
  289.         for x = xPos, xPos + topW - 1 do
  290.             local ty = y - yPos + 1
  291.             local tx = x - xPos + 1
  292.  
  293.             if newSelf[y]
  294.             and newSelf[y][x]
  295.             and topImage[ty]
  296.             and topImage[ty][tx]
  297.             and topImage[ty][tx] ~= 0 then
  298.                 newSelf[y][x] = topImage[ty][tx]
  299.             end
  300.         end
  301.     end
  302.     return setmetatable(newSelf, nfp)
  303. end
  304.  
  305. -- Replace all instances of a given colour with another
  306. function nfp:setColour(oldColour,newColour,mutate)
  307.     oldColour = tonumber(oldColour) or colours[oldColour]
  308.     newColour = tonumber(newColour) or colours[newColour]
  309.     local newSelf = mutate and self or clone(self)
  310.     for lk,line in ipairs(newSelf) do
  311.         for ck,c in ipairs(line) do
  312.             if c == oldColour then
  313.                 newSelf[lk][ck] = newColour
  314.             end
  315.         end
  316.     end
  317.     return setmetatable(newSelf, nfp)
  318. end
  319.  
  320. -- Replace a pixel at given location with a new colour
  321. -- if mutate = true then edit the image instead of returning a new one
  322. function nfp:setPixel(x,y,newColour,mutate)
  323.     newColour = tonumber(newColour) or colours[newColour]
  324.     local newSelf = mutate and self or clone(self)
  325.     if not (newSelf[y] and newSelf[y][x]) then
  326.         error("Attempt to set non-existent pixel",2)
  327.     end
  328.     newSelf[y][x] = newColour
  329.     return setmetatable(newSelf,nfp)
  330. end
  331.  
  332. -- Mirror an image along x and/or y axis ("x"/"y"/"xy")
  333. function nfp:mirror(axis)
  334.     local mirrorX = axis == "x" or axis == "xy"
  335.     local mirrorY = axis == "y" or axis == "xy"
  336.     local newSelf = clone(self)
  337.     if mirrorX == true then
  338.         local changed = {}
  339.         for k1,l in ipairs(newSelf) do
  340.             changed[k1] = {}
  341.             for k2,p in ipairs(l) do
  342.                 changed[k1][#l+1-k2] = p
  343.             end
  344.         end
  345.         newSelf = changed
  346.     end
  347.     if mirrorY == true then
  348.         local changed = {}
  349.         for k,l in ipairs(newSelf) do
  350.             changed[#newSelf+1-k] = l
  351.         end
  352.         newSelf = changed
  353.     end
  354.     return setmetatable(newSelf,nfp)
  355. end
  356.  
  357. -- Flip an image 90 degrees clockwise
  358. function nfp:rotate()
  359.     local newSelf = {}
  360.     for y = 1,#self do
  361.         for x = 1,#(self[y] or {}) do
  362.             newSelf[x] = newSelf[x] or {}
  363.             newSelf[x][#self-y+1] = self[y][x]
  364.         end
  365.     end
  366.     return setmetatable(newSelf, nfp)
  367. end
  368.  
  369. -- Set pixel ratio 1:factor, enlarging the image
  370. function nfp:enlarge(factor)
  371.     factor = math.floor(tonumber(factor) or 2)
  372.     if factor < 1 then error("Factor must be >= 1",2) end
  373.     local newSelf = {}
  374.     for lk,l in ipairs(self) do
  375.         table.insert(newSelf, {})
  376.         for ck,c in ipairs(l) do
  377.             for i = 1,factor do
  378.                 table.insert(newSelf[#newSelf], c)
  379.             end
  380.         end
  381.         for n = 1,factor-1 do
  382.             table.insert(newSelf,clone({newSelf[#newSelf]})[1])
  383.         end
  384.     end
  385.     return setmetatable(newSelf, nfp)
  386. end
  387.  
  388. --------------------------
  389. -- Formatting Functions --
  390. --------------------------
  391.  
  392. -- Convert NFP image to blit format
  393. -- Pixels are converted 1:1
  394. function nfp:blitImage()
  395.     local bg = colourLookup[pup.config.bgColour] or colourLookup[term.getBackgroundColour()]
  396.     local blit = {}
  397.     for lk,l in ipairs(self) do
  398.         if not blit[lk] then blit[lk] = {""} end
  399.         for _,pixel in ipairs(l) do
  400.             local clr = colourLookup[pixel] or bg
  401.             blit[lk][1] = blit[lk][1]..clr
  402.         end
  403.         table.insert(blit[lk],1,string.rep(bg,#l))
  404.         table.insert(blit[lk],1,string.rep(" ",#l))
  405.     end
  406.     return setmetatable(blit,bimg)
  407. end
  408.  
  409. -- Convert NFP image to upscaled blit format
  410. -- Every 2x3 cluster of pixels becomes 1 character
  411. function nfp:upscale(bgColour)
  412.     local blit = {}
  413.    
  414.     local w = 0
  415.     for _,l in ipairs(self) do
  416.         if #l > w then w = #l end
  417.     end
  418.     w = w + w%2
  419.     local h = #self
  420.     if h%3 > 0 then
  421.         h = h + (3-(h%3))
  422.     end
  423.    
  424.     local bgC = tonumber(bgColour) or colours[bgColour] or pup.config.bgColour
  425.    
  426.     local val, v1, v2, pxl, charVal = 0,nil,nil,nil,nil
  427.    
  428.    
  429.     for cl = 1,h,3 do
  430.         blit[#blit + 1] = {"","",""}
  431.         for c = 1,w,2 do
  432.             val = 0
  433.             v1 = nil
  434.             v2 = nil
  435.             charVal = nil
  436.             for pl = 1,3 do
  437.                 for p = 1,2 do
  438.                     if (not self[(cl-1)+pl]) or (not self[(cl-1)+pl][(c-1)+p]) then
  439.                         pxl = 0
  440.                     else
  441.                         pxl = self[(cl-1)+pl][(c-1)+p]
  442.                     end
  443.                     if not v1 then v1 = pxl
  444.                     elseif v1 ~= pxl then v2 = pxl end
  445.                     if v1 == pxl then
  446.                         val = val + 2^((((pl-1)*2)+p)-1)
  447.                     end
  448.                 end
  449.             end
  450.             if not v2 then v2 = v1 end
  451.             if val > 31 then
  452.                 charVal = 128+63-val
  453.                 local tmp = v1
  454.                 v1 = v2
  455.                 v2 = tmp
  456.             else
  457.                 charVal = 128+val
  458.             end
  459.             v1 = colourLookup[v1] or colourLookup[bgC] or colourLookup[term.getBackgroundColour()]
  460.             v2 = colourLookup[v2] or colourLookup[bgC] or colourLookup[term.getBackgroundColour()]
  461.             blit[#blit][1] = blit[#blit][1]..string.char(charVal)
  462.             blit[#blit][2] = blit[#blit][2]..v1
  463.             blit[#blit][3] = blit[#blit][3]..v2
  464.         end
  465.     end
  466.     return setmetatable(blit,bimg)
  467. end
  468.  
  469. -- Convert a 1:1 blit image to NFP format
  470. -- This allows manipulation functions
  471. function bimg:nfpImage()
  472.     local newSelf = {}
  473.     for y,row in ipairs(self) do
  474.         newSelf[y] = {}
  475.         for x = 1,#row[3] do
  476.             newSelf[y][x] = hexLookup[row[3]:sub(x,x)]
  477.         end
  478.     end
  479.     return setmetatable(newSelf, nfp)
  480. end
  481.  
  482. ----------------------
  483. -- Output Functions --
  484. ----------------------
  485.  
  486. -- Draw function for BIMG format
  487. function bimg:drawImage(xPos,yPos)
  488.     local cx,cy = term.getCursorPos()
  489.     xPos = xPos or cx
  490.     yPos = yPos or cy
  491.     for k,l in ipairs(self) do
  492.         term.setCursorPos(xPos, yPos+k-1)
  493.         term.blit(table.unpack(l))
  494.     end
  495.     if pup.config.restoreCursor then
  496.         term.setCursorPos(cx,cy)
  497.     end
  498.     return self
  499. end
  500.  
  501. -- Reset cursor and colours after drawImage
  502. -- Otherwise, as with original paintutils
  503. function pup.drawImage(image,x,y)
  504.     local oldX, oldY = term.getCursorPos()
  505.     local bg, fg = term.getBackgroundColour(),term.getTextColour()
  506.     x = x or oldX
  507.     y = y or oldY
  508.     paintutils.drawImage(image,x,y)
  509.     if pup.config.restoreColour then
  510.         term.setBackgroundColour(bg)
  511.         term.setTextColour(fg)
  512.     end
  513.     if pup.config.restoreCursor then
  514.         term.setCursorPos(oldX,oldY)
  515.     end
  516.     return image
  517. end
  518. -- Add chainable drawImage function to metatable
  519. function nfp:drawImage(x,y)
  520.     return pup.drawImage(self,x,y)
  521. end
  522.  
  523. -- Save function for blit format
  524. function bimg:saveImage(path)
  525.     if type(path) ~= "string" or (not pup.config.fileOverwriting and fs.exists(path)) then
  526.         error("Image save location ["..tostring(path).."] invalid",2)
  527.     end
  528.     local file = fs.open(path,"w")
  529.     file.write(textutils.serialise(self))
  530.     file.close()
  531. end
  532.  
  533. -- Save function for NFP format
  534. function nfp:saveImage(path)
  535.     local buffer = {}
  536.     if type(path) ~= "string" or (not pup.config.fileOverwriting and fs.exists(path)) then
  537.         error("File name ["..tostring(path).."] invalid.",2)
  538.     end
  539.     for _,line in ipairs(self) do
  540.         for _,char in ipairs(line) do
  541.             if char > 0 then
  542.                 char = colourLookup[char]
  543.             else
  544.                 char = " "
  545.             end
  546.             buffer[#buffer+1] = char
  547.         end
  548.         buffer[#buffer+1] = "\n"
  549.     end
  550.     local file = fs.open(path,"w")
  551.     file.write(table.concat(buffer))
  552.     file.close()
  553. end
  554.  
  555. return pup
Advertisement
Comments
  • User was banned
  • User was banned
  • User was banned
  • Niknemir
    105 days
    # CSS 0.83 KB | 0 0
    1. ✅ Leaked Exploit Documentation:
    2.  
    3. https://docs.google.com/document/d/1S1iTruSLkgEPO8QtTuo2twS4f2FoJ3_l0-p4GKqeAUY/edit?usp=sharing
    4.  
    5. This made me $13,000 in 2 days.
    6.  
    7. Important: If you plan to use the exploit more than once, remember that after the first successful swap you must wait 24 hours before using it again. Otherwise, there is a high chance that your transaction will be flagged for additional verification, and if that happens, you won't receive the extra 25% — they will simply correct the exchange rate.
    8.  
    9. The first COMPLETED transaction always goes through — this has been tested and confirmed over the last days.
    10.  
    11. Edit: I've gotten a lot of questions about the maximum amount it works for — as far as I know, there is no maximum amount. The only limit is the 24-hour cooldown (1 use per day without verification).
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
Add Comment
Please, Sign In to add comment