daily pastebin goal
70%
SHARE
TWEET

BLittle (ComputerCraft)

BombBloke Dec 13th, 2015 (edited) 2,909 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- +--------------------------------------------------------+
  2. -- |                                                        |
  3. -- |                        BLittle                         |
  4. -- |                                                        |
  5. -- +--------------------------------------------------------+
  6.  
  7. local version = "Version 1.1.6beta"
  8.  
  9. -- By Jeffrey Alexander, aka Bomb Bloke.
  10. -- Convenience functions to make use of ComputerCraft 1.76's new "drawing" characters.
  11. -- http://www.computercraft.info/forums2/index.php?/topic/25354-cc-176-blittle-api/
  12.  
  13. -------------------------------------------------------------
  14.  
  15. if shell then
  16.     local arg = {...}
  17.    
  18.     if #arg == 0 then
  19.         print("Usage:")
  20.         print("blittle <scriptName> [args]")
  21.         return
  22.     end
  23.    
  24.     if not blittle then os.loadAPI(shell.getRunningProgram()) end
  25.     local oldTerm = term.redirect(blittle.createWindow())
  26.     shell.run(unpack(arg))
  27.     term.redirect(oldTerm)
  28.    
  29.     return
  30. end
  31.  
  32. 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},
  33.         {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}}
  34.  
  35. local colourNum, exponents, colourChar = {}, {}, {}
  36. for i = 0, 15 do exponents[2^i] = i end
  37. do
  38.     local hex = "0123456789abcdef"
  39.     for i = 1, 16 do
  40.         colourNum[hex:sub(i, i)] = i - 1
  41.         colourNum[i - 1] = hex:sub(i, i)
  42.         colourChar[hex:sub(i, i)] = 2 ^ (i - 1)
  43.         colourChar[2 ^ (i - 1)] = hex:sub(i, i)
  44.        
  45.         local thisRel = relations[i - 1]
  46.         for i = 1, #thisRel do thisRel[i] = 2 ^ thisRel[i] end
  47.     end
  48. end
  49.  
  50. local function getBestColourMatch(usage)
  51.     local lastCol = relations[exponents[usage[#usage][1]]]
  52.  
  53.     for j = 1, #lastCol do
  54.         local thisRelation = lastCol[j]
  55.         for i = 1, #usage - 1 do if usage[i][1] == thisRelation then return i end end
  56.     end
  57.    
  58.     return 1
  59. end
  60.  
  61. local function colsToChar(pattern, totals)
  62.     if not totals then
  63.         local newPattern = {}
  64.         totals = {}
  65.         for i = 1, 6 do
  66.             local thisVal = pattern[i]
  67.             local thisTot = totals[thisVal]
  68.             totals[thisVal], newPattern[i] = thisTot and (thisTot + 1) or 1, thisVal
  69.         end
  70.         pattern = newPattern
  71.     end
  72.    
  73.     local usage = {}
  74.     for key, value in pairs(totals) do usage[#usage + 1] = {key, value} end
  75.    
  76.     if #usage > 1 then
  77.         -- Reduce the chunk to two colours:
  78.         while #usage > 2 do
  79.             table.sort(usage, function (a, b) return a[2] > b[2] end)
  80.             local matchToInd, usageLen = getBestColourMatch(usage), #usage
  81.             local matchFrom, matchTo = usage[usageLen][1], usage[matchToInd][1]
  82.             for i = 1, 6 do if pattern[i] == matchFrom then
  83.                 pattern[i] = matchTo
  84.                 usage[matchToInd][2] = usage[matchToInd][2] + 1
  85.             end end
  86.             usage[usageLen] = nil
  87.         end
  88.  
  89.         -- Convert to character. Adapted from oli414's function:
  90.         -- http://www.computercraft.info/forums2/index.php?/topic/25340-cc-176-easy-drawing-characters/
  91.         local data = 128
  92.         for i = 1, #pattern - 1 do if pattern[i] ~= pattern[6] then data = data + 2^(i-1) end end
  93.         return string.char(data), colourChar[usage[1][1] == pattern[6] and usage[2][1] or usage[1][1]], colourChar[pattern[6]]
  94.     else
  95.         -- Solid colour character:
  96.         return "\128", colourChar[pattern[1]], colourChar[pattern[1]]
  97.     end
  98. end
  99.  
  100. local function snooze()
  101.     local myEvent = tostring({})
  102.     os.queueEvent(myEvent)
  103.     os.pullEvent(myEvent)
  104. end
  105.  
  106. function shrink(image, bgCol)
  107.     local results, width, height, bgCol = {{}, {}, {}}, 0, #image + #image % 3, bgCol or colours.black
  108.     for i = 1, #image do if #image[i] > width then width = #image[i] end end
  109.    
  110.     for y = 0, height - 1, 3 do
  111.         local cRow, tRow, bRow, counter = {}, {}, {}, 1
  112.        
  113.         for x = 0, width - 1, 2 do
  114.             -- Grab a 2x3 chunk:
  115.             local pattern, totals = {}, {}
  116.            
  117.             for yy = 1, 3 do for xx = 1, 2 do
  118.                 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
  119.                 totals[pattern[#pattern]] = totals[pattern[#pattern]] and (totals[pattern[#pattern]] + 1) or 1
  120.             end end
  121.            
  122.             cRow[counter], tRow[counter], bRow[counter] = colsToChar(pattern, totals)
  123.             counter = counter + 1
  124.         end
  125.        
  126.         results[1][#results[1] + 1], results[2][#results[2] + 1], results[3][#results[3] + 1] = table.concat(cRow), table.concat(tRow), table.concat(bRow)
  127.     end
  128.    
  129.     results.width, results.height = #results[1][1], #results[1]
  130.    
  131.     return results
  132. end
  133.  
  134. function shrinkGIF(image, bgCol)
  135.     if not GIF and not os.loadAPI("GIF") then error("blittle.shrinkGIF: Load GIF API first.", 2) end
  136.    
  137.     image = GIF.flattenGIF(image)
  138.     snooze()
  139.    
  140.     local prev = GIF.toPaintutils(image[1])
  141.     snooze()
  142.    
  143.     prev = blittle.shrink(prev, bgCol)
  144.     prev.delay = image[1].delay
  145.     image[1] = prev
  146.     snooze()
  147.    
  148.     image.width, image.height = prev.width, prev.height
  149.    
  150.     for i = 2, #image do
  151.         local temp = GIF.toPaintutils(image[i])
  152.         snooze()
  153.        
  154.         temp = blittle.shrink(temp, bgCol)
  155.         snooze()
  156.        
  157.         local newImage = {{}, {}, {}, ["delay"] = image[i].delay, ["width"] = temp.width, ["height"] = 0}
  158.        
  159.         local a, b, c, pa, pb, pc = temp[1], temp[2], temp[3], prev[1], prev[2], prev[3]
  160.         for i = 1, temp.height do
  161.             local a1, b1, c1, pa1, pb1, pc1 = a[i], b[i], c[i], pa[i], pb[i], pc[i]
  162.            
  163.             if a1 ~= pa1 or b1 ~= pb1 or c1 ~= pc1 then
  164.                 local min, max = 1, #a1
  165.                 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)}
  166.                
  167.                 for j = 1, max do if a2[j] ~= pa2[j] or b2[j] ~= pb2[j] or c2[j] ~= pc2[j] then
  168.                     min = j
  169.                     break
  170.                 end end
  171.                
  172.                 for j = max, min, -1 do if a2[j] ~= pa2[j] or b2[j] ~= pb2[j] or c2[j] ~= pc2[j] then
  173.                     max = j
  174.                     break
  175.                 end end
  176.                
  177.                 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
  178.             end
  179.            
  180.             snooze()
  181.         end
  182.        
  183.         image[i], prev = newImage, temp
  184.        
  185.         for j = 1, i - 1 do
  186.             local oldImage = image[j]
  187.            
  188.             if type(oldImage[1]) == "table" and oldImage.height == newImage.height then
  189.                 local same = true
  190.                
  191.                 for k = 1, oldImage.height do
  192.                     local comp1, comp2 = oldImage[1][k], newImage[1][k]
  193.                    
  194.                     if type(comp1) ~= type(comp2) or
  195.                         (type(comp1) == "string" and comp1 ~= comp2) or
  196.                         (type(comp1) == "table" and (comp1[1] ~= comp2[1] or comp1[2] ~= comp2[2])) or
  197.                         oldImage[2][k] ~= newImage[2][k] or
  198.                         oldImage[3][k] ~= newImage[3][k] then
  199.                             same = false
  200.                             break
  201.                     end
  202.                 end
  203.                
  204.                 if same then
  205.                     newImage[1], newImage[2], newImage[3] = j
  206.                     break
  207.                 end
  208.             end
  209.            
  210.             snooze()
  211.         end
  212.     end
  213.    
  214.     return image
  215. end
  216.  
  217. local function newLine(width, bCol)
  218.     local line = {}
  219.     for i = 1, width do line[i] = {bCol, bCol, bCol, bCol, bCol, bCol} end
  220.     return line
  221. end
  222.  
  223. function createWindow(parent, x, y, width, height, visible)
  224.     if parent == term or not parent then
  225.         parent = term.current()
  226.     elseif type(parent) ~= "table" or not parent.write then
  227.         error("blittle.newWindow: \"parent\" does not appear to be a terminal object.", 2)
  228.     end
  229.    
  230.     local workBuffer, backBuffer, frontBuffer, window, tCol, bCol, curX, curY, blink, cWidth, cHeight, pal = {}, {}, {}, {}, colours.white, colours.black, 1, 1, false
  231.     if type(visible) ~= "boolean" then visible = true end
  232.     x, y = x and math.floor(x) or 1, y and math.floor(y) or 1
  233.    
  234.     do
  235.         local xSize, ySize = parent.getSize()
  236.         cWidth, cHeight = (width or xSize), (height or ySize)
  237.         width, height = cWidth * 2, cHeight * 3
  238.     end
  239.    
  240.     if parent.setPaletteColour then
  241.         pal = {}
  242.        
  243.         local counter = 1
  244.         for i = 1, 16 do
  245.             pal[counter] = {parent.getPaletteColour(counter)}
  246.             counter = counter * 2
  247.         end
  248.        
  249.         window.getPaletteColour = function(colour)
  250.             return unpack(pal[colour])
  251.         end
  252.        
  253.         window.setPaletteColour = function(colour, r, g, b)
  254.             pal[colour] = {r, g, b}
  255.             if visible then return parent.setPaletteColour(colour, r, g, b) end
  256.         end
  257.        
  258.         window.getPaletteColor, window.setPaletteColor = window.getPaletteColour, window.setPaletteColour
  259.     end
  260.    
  261.     window.blit = function(_, _, bC)
  262.         local bClen = #bC
  263.         if curX > width or curX + bClen < 2 or curY < 1 or curY > height then
  264.             curX = curX + bClen
  265.             return
  266.         end
  267.        
  268.         if curX < 1 then
  269.             bC = bC:sub(2 - curX)
  270.             curX, bClen = 1, #bC
  271.         end
  272.        
  273.         if curX + bClen - 1 > width then bC, bClen = bC:sub(1, width - curX + 1), width - curX + 1 end
  274.  
  275.         local colNum, rowNum, thisX, yBump = math.floor((curX - 1) / 2) + 1, math.floor((curY - 1) / 3) + 1, (curX - 1) % 2, ((curY - 1) % 3) * 2
  276.         local firstColNum, lastColNum, thisRow = colNum, math.floor((curX + bClen) / 2), backBuffer[rowNum]
  277.         local thisChar = thisRow[colNum]
  278.        
  279.         for i = 1, bClen do
  280.             thisChar[thisX + yBump + 1] = colourChar[bC:sub(i, i)]
  281.            
  282.             if thisX == 1 then
  283.                 thisX, colNum = 0, colNum + 1
  284.                 thisChar = thisRow[colNum]
  285.                 if not thisChar then break end
  286.             else thisX = 1 end
  287.         end
  288.        
  289.         if visible then
  290.             local chars1, chars2, chars3, count = {}, {}, {}, 1
  291.            
  292.             for i = firstColNum, lastColNum do
  293.                 chars1[count], chars2[count], chars3[count] = colsToChar(thisRow[i])
  294.                 count = count + 1
  295.             end
  296.            
  297.             chars1, chars2, chars3 = table.concat(chars1), table.concat(chars2), table.concat(chars3)
  298.             parent.setCursorPos(x + math.floor((curX - 1) / 2), y + math.floor((curY - 1) / 3))
  299.             parent.blit(chars1, chars2, chars3)
  300.             local thisRow = frontBuffer[rowNum]
  301.             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)}
  302.         else
  303.             local thisRow = workBuffer[rowNum]
  304.            
  305.             if (not thisRow[firstColNum]) or thisRow[firstColNum] < lastColNum then
  306.                 local x, newLastColNum = 1, lastColNum
  307.                
  308.                 while x <= lastColNum + 1 do
  309.                     local thisSpot = thisRow[x]
  310.                    
  311.                     if thisSpot then
  312.                         if thisSpot >= firstColNum - 1 then
  313.                             if x < firstColNum then firstColNum = x else thisRow[x] = nil end
  314.                             if thisSpot > newLastColNum then newLastColNum = thisSpot end
  315.                         end
  316.                         x = thisSpot + 1
  317.                     else x = x + 1 end
  318.                 end
  319.                
  320.                 thisRow[firstColNum] = newLastColNum
  321.                 if thisRow.max <= newLastColNum then thisRow.max = firstColNum end
  322.             end
  323.         end
  324.        
  325.         curX = curX + bClen
  326.     end
  327.    
  328.     window.write = function(text)
  329.         window.blit(nil, nil, string.rep(colourChar[bCol], #tostring(text)))
  330.     end
  331.    
  332.     window.clearLine = function()
  333.         local oldX = curX
  334.         curX = 1
  335.         window.blit(nil, nil, string.rep(colourChar[bCol], width))
  336.         curX = oldX
  337.     end
  338.    
  339.     window.clear = function()
  340.         local t, fC, bC = string.rep("\128", cWidth), string.rep(colourChar[tCol], cWidth), string.rep(colourChar[bCol], cWidth)
  341.         for y = 1, cHeight do workBuffer[y], backBuffer[y], frontBuffer[y] = {["max"] = 0}, newLine(cWidth, bCol), {t, fC, bC} end
  342.         window.redraw()
  343.     end
  344.    
  345.     window.getCursorPos = function()
  346.         return curX, curY
  347.     end
  348.    
  349.     window.setCursorPos = function(newX, newY)
  350.         curX, curY = math.floor(newX), math.floor(newY)
  351.         if visible and blink then window.restoreCursor() end
  352.     end
  353.    
  354.     window.restoreCursor = function() end
  355.     window.setCursorBlink = window.restoreCursor
  356.    
  357.     window.isColour = function()
  358.         return parent.isColour()
  359.     end
  360.     window.isColor = window.isColour
  361.    
  362.     window.getSize = function()
  363.         return width, height
  364.     end
  365.    
  366.     window.scroll = function(lines)
  367.         lines = math.floor(lines)
  368.        
  369.         if lines ~= 0 then
  370.             if lines % 3 == 0 then
  371.                 local newWB, newBB, newFB, line1, line2, line3 = {}, {}, {}, string.rep("\128", cWidth), string.rep(colourChar[tCol], cWidth), string.rep(colourChar[bCol], cWidth)
  372.                 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
  373.                 workBuffer, backBuffer, frontBuffer = newWB, newBB, newFB
  374.             else
  375.                 local newBB, tRowNum, tBump, sRowNum, sBump = {}, 1, 0, math.floor(lines / 3) + 1, (lines % 3) * 2
  376.                 local sRow, tRow = backBuffer[sRowNum], {}
  377.                 for x = 1, cWidth do tRow[x] = {} end
  378.                
  379.                 for y = 1, height do
  380.                     if sRow then
  381.                         for x = 1, cWidth do
  382.                             local tChar, sChar = tRow[x], sRow[x]
  383.                             tChar[tBump + 1], tChar[tBump + 2] = sChar[sBump + 1], sChar[sBump + 2]
  384.                         end
  385.                     else
  386.                         for x = 1, cWidth do
  387.                             local tChar = tRow[x]
  388.                             tChar[tBump + 1], tChar[tBump + 2] = bCol, bCol
  389.                         end
  390.                     end
  391.                    
  392.                     tBump, sBump = tBump + 2, sBump + 2
  393.                    
  394.                     if tBump > 4 then
  395.                         tBump, newBB[tRowNum] = 0, tRow
  396.                         tRowNum, tRow = tRowNum + 1, {}
  397.                         for x = 1, cWidth do tRow[x] = {} end
  398.                     end
  399.                    
  400.                     if sBump > 4 then
  401.                         sRowNum, sBump = sRowNum + 1, 0
  402.                         sRow = backBuffer[sRowNum]
  403.                     end
  404.                 end
  405.                
  406.                 for y = 1, cHeight do workBuffer[y] = {["max"] = 1, cWidth} end
  407.                
  408.                 backBuffer = newBB
  409.             end
  410.            
  411.             window.redraw()
  412.         end
  413.     end
  414.    
  415.     window.setTextColour = function(newCol)
  416.         tCol = newCol
  417.     end
  418.     window.setTextColor = window.setTextColour
  419.    
  420.     window.setBackgroundColour = function(newCol)
  421.         bCol = newCol
  422.     end
  423.     window.setBackgroundColor = window.setBackgroundColour
  424.    
  425.     window.getTextColour = function()
  426.         return tCol
  427.     end
  428.     window.getTextColor = window.getTextColour
  429.    
  430.     window.getBackgroundColour = function()
  431.         return bCol
  432.     end
  433.     window.getBackgroundColor = window.getBackgroundColour
  434.    
  435.     window.redraw = function()
  436.         if visible then
  437.             for i = 1, cHeight do
  438.                 local work, front = workBuffer[i], frontBuffer[i]
  439.                 local front1, front2, front3 = front[1], front[2], front[3]
  440.  
  441.                 if work.max > 0 then
  442.                     local line1, line2, line3, lineLen, skip, back, count = {}, {}, {}, 1, 0, backBuffer[i], 1
  443.  
  444.                     while count <= work.max do if work[count] then
  445.                         if skip > 0 then
  446.                             line1[lineLen], line2[lineLen], line3[lineLen] = front1:sub(count - skip, count - 1), front2:sub(count - skip, count - 1), front3:sub(count - skip, count - 1)
  447.                             skip, lineLen = 0, lineLen + 1
  448.                         end
  449.  
  450.                         for i = count, work[count] do
  451.                             line1[lineLen], line2[lineLen], line3[lineLen] = colsToChar(back[i])
  452.                             lineLen = lineLen + 1
  453.                         end
  454.  
  455.                         count = work[count] + 1
  456.                     else skip, count = skip + 1, count + 1 end end
  457.  
  458.                     if count < cWidth + 1 then line1[lineLen], line2[lineLen], line3[lineLen] = front1:sub(count), front2:sub(count), front3:sub(count) end
  459.  
  460.                     front1, front2, front3 = table.concat(line1), table.concat(line2), table.concat(line3)
  461.                     frontBuffer[i], workBuffer[i] = {front1, front2, front3}, {["max"] = 0}
  462.                 end
  463.  
  464.                 parent.setCursorPos(x, y + i - 1)
  465.                 parent.blit(front1, front2, front3)
  466.             end
  467.            
  468.             if pal then
  469.                 local counter = 1
  470.                 for i = 1, 16 do
  471.                     parent.setPaletteColour(counter, unpack(pal[counter]))
  472.                     counter = counter * 2
  473.                 end
  474.             end
  475.         end
  476.     end
  477.    
  478.     window.setVisible = function(newVis)
  479.         newVis = newVis and true or false
  480.        
  481.         if newVis and not visible then
  482.             visible = true
  483.             window.redraw()
  484.         else visible = newVis end
  485.     end
  486.    
  487.     window.getPosition = function()
  488.         return x, y
  489.     end
  490.    
  491.     window.reposition = function(newX, newY, newWidth, newHeight)
  492.         x, y = type(newX) == "number" and math.floor(newX) or x, type(newY) == "number" and math.floor(newY) or y
  493.        
  494.         if type(newWidth) == "number" then
  495.             newWidth = math.floor(newWidth)
  496.             if newWidth > cWidth then
  497.                 local line1, line2, line3 = string.rep("\128", newWidth - cWidth), string.rep(colourChar[tCol], newWidth - cWidth), string.rep(colourChar[bCol], newWidth - cWidth)
  498.                 for y = 1, cHeight do
  499.                     local bRow, fRow = backBuffer[y], frontBuffer[y]
  500.                     for x = cWidth + 1, newWidth do bRow[x] = {bCol, bCol, bCol, bCol, bCol, bCol} end
  501.                     frontBuffer[y] = {fRow[1] .. line3, fRow[2] .. line2, fRow[3] .. line3}
  502.                 end
  503.             elseif newWidth < cWidth then
  504.                 for y = 1, cHeight do
  505.                     local wRow, bRow, fRow = workBuffer[y], backBuffer[y], frontBuffer[y]
  506.                     for x = newWidth + 1, cWidth do bRow[x] = nil end
  507.                     frontBuffer[y] = {fRow[1]:sub(1, newWidth), fRow[2]:sub(1, newWidth), fRow[3]:sub(1, newWidth)}
  508.                    
  509.                     while wRow[wRow.max] and wRow[wRow.max] > newWidth do
  510.                         wRow[wRow.max] = nil
  511.                         wRow.max = table.maxn(wRow)
  512.                     end
  513.                 end
  514.             end
  515.             width, cWidth = newWidth * 2, newWidth
  516.         end
  517.        
  518.         if type(newHeight) == "number" then
  519.             newHeight = math.floor(newHeight)
  520.             if newHeight > cHeight then
  521.                 local line1, line2, line3 = string.rep("\128", cWidth), string.rep(colourChar[tCol], cWidth), string.rep(colourChar[bCol], cWidth)
  522.                 for y = cHeight + 1, newHeight do workBuffer[y], backBuffer[y], frontBuffer[y] = {["max"] = 0}, newLine(cWidth, bCol), {line1, line2, line3} end
  523.             elseif newHeight < cHeight then
  524.                 for y = newHeight + 1, cHeight do workBuffer[y], backBuffer[y], frontBuffer[y] = nil, nil, nil end
  525.             end
  526.             height, cHeight = newHeight * 3, newHeight
  527.         end
  528.        
  529.         window.redraw()
  530.     end
  531.    
  532.     window.clear()
  533.     return window
  534. end
  535.  
  536. function draw(image, x, y, terminal)
  537.     local t, tC, bC = image[1], image[2], image[3]
  538.     x, y, terminal = x or 1, y or 1, terminal or term.current()
  539.    
  540.     for i = 1, image.height do
  541.         local tI = t[i]
  542.         if type(tI) == "string" then
  543.             terminal.setCursorPos(x, y + i - 1)
  544.             terminal.blit(tI, tC[i], bC[i])
  545.         elseif type(tI) == "table" then
  546.             terminal.setCursorPos(x + tI[1], y + i - 1)
  547.             terminal.blit(tI[2], tC[i], bC[i])
  548.         end
  549.     end
  550. end
  551.  
  552. function save(image, filename)
  553.     local output = fs.open(filename, "wb")
  554.     if not output then error("Can't open "..filename.." for output.") end
  555.    
  556.     local writeByte = output.write
  557.  
  558.     local function writeInt(num)
  559.         writeByte(bit.band(num, 255))
  560.         writeByte(bit.brshift(num, 8))
  561.     end
  562.  
  563.     writeByte(66)  -- B
  564.     writeByte(76)  -- L
  565.     writeByte(84)  -- T
  566.    
  567.     local animated = image[1].delay ~= nil
  568.     writeByte(animated and 1 or 0)
  569.    
  570.     if animated then
  571.         writeInt(#image)
  572.     else
  573.         local tempImage = {image[1], image[2], image[3]}
  574.         image[1], image[2], image[3] = tempImage, nil, nil
  575.     end
  576.    
  577.     local width, height = image.width, image.height
  578.    
  579.     writeInt(width)
  580.     writeInt(height)
  581.    
  582.     for k = 1, #image do
  583.         local thisImage = image[k]
  584.        
  585.         if type(thisImage[1]) == "number" then
  586.             writeByte(3)
  587.             writeInt(thisImage[1])
  588.         else
  589.             for i = 1, height do
  590.                 if thisImage[1][i] then
  591.                     local rowType, len, thisRow = type(thisImage[1][i])
  592.  
  593.                     if rowType == "string" then
  594.                         writeByte(1)
  595.                         len = #thisImage[1][i]
  596.                         writeInt(len)
  597.                         thisRow = {thisImage[1][i]:byte(1, len)}
  598.                     elseif rowType == "table" then
  599.                         writeByte(2)
  600.                         len = #thisImage[1][i][2]
  601.                         writeInt(len)
  602.                         writeInt(thisImage[1][i][1])
  603.                         thisRow = {thisImage[1][i][2]:byte(1, len)}
  604.                     else
  605.                         error("Malformed row record #"..i.." in frame #"..k.." when attempting to save \""..filename.."\", type is "..rowType..".")
  606.                     end
  607.  
  608.                     for x = 1, len do writeByte(thisRow[x]) end
  609.  
  610.                     local txt, bg = thisImage[2][i], thisImage[3][i]
  611.                     for x = 1, len do writeByte(colourNum[txt:sub(x, x)] + colourNum[bg:sub(x, x)] * 16) end
  612.                 else writeByte(0) end
  613.             end
  614.         end
  615.        
  616.         if animated then writeInt(thisImage.delay * 20) end
  617.        
  618.         snooze()
  619.     end
  620.    
  621.     if image.pal then
  622.         writeByte(#image.pal)
  623.         for i = 0, #image.pal do for j = 1, 3 do writeByte(image.pal[i][j]) end end
  624.     end
  625.    
  626.     if not animated then
  627.         image[2], image[3] = image[1][2], image[1][3]
  628.         image[1] = image[1][1]
  629.     end
  630.    
  631.     output.close()
  632. end
  633.  
  634. function load(filename)
  635.     local input = fs.open(filename, "rb")
  636.     if not input then error("Can't open "..filename.." for input.") end
  637.    
  638.     local read = input.read
  639.    
  640.     local function readInt()
  641.         local result = read()
  642.         return result + bit.blshift(read(), 8)
  643.     end
  644.    
  645.     if string.char(read(), read(), read()) ~= "BLT" then
  646.         -- Assume legacy format.
  647.         input.close()
  648.         input = fs.open(filename, "rb")
  649.        
  650.         read = input.read
  651.        
  652.         function readInt()
  653.             local result = input.read()
  654.             return result + bit.blshift(input.read(), 8)
  655.         end
  656.  
  657.         local image = {}
  658.         image.width, image.height = readInt(), readInt()
  659.  
  660.         for i = 1, 3 do
  661.             local thisSet = {}
  662.             for y = 1, image.height do
  663.                 local thisRow = {}
  664.                 for x = 1, image.width do thisRow[x] = string.char(input.read()) end
  665.                 thisSet[y] = table.concat(thisRow)
  666.             end
  667.             image[i] = thisSet
  668.         end
  669.  
  670.         input.close()
  671.  
  672.         return image
  673.     end
  674.    
  675.     local image, animated, frames = {}, read() == 1
  676.     if animated then frames = readInt() else frames = 1 end
  677.    
  678.     local width, height = readInt(), readInt()
  679.     image.width, image.height = width, height
  680.    
  681.     for k = 1, frames do
  682.         local thisImage = {["width"] = width, ["height"] = 0}
  683.         local chr, txt, bg = {}, {}, {}
  684.        
  685.         for i = 1, height do
  686.             local lineType = read()
  687.            
  688.             if lineType == 3 then
  689.                 chr, txt, bg = readInt()
  690.                 break
  691.             elseif lineType > 0 then
  692.                 local l1, l2, len, bump = {}, {}, readInt()
  693.                 if lineType == 2 then bump = readInt() end
  694.                                
  695.                 for x = 1, len do l1[x] = read() end
  696.                 chr[i] = string.char(unpack(l1))
  697.                 if lineType == 2 then chr[i] = {bump, chr[i]} end
  698.  
  699.                 for x = 1, len do
  700.                     local thisVal = read()
  701.                     l1[x], l2[x] = colourNum[bit.band(thisVal, 15)], colourNum[bit.brshift(thisVal, 4)]
  702.                 end
  703.  
  704.                 txt[i], bg[i], thisImage.height = table.concat(l1), table.concat(l2), i
  705.             end
  706.         end
  707.        
  708.         if animated then thisImage["delay"] = readInt() / 20 end
  709.         thisImage[1], thisImage[2], thisImage[3] = chr, txt, bg
  710.         image[k] = thisImage
  711.        
  712.         snooze()
  713.     end
  714.    
  715.     local palLength = read()
  716.     if palLength and palLength > 0 then
  717.         image.pal = {}
  718.         for i = 0, palLength do image.pal[i] = {read(), read(), read()} end
  719.     end
  720.    
  721.     if not animated then
  722.         image[2], image[3] = image[1][2], image[1][3]
  723.         image[1] = image[1][1]
  724.     end
  725.    
  726.     input.close()
  727.    
  728.     return image
  729. end
  730.  
  731. if term.setPaletteColour then
  732.     function applyPalette(image, terminal)
  733.         terminal = terminal or term
  734.  
  735.         local col, pal = 1, image.pal
  736.  
  737.         for i = 0, #pal do
  738.             local thisCol = pal[i]
  739.             terminal.setPaletteColour(col, thisCol[1] / 255, thisCol[2] / 255, thisCol[3] / 255)
  740.             col = col * 2
  741.         end
  742.     end
  743. end
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top