Advertisement
SquidDev

ImagesAPI

Jun 4th, 2014
1,046
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 10.91 KB | None | 0 0
  1. local function _W(f) local e=setmetatable({}, {__index = _ENV or getfenv()}) if setfenv then setfenv(f, e) end return f(e) or e end
  2. local Class=_W(function(_ENV, ...)
  3. local Object = { __name = "Object" }
  4.  
  5. function Object:init(...) end
  6.  
  7. function Object:super(class)
  8.     local obj = self
  9.     return setmetatable({ }, { __index = function(t, k)
  10.         return function(...)
  11.             while class do
  12.                 local method = class[k]
  13.                 if method then
  14.                     return method(obj, ...)
  15.                 end
  16.                 class = class.__super
  17.             end
  18.         end
  19.     end })
  20. end
  21. function Object:createTable()
  22.     local obj = self
  23.     local class = obj:getClass()
  24.     return setmetatable({ }, { __index = function(t, k)
  25.         return function(...)
  26.             local method = class[k]
  27.             return method(obj, ...)
  28.         end
  29.     end })
  30. end
  31.  
  32. function Object:getSuper()
  33.     return rawget(self:getClass(), "__super")
  34. end
  35.  
  36. function Object:getClass()
  37.     return rawget(self, "__class")
  38. end
  39.  
  40. function Object:instanceof(targetType)
  41.     local objectType = self:getClass()
  42.     while objectType do
  43.         if targetType == objectType then
  44.             return true
  45.         end
  46.         objectType = objectType:getSuper()
  47.     end
  48.     return false
  49. end
  50.  
  51. function Object:toString()
  52.     return self:getClass():getName().." ("..tostring(self):sub(8, -1)..")"
  53. end
  54.  
  55. function Object:extend(obj)
  56.     self.__index = self -- This is a metatable
  57.  
  58.     local obj = obj or {}
  59.     obj.__super = self
  60.     local obj = setmetatable(obj, self)
  61.     return obj
  62. end
  63.  
  64. local Class = Object:extend({__name = "Class"})
  65. function Class:new(...)
  66.     local instance = { __class = self }
  67.     self:extend(instance)
  68.     instance:init(...)
  69.     return instance
  70. end
  71.  
  72. function Class:getName(name)
  73.     return self.__name
  74. end
  75.  
  76. function Class:subClass(name)
  77.     local instance = { }
  78.     instance.__name = name
  79.  
  80.     return self:extend(instance)
  81. end
  82.  
  83. return Class
  84. end)
  85. Colors=_W(function(_ENV, ...)
  86. local pairs = pairs
  87. local colors = colors or setmetatable({}, {__index = function() return 1 end})
  88. local termSets = {
  89.     [colors.white] = {240, 240, 240},
  90.     [colors.orange] = {242, 178, 51},
  91.     [colors.magenta] = {229, 127, 216},
  92.     [colors.lightBlue] = {153, 178, 242},
  93.     [colors.yellow] = {222, 222, 108},
  94.     [colors.lime] = {127, 204, 25},
  95.     [colors.pink] = {242, 178, 204},
  96.     [colors.gray] = {76, 76, 76},
  97.     [colors.lightGray] = {153, 153, 153},
  98.     [colors.cyan] = {76, 153, 178},
  99.     [colors.purple] = {178, 102, 229},
  100.     [colors.blue] = {37, 49, 146},
  101.     [colors.brown] = {127, 102, 76},
  102.     [colors.green] = {5, 122, 100},
  103.     [colors.red] = {204, 76, 76},
  104.     [colors.black] = {0, 0, 0},
  105. }
  106.  
  107. local strSets = {
  108.     ["0"] = {240, 240, 240},
  109.     ["1"] = {242, 178, 51},
  110.     ["2"] = {229, 127, 216},
  111.     ["3"] = {153, 178, 242},
  112.     ["4"] = {222, 222, 108},
  113.     ["5"] = {127, 204, 25},
  114.     ["6"] = {242, 178, 204},
  115.     ["7"] = {76, 76, 76},
  116.     ["8"] = {153, 153, 153},
  117.     ["9"] = {76, 153, 178},
  118.     ["a"] = {178, 102, 229},
  119.     ["b"] = {37, 49, 146},
  120.     ["c"] = {127, 102, 76},
  121.     ["d"] = {5, 122, 100},
  122.     ["e"] = {204, 76, 76},
  123.     ["f"] = {0, 0, 0},
  124. }
  125.  
  126. local function findClosestColor(colors, r, g, b)
  127.     local smallestDifference = nil
  128.     local smallestColor = nil
  129.     for id, rgb in pairs(colors) do
  130.         local diff = (r - rgb[1])^2 + (g - rgb[2])^2 + (b - rgb[3])^2
  131.  
  132.         if not smallestDifference or diff < smallestDifference then
  133.             smallestColor = id
  134.             smallestDifference = diff
  135.         end
  136.     end
  137.  
  138.     return smallestColor
  139. end
  140.  
  141. local closestCache = {}
  142.  
  143. --- Find the closest colour using a cache to store the results.
  144. -- This is because findClosestColor is not the fastest code.
  145. -- However, this could grow to be 256 ^ 3 bytes large (over 14MiB) which isn't great.
  146. local function findClosestCached(colors, r, g, b)
  147.     local cache = closestCache[colors]
  148.     if not cache then
  149.         cache = {}
  150.         closestCache[colors] = cache
  151.     end
  152.  
  153.     local index = r * 65536 + g * 256 + b
  154.     local result = cache[index]
  155.     if not result then
  156.         result = findClosestColor(colors, r, g, b)
  157.         cache[index] = result
  158.     end
  159.  
  160.     return result
  161. end
  162.  
  163. return {
  164.     findClosestColor = findClosestColor,
  165.     findClosestCached = findClosestCached,
  166.     termSets = termSets,
  167.     strSets = strSets,
  168. }
  169. end)
  170. BitmapPixels=_W(function(_ENV, ...)
  171. local BitmapPixels = Class:subClass("BitmapPixels")
  172.  
  173. function BitmapPixels:init(parser)
  174.     self.parser = parser
  175.     self.pixels = parser.pixels
  176.     self.file = parser.file
  177.  
  178.     self.width = parser.width
  179.     self.height = parser.height
  180. end
  181.  
  182. function BitmapPixels:parse()
  183.     local width = self.width
  184.     for Y = self.height, 1, -1 do
  185.         local row = {}
  186.         self.pixels[Y] = row
  187.  
  188.         local startOffset = self.file.offset
  189.         for X = 1, width, 1 do
  190.             row[X] = self:parsePixel()
  191.         end
  192.  
  193.         self:finaliseRow()
  194.  
  195.         local pixelsRemaning = (self.file.offset - startOffset) % 4
  196.         if pixelsRemaning > 0 then
  197.             self.file:discardBytes(4 - pixelsRemaning)
  198.         end
  199.     end
  200.  
  201.     self.file:close()
  202. end
  203.  
  204. function BitmapPixels:parsePixel() return 1 end
  205. function BitmapPixels:finaliseRow() end
  206.  
  207. return BitmapPixels
  208. end)
  209. BinaryFile=_W(function(_ENV, ...)
  210. local BinaryFile = Class:subClass("BinaryFile")
  211.  
  212. function BinaryFile:init(path)
  213.     self.path = path
  214.     self.offset = 0
  215.  
  216.     self.file = fs.open(path, "rb")
  217. end
  218.  
  219. function BinaryFile:close()
  220.     self.file.close()
  221.     self.file = nil
  222. end
  223.  
  224. function BinaryFile:readByte()
  225.     self.offset = self.offset + 1
  226.     return self.file.read()
  227. end
  228. function BinaryFile:readBytes(length)
  229.     local bytes = 0
  230.     for i = 0, length - 1, 1 do
  231.         bytes = bytes + (self:readByte() * (256 ^ i))
  232.     end
  233.  
  234.     return bytes
  235. end
  236. function BinaryFile:discardBytes(length)
  237.     for i = 1, length, 1 do
  238.         self:readByte()
  239.     end
  240. end
  241.  
  242. return BinaryFile
  243. end)
  244. BitmapDepths=_W(function(_ENV, ...)
  245. local black, white = {0, 0, 0}, {255, 255, 255}
  246.  
  247. --Monochrome bitmap
  248.  
  249. local BitmapMono = BitmapPixels:subClass("BitmapMono")
  250.  
  251. function BitmapMono:init(parser)
  252.     self:super(BitmapPixels).init(parser)
  253.  
  254.     self.byte = nil
  255.     self.bitPosition = 0
  256.  
  257.     self.file:discardBytes(parser.Starts - self.file.Offset)
  258. end
  259.  
  260. function BitmapMono:parsePixel()
  261.  
  262.     if self.byte == nil then
  263.         local byte = self.file:readByte()
  264.  
  265.         local byteT = {}
  266.         local counter = 1
  267.         while (counter <= 8) do
  268.             local last = byte % 2
  269.             if(last == 1) then
  270.                 byteT[counter] = black
  271.             else
  272.                 byteT[counter] = white
  273.             end
  274.             byte = (byte-last)/2
  275.             counter = counter + 1
  276.         end
  277.  
  278.         self.byte = byteT
  279.  
  280.         self.bitPosition = 9
  281.     end
  282.  
  283.     self.bitPosition = self.bitPosition - 1
  284.     return self.byte[self.bitPosition]
  285. end
  286.  
  287. function BitmapMono:finaliseRow()
  288.     self.byte = nil
  289.     self.bitPosition = 0
  290. end
  291.  
  292. --4 Bit Bitmap
  293. local BitmapFour = BitmapPixels:subClass("Bitmap4")
  294.  
  295. function BitmapFour:init(parser)
  296.     self:super(BitmapPixels).init(parser)
  297.  
  298.     self.colors = {}
  299.     for I = 0, 15, 1 do
  300.         local b = self.file:readByte()
  301.         local g = self.file:readByte()
  302.         local r = self.file:readByte()
  303.  
  304.         self.file:readByte() -- Should be 0
  305.  
  306.         self.colors[I] = {r, g, b}
  307.     end
  308.  
  309.     self.byte = nil
  310. end
  311.  
  312. function BitmapFour:parsePixel()
  313.     local thisColour = nil
  314.  
  315.     if self.byte == nil then
  316.         local byte = self.file:readByte()
  317.  
  318.         thisColour = math.floor(byte / 16)
  319.         self.byte = byte % 16
  320.     else
  321.         thisColour = self.byte
  322.         self.byte = nil
  323.     end
  324.  
  325.     return self.colors[thisColour]
  326. end
  327.  
  328. function BitmapFour:finaliseRow()
  329.     self.byte = nil
  330. end
  331.  
  332. --Eight bit bitmap
  333. local BitmapEight = BitmapPixels:subClass("Bitmap8")
  334.  
  335. function BitmapEight:init(parser)
  336.     self:super(BitmapPixels).init(parser)
  337.  
  338.     self.colors = {}
  339.     for I = 0, 255, 1 do
  340.         local b = self.file:readByte()
  341.         local g = self.file:readByte()
  342.         local r = self.file:readByte()
  343.  
  344.         self.file:readByte() -- Should be 0
  345.  
  346.         self.colors[I] = {r, g, b}
  347.     end
  348. end
  349.  
  350. function BitmapEight:parsePixel()
  351.     return self.colors[self.file:readByte()]
  352. end
  353.  
  354. --24 bit bitmap
  355. local BitmapTwentyFour = BitmapPixels:subClass("Bitmap24")
  356.  
  357. function BitmapTwentyFour:init(parser)
  358.     self:super(BitmapPixels).init(parser)
  359.  
  360.     self.file:discardBytes(parser.starts - self.file.offset)
  361. end
  362.  
  363. function BitmapTwentyFour:parsePixel()
  364.     local b = self.file:readByte()
  365.     local g = self.file:readByte()
  366.     local r = self.file:readByte()
  367.  
  368.     return {r, g, b}
  369. end
  370.  
  371. return {
  372.     [1]  = BitmapMono,
  373.     [4]  = BitmapFour,
  374.     [8]  = BitmapEight,
  375.     [24] = BitmapTwentyFour,
  376. }
  377. end)
  378. BitmapParser=_W(function(_ENV, ...)
  379. local floor, log, sub, pairs = math.floor, math.log, string.sub, pairs
  380.  
  381. local BitmapParser = Class:subClass("BitmapParser")
  382.  
  383. function BitmapParser:init(file)
  384.     self.file = file
  385.  
  386.     --==================================
  387.     --Bitmap file header
  388.     --==================================
  389.     self.headerField = string.char(file:readByte()) .. string.char(file:readByte())
  390.     self.size = file:readBytes(4)
  391.  
  392.     --Useless bytes (not useless but kinda are)
  393.     file:discardBytes(4)
  394.     self.starts = file:readBytes(4)
  395.     --==================================
  396.     -- BITMAPINFOHEADER (DBI Header)
  397.     --==================================
  398.     self.headerSize = file:readBytes(4)
  399.     self.width = file:readBytes(4)
  400.     self.height = file:readBytes(4)
  401.  
  402.     self.colourPlanes = file:readBytes(2)
  403.  
  404.     self.colourDepth = file:readBytes(2)
  405.  
  406.     self.compression = file:readBytes(4)
  407.  
  408.     --More junky stuff
  409.     file:discardBytes(4) --the image size. This is the size of the raw bitmap data; a dummy 0 can be given for BI_RGB bitmaps.
  410.     file:discardBytes(4) --the horizontal resolution of the image. (pixel per meter, signed integer)
  411.     file:discardBytes(4) --the vertical resolution of the image. (pixel per meter, signed integer)
  412.     file:discardBytes(4) --the number of colors in the color palette, or 0 to default to 2^n
  413.     file:discardBytes(4) --the number of important colors used, or 0 when every color is important; generally ignored
  414.  
  415.     --Should have an offset of 55
  416.  
  417.     self.pixels = {}
  418.  
  419.     local pixelParser = BitmapDepths[self.colourDepth]
  420.     if not pixelParser then
  421.         error("Can't find a parser for depth "..tostring(self.colourDepth))
  422.     end
  423.  
  424.     pixelParser:new(self):parse()
  425. end
  426.  
  427. local closest, strSets = Colors.findClosestColor, Colors.strSets
  428. function BitmapParser:save(handle, save)
  429.     if type(handle) == "string" then
  430.         handle = fs.open(handle, "w")
  431.         save = true
  432.     end
  433.  
  434.     for y, values in pairs(self.pixels) do
  435.         local Line = ""
  436.         for x, col in pairs(values) do
  437.             line = line .. findClosestColor(strSets, unpack(col))
  438.         end
  439.  
  440.         handle.writeLine(Line)
  441.     end
  442.  
  443.     if save then
  444.         handle.close()
  445.     end
  446. end
  447.  
  448. return BitmapParser
  449. end)
  450. ImageHelpers=_W(function(_ENV, ...)
  451. local pairs, unpack = pairs, unpack
  452.  
  453. function parseFile(path)
  454.     local BinFile = BinaryFile:new(path)
  455.     return BitmapParser:new(BinFile)
  456. end
  457.  
  458.  
  459. local closest, termSets = Colors.findClosestColor, Colors.termSets
  460. function createTermPixel(redirect)
  461.     redirect = redirect or term
  462.     return function(x, y, color)
  463.         redirect.setCursorPos(x, y)
  464.         redirect.setBackgroundColour(closest(termSets, unpack(color)))
  465.         redirect.write(" ")
  466.     end
  467. end
  468.  
  469. termPixel = createTermPixel()
  470.  
  471. function drawImage(parser, pixel)
  472.     pixel = pixel or termPixel
  473.     for y, row in pairs(parser.pixels) do
  474.         -- print(y)
  475.         for x, color in pairs(row) do
  476.             -- print(string.format("  %s, {%s, %s, %s}", x, color[1], color[2], color[3]))
  477.             pixel(x, y, color)
  478.         end
  479.     end
  480. end
  481. end)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement