Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -----------------------------------------
- -- Bitmap API built by Matthew Cenance --
- -----------------------------------------
- -- The section of code below is from CrazedProgrammer's RGB API. --
- -- Uses portion of the RGB API version 1.0 by CrazedProgrammer.
- -- Slightly modified to add additional functions for dithering.
- -- You can find info and documentation on these pages:
- -- http://cp.msdev.nl/computercraft/rgb-api/
- -- You may use this in your ComputerCraft programs and modify it without asking.
- -- However, you may not publish this API under your name without asking me.
- -- If you have any suggestions, bug reports or questions then please send an email to:
- local hex = {"F0F0F0", "F2B233", "E57FD8", "99B2F2", "DEDE6C", "7FCC19", "F2B2CC", "4C4C4C", "999999", "4C99B2", "B266E5", "3366CC", "7F664C", "57A64E", "CC4C4C", "191919"}
- local rgb = {}
- for i=1,16,1 do
- rgb[i] = {tonumber(hex[i]:sub(1, 2), 16), tonumber(hex[i]:sub(3, 4), 16), tonumber(hex[i]:sub(5, 6), 16)}
- end
- local rgb2 = {}
- for i=1,16,1 do
- rgb2[i] = {}
- for j=1,16,1 do
- rgb2[i][j] = {(rgb[i][1] * 34 + rgb[j][1] * 20) / 54, (rgb[i][2] * 34 + rgb[j][2] * 20) / 54, (rgb[i][3] * 34 + rgb[j][3] * 20) / 54}
- end
- end
- -- Returns a ComputerCraft color from the Color3.
- local fromRGB = function (r, g, b)
- local precision = 1e100
- local difference = 1e100
- local color = 0
- for i=1, 16, 1 do
- difference = math.sqrt((math.max(rgb[i][1], r) - math.min(rgb[i][1], r)) ^ 2 + (math.max(rgb[i][2], g) - math.min(rgb[i][2], g)) ^ 2 + (math.max(rgb[i][3], b) - math.min(rgb[i][3], b)) ^ 2)
- if difference < precision then
- precision = difference
- color = i - 1
- end
- end
- return 2 ^ color
- end
- -- Returns two ComputerCraft colors from the Color3 and the difference between the ComputerCraft colors and the actual colors.
- local fromRGBForDither = function (r, g, b)
- local precision, precision2 = 1e100, 1e100
- local difference = 1e100
- local color, color2 = 0, 0
- for i=1, 16, 1 do
- difference = math.sqrt((math.max(rgb[i][1], r) - math.min(rgb[i][1], r)) ^ 2 + (math.max(rgb[i][2], g) - math.min(rgb[i][2], g)) ^ 2 + (math.max(rgb[i][3], b) - math.min(rgb[i][3], b)) ^ 2)
- if difference < precision then
- precision2, color2 = precision, color
- precision = difference
- color = i - 1
- end
- end
- return 2 ^ color, 2 ^ color2, precision, precision2
- end
- local dithers = {
- " ",
- ".",
- "-",
- "+",
- "%",
- "*",
- "#",
- "@",
- }
- local ditherWeight = 100.25
- -- Returns values which can be used to dither.
- local ditherRGB = function (r, g, b)
- local bgcolor, textcolor, precision, precision2 = fromRGBForDither(r, g, b)
- local charId = ((precision2 - precision)/ditherWeight)*#dithers
- if charId >= #dithers + 1 then
- charId = #dithers - math.ceil((charId > 0 and charId) or 1)
- local char = dithers[charId]
- return char, bgcolor, textcolor
- end
- charId = math.floor((charId > 0 and charId) or 1)
- local char = dithers[(charId < #dithers and charId) or #dithers]
- return char, textcolor, bgcolor
- end
- -- End of the RGB API. The code below starts the Bitmap API. --
- -- Color API --
- local tHex = {
- [ colors.white ] = "0",
- [ colors.orange ] = "1",
- [ colors.magenta ] = "2",
- [ colors.lightBlue ] = "3",
- [ colors.yellow ] = "4",
- [ colors.lime ] = "5",
- [ colors.pink ] = "6",
- [ colors.gray ] = "7",
- [ colors.lightGray ] = "8",
- [ colors.cyan ] = "9",
- [ colors.purple ] = "a",
- [ colors.blue ] = "b",
- [ colors.brown ] = "c",
- [ colors.green ] = "d",
- [ colors.red ] = "e",
- [ colors.black ] = "f",
- }
- local Color3 = {}
- Color3.__index = Color3
- Color3.__tostring = function(value)
- return "Red: "..value.red.." Green:"..value.green.." Blue:"..value.blue
- end
- -- Creates a new Color3.
- function Color3.new(red, green, blue)
- if not blue then error("Expected red, green, blue", 2) end
- local self = setmetatable({red = red or 0, green = green or 0, blue = blue or 0}, Color3)
- return self
- end
- -- Returns the Color3.
- function Color3:GetValue()
- local maxValue = math.max(self.red, self.green, self.blue)
- local divideBy = 255/maxValue
- return Color3.new(self.red*divideBy, self.green*divideBy, self.blue*divideBy)
- end
- -- Returns the hue of the color.
- function Color3:GetHue()
- local red, green, blue = self.red, self.green, self.blue
- return math.atan2(math.sqrt(3)*(green - (blue)), 2*red - green - blue)
- end
- -- Returns a terminal color that is closest to the Color3 value that can be used to draw to the terminal.
- function Color3:ToComputerCraftColor()
- return fromRGB(self.red, self.green, self.blue)
- end
- -- Returns three values which can be used to dither.
- function Color3:ToComputerCraftDither()
- return ditherRGB(self.red, self.green, self.blue)
- end
- -- End of Color API --
- -- Returns the bytes in the value.
- function getValues(tab, startingKey, keys)
- startingKey = startingKey + 1
- local variables = {nil, nil, nil, nil, nil, nil, nil, nil}
- for k=startingKey, startingKey + (keys - 1) do
- variables[(k - startingKey) + 1] = tab[k]
- end
- return variables
- end
- -- Returns a number from the byte table.
- function tableWithNumbersToString(tab)
- local tab2 = {}
- for k=1, #tab do
- tab2[k] = string.char(tab[k] or 0)
- end
- return table.concat(tab2, "")
- end
- -- Returns a string from the byte table.
- function tableWithNumbersToNumber(tab)
- local number = 0
- local deg = #tab
- for key=1, #tab do
- number = number + bit32.lshift(tab[key], (key-1)*8)
- end
- return number
- end
- function getNumber(...)
- return tableWithNumbersToNumber(getValues(...))
- end
- function getString(...)
- return tableWithNumbersToString(getValues(...))
- end
- -- Bitmap API --
- -- Compression Types.
- local BI_RGB = 0 -- Uncompressed Red Green Blue format.
- local BI_RLE8 = 1 -- RLE 8-bit/pixel.
- local BI_RLE4 = 2 -- RLE 4-bit/pixel.
- local BI_BITFIELDS = 3 -- Huffman 1D.
- local BI_CMYK = 11 -- Uncompressed CYMK format.
- local Bitmap = {}
- -- Bitmap metatable.
- Bitmap.__index = Bitmap
- Bitmap.__tostring = function(self)
- if self.bitmapInfo then
- return "Bitmap object (Width: "..self.bitmapInfo.Width..", Height: "..self.bitmapInfo.Height..")"
- else
- return "Bitmap class"
- end
- end
- -- Creates a bitmap object with the selected width and height.
- function Bitmap.new( width, height, backgroundColor )
- -- Create the bitmap object.
- local self = setmetatable({}, Bitmap)
- -- Load the advanced Bitmap info.
- local bitmapInfo = {
- Identity = "BM",
- Size = 54,
- App = "LApi",
- BitMapOffset = 54,
- HeaderSize = 54,
- Width = width,
- Height = height,
- ColorPlanes = 1,
- BitsPerPixel = 24,
- CompressionType = BI_RGB,
- RawBitmapDataSize = 0,
- HorizontalResolution = 2,
- VerticalResolution = 2,
- ColorsInColorPalette = 0,
- ImportantColors = 0,
- }
- end
- -- Loads a bitmap from the Hard Disk.
- function Bitmap.load( _sPath )
- -- Create the bitmap object.
- local self = setmetatable({}, Bitmap)
- local tBytes = {}
- -- Check that the file exists.
- if fs.exists( _sPath ) then
- local file = io.open( _sPath, "rb" )
- local sLine = file:read()
- while sLine do
- tBytes[#tBytes + 1] = sLine
- sLine = file:read()
- end
- file:close()
- else
- error("There is no file located at ".._sPath..".", 2)
- end
- -- Load the advanced Bitmap info.
- local bitmapInfo = {
- Identity = getString(tBytes, 0, 2),
- Size = getNumber(tBytes, 2, 4),
- App = getString(tBytes, 6, 4),
- BitMapOffset = getNumber(tBytes, 10, 4),
- HeaderSize = getNumber(tBytes, 14, 4),
- Width = getNumber(tBytes, 18, 4),
- Height = getNumber(tBytes, 22, 4),
- ColorPlanes = getNumber(tBytes, 26, 2),
- BitsPerPixel = getNumber(tBytes, 28, 2),
- CompressionType = getNumber(tBytes, 30, 4),
- RawBitmapDataSize = getNumber(tBytes, 34, 4),
- HorizontalResolution = getNumber(tBytes, 38, 4),
- VerticalResolution = getNumber(tBytes, 42, 4),
- ColorsInColorPalette = getNumber(tBytes, 46, 4),
- ImportantColors = getNumber(tBytes, 50, 4),
- }
- self.Width = bitmapInfo.Width
- self.Height = bitmapInfo.Height
- self.bitmapInfo = bitmapInfo
- -- Do we know the identity of this file? If not then this is not a valid Bitmap file and cannot be loaded.
- if bitmapInfo.Identity ~= "BM" then
- error(_sPath.." is not a valid bitmap file.", 2)
- end
- -- Attempt to load the Bitmap data from the file if we can.
- local success, errorMessage = pcall(function()
- if bitmapInfo.CompressionType == BI_RGB then
- local BitsPerPixel = math.ceil(bitmapInfo.BitsPerPixel)
- local BytesPerPixel = BitsPerPixel/8
- local BitMapOffset = bitmapInfo.BitMapOffset
- local BitmapWidth = bitmapInfo.Width
- local BitmapHeight = bitmapInfo.Height
- local RowSize = (math.floor(( BitsPerPixel * BitmapWidth + 31) / 32) * 4)
- local RowSizeBytes = math.ceil(RowSize/8)
- local PixelArraySize = RowSize*math.ceil(BitmapHeight)
- local PixelArraySizeBytes = RowSizeBytes*math.ceil(BitmapHeight)
- local offset = 0 -- The offset used to get a pixel's color.
- local bit16 = 255/15
- local byte1, byte2
- bitmapInfo.Bitmap = {{},{},{},{}} -- The data of the bitmap.
- -- Load the bitmap data into the table.
- if BitsPerPixel == 16 then -- 16 bits per pixel.
- for x = 1, BitmapWidth do
- bitmapInfo.Bitmap[x] = {}
- for y = 1, BitmapHeight do
- offset = BitMapOffset + ( (x - 1)*BytesPerPixel ) + ( (BitmapHeight - (y - 1) - 1)*RowSize )
- byte1, byte2 = tBytes[offset+2], tBytes[offset+1]
- bitmapInfo.Bitmap[x][y] = Color3.new(math.floor((byte1%16)*bit16), math.floor(bit32.rshift(byte2, 4)*bit16), math.floor((byte2%16)*bit16))
- end
- end
- else -- 24 bits per pixel.
- for x = 1, BitmapWidth do
- bitmapInfo.Bitmap[x] = {}
- for y = 1, BitmapHeight do
- offset = BitMapOffset + ( (x - 1)*BytesPerPixel ) + ( (BitmapHeight - (y - 1) - 1)*RowSize )
- bitmapInfo.Bitmap[x][y] = Color3.new(tBytes[offset+3], tBytes[offset+2], tBytes[offset+1])
- end
- end
- end
- return true
- end
- return "Compression type "..bitmapInfo.CompressionType.." is not able to be loaded by this API."
- end)
- -- Did we succeed in loading the Bitmap data?
- if success and errorMessage == true then
- bitmapInfo.LoadedBitmap = true
- else
- bitmapInfo.LoadedBitmap = false
- bitmapInfo.ErrorMessage = errorMessage
- error(errorMessage)
- end
- -- Return the new Bitmap object.
- return self
- end
- -- Saves the bitmap to the Hard Disk as a bitmap file.
- -- For bitmap objects created using the Bitmap.load function,
- -- the Bitmap array must be loaded successfully to work correctly.
- function Bitmap:Save(_sPath)
- error("This function is not implemented.", 2)
- end
- -- Converts the bitmap to an Advanced Computer Paint file.
- -- For bitmap objects created using the Bitmap.load function,
- -- the Bitmap array must be loaded successfully to work correctly.
- function Bitmap:SaveAsPaintFile(_sPath, scale)
- if self.bitmapInfo.LoadedBitmap == true then
- local file = fs.open(_sPath, "w")
- if file then
- local scale = scale or 1
- local term = terminal or term
- local Bitmap = self.bitmapInfo.Bitmap
- for x=1, #Bitmap, math.max(1/scale, 1) do
- for y=1, #Bitmap[1], math.max(1/scale, 1) do
- local color = Bitmap[x][y]:ToComputerCraftColor()
- file.write(tHex[color])
- end
- file.write("\n")
- end
- file.close()
- end
- else
- error("LoadedBitmap is not equal to true, meaning that the bitmap data was not loaded. This function can only be used if the bitmap data is available.", 2)
- end
- end
- -- Works with the Minecraft mod called ComputerCraft only.
- -- Draws the bitmap to the terminal.
- -- For bitmap objects created using the Bitmap.load function,
- -- the Bitmap array must be loaded successfully to work correctly.
- -- The dither argument determines whether the drawn picture will be dithered or not.
- function Bitmap:DrawToTerm(xPos, yPos, scale, dithered, terminal)
- if not term then
- error("This function is only supported on ComputerCraft.", 2)
- end
- if self.bitmapInfo.LoadedBitmap == true then
- -- Draw the bitmap to the screen.
- local scale = scale or 1
- local term = terminal or term
- local Bitmap = self.bitmapInfo.Bitmap
- local x2, y2 = 0, 0
- local text, textcolor, bgcolor
- if dithered then
- -- Dithered drawing.
- for x=1, #Bitmap, (math.max(1/scale, 1)) do
- x2 = x2 + 1
- y2 = 0
- for y=1, #Bitmap[1], (math.max(1/scale, 1)) do
- y2 = y2 + 1
- text, textcolor, bgcolor = Bitmap[math.ceil(x)][math.ceil(y)]:ToComputerCraftDither()
- term.setCursorPos(xPos + math.floor((x2-1)), yPos + math.floor((y2-1)))
- term.setBackgroundColor(bgcolor or 1)
- term.setTextColor(textcolor or colors.black)
- term.write(text or " ")
- end
- end
- else
- for x=1, #Bitmap, (math.max(1/scale, 1)) do
- x2 = x2 + 1
- y2 = 0
- for y=1, #Bitmap[1], (math.max(1/scale, 1)) do
- y2 = y2 + 1
- bgcolor = Bitmap[math.ceil(x)][math.ceil(y)]:ToComputerCraftColor()
- term.setCursorPos(xPos + math.floor((x2-1)), yPos + math.floor((y2-1)))
- term.setBackgroundColor(bgcolor or 1)
- term.write(" ")
- end
- end
- end
- else
- error("LoadedBitmap is not equal to true, meaning that the bitmap data was not loaded. This function can only be used if the bitmap data is available.", 2)
- end
- end
- -- Retrieves the pixel color at a specified location on the bitmap surface.
- -- For bitmap objects created using the Bitmap.load function,
- -- the Bitmap array must be loaded successfully to work correctly.
- function Bitmap:GetPixel(x, y)
- if self.bitmapInfo.LoadedBitmap == true then
- local Width, Height = bitmapInfo.Width, bitmapInfo.Height
- local X = ((x < Width and x) or Width)
- local Y = ((y < Height and y) or Height)
- return self.bitmapInfo.Bitmap[(X > 1 and X) or 1][(Y > 1 and Y) or 1]
- end
- return false
- end
- -- Sets the color for a specified pixel.
- -- For bitmap objects created using the Bitmap.load function,
- -- the Bitmap array must be loaded successfully to work correctly.
- function Bitmap:SetPixel(color, x, y)
- if self.bitmapInfo.LoadedBitmap == true then
- local Width, Height = bitmapInfo.Width, bitmapInfo.Height
- local X = ((x < Width and x) or Width)
- local Y = ((y < Height and y) or Height)
- self.bitmapInfo.Bitmap[(X > 1 and X) or 1][(Y > 1 and Y) or 1] = color
- end
- return false
- end
- _G.Color3 = Color3
- _G.Bitmap = Bitmap
- -- Command Line Functionality for ComputerCraft. --
- local tArgs = {...}
- if #tArgs > 0 then
- local myBitmap
- if tArgs[1] == "convert" then
- if tArgs[3] then
- myBitmap = Bitmap.load(tArgs[2])
- myBitmap:SaveAsPaintFile(tArgs[3])
- else
- print("Usage: bmpreader convert <path> <copy to>")
- end
- elseif tArgs[1] == "view" then
- if tArgs[2] then
- local width, height = term.getSize()
- myBitmap = Bitmap.load(tArgs[2])
- term.clear()
- local scale = math.min(width/myBitmap.Width, height/myBitmap.Height, 1)
- local x, y = width*.5 + 1 - (scale*width*.5), height*.5 + 1 - (scale*height*.5)
- myBitmap:DrawToTerm(1, 1, scale, true)
- os.pullEvent("key")
- term.setCursorPos(1,1)
- term.setTextColor(colors.white)
- term.setBackgroundColor(colors.black)
- term.clear()
- else
- print("Usage: bmpreader view <path>")
- end
- else
- print("This is an API, but you can use it directly on the command line using these functions:")
- print("Convert a bitmap image to the ComputerCraft paint format: bmpreader convert <path> <copy to>")
- print("View a bitmap image: bmpreader view <path>")
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement