Advertisement
nonogamer9

CC-BMP : An BMP Image Viewer For ComputerCraft

Jan 24th, 2025 (edited)
65
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 9.63 KB | Software | 0 0
  1. local function readBMP(filename)
  2.   local file = fs.open(filename, "rb")
  3.   if not file then
  4.     return nil, "Failed to open file"
  5.   end
  6.  
  7.   local header = file.read(54)
  8.   local width = string.byte(header, 19) + string.byte(header, 20) * 256
  9.   local height = string.byte(header, 23) + string.byte(header, 24) * 256
  10.  
  11.   local pixels = {}
  12.   for y = height, 1, -1 do
  13.     pixels[y] = {}
  14.     for x = 1, width do
  15.       local b = file.read(1)
  16.       local g = file.read(1)
  17.       local r = file.read(1)
  18.       pixels[y][x] = {r = string.byte(r), g = string.byte(g), b = string.byte(b)}
  19.     end
  20.     if y % 10 == 0 then
  21.       os.queueEvent("yield")
  22.       os.pullEvent("yield")
  23.     end
  24.   end
  25.  
  26.   file.close()
  27.   return {width = width, height = height, pixels = pixels}
  28. end
  29.  
  30. local function resizeImage(image, newWidth, newHeight)
  31.   local resized = {width = newWidth, height = newHeight, pixels = {}}
  32.   for y = 1, newHeight do
  33.     resized.pixels[y] = {}
  34.     for x = 1, newWidth do
  35.       local srcX = math.floor(x * image.width / newWidth)
  36.       local srcY = math.floor(y * image.height / newHeight)
  37.       resized.pixels[y][x] = image.pixels[srcY][srcX]
  38.     end
  39.     if y % 10 == 0 then
  40.       os.queueEvent("yield")
  41.       os.pullEvent("yield")
  42.     end
  43.   end
  44.   return resized
  45. end
  46.  
  47. local function rgbToComputerCraftColor(r, g, b)
  48.   local colors = {
  49.     [0x1] = {240, 240, 240}, [0x2] = {242, 178, 51}, [0x4] = {229, 127, 216},
  50.     [0x8] = {153, 178, 242}, [0x10] = {222, 222, 108}, [0x20] = {127, 204, 25},
  51.     [0x40] = {242, 178, 204}, [0x80] = {76, 76, 76}, [0x100] = {153, 153, 153},
  52.     [0x200] = {76, 153, 178}, [0x400] = {178, 102, 229}, [0x800] = {51, 102, 204},
  53.     [0x1000] = {127, 102, 76}, [0x2000] = {87, 166, 78}, [0x4000] = {204, 76, 76},
  54.     [0x8000] = {17, 17, 17}
  55.   }
  56.  
  57.   local closestColor = 0x1
  58.   local minDifference = math.huge
  59.  
  60.   for code, rgb in pairs(colors) do
  61.     local difference = math.sqrt((r - rgb[1])^2 + (g - rgb[2])^2 + (b - rgb[3])^2)
  62.     if difference < minDifference then
  63.       minDifference = difference
  64.       closestColor = code
  65.     end
  66.   end
  67.  
  68.   return closestColor
  69. end
  70.  
  71. local function displayBMP(image, output)
  72.   local width, height = output.getSize()
  73.   local resizedImage = resizeImage(image, width, height)
  74.  
  75.   for y = 1, height do
  76.     for x = 1, width do
  77.       local pixel = resizedImage.pixels[y][x]
  78.       local color = rgbToComputerCraftColor(pixel.r, pixel.g, pixel.b)
  79.       output.setCursorPos(x, y)
  80.       output.setBackgroundColor(color)
  81.       output.write(" ")
  82.     end
  83.     if y % 5 == 0 then
  84.       os.queueEvent("yield")
  85.       os.pullEvent("yield")
  86.     end
  87.   end
  88. end
  89.  
  90. local function displayBMPMode2(image, output)
  91.   local width, height = output.getSize()
  92.   local resizedImage = resizeImage(image, width * 2, height * 2)
  93.  
  94.   local charSet = {" ", "\131", "\132", "\133", "\135", "\136", "\137", "\138", "\139", "\140"}
  95.  
  96.   local colors = {
  97.     [0x1] = {240, 240, 240}, [0x2] = {242, 178, 51}, [0x4] = {229, 127, 216},
  98.     [0x8] = {153, 178, 242}, [0x10] = {222, 222, 108}, [0x20] = {127, 204, 25},
  99.     [0x40] = {242, 178, 204}, [0x80] = {76, 76, 76}, [0x100] = {153, 153, 153},
  100.     [0x200] = {76, 153, 178}, [0x400] = {178, 102, 229}, [0x800] = {51, 102, 204},
  101.     [0x1000] = {127, 102, 76}, [0x2000] = {87, 166, 78}, [0x4000] = {204, 76, 76},
  102.     [0x8000] = {17, 17, 17}
  103.   }
  104.  
  105.   local function getNearestColor(r, g, b)
  106.     local closestColor = 0x1
  107.     local minDifference = math.huge
  108.     for code, rgb in pairs(colors) do
  109.       local difference = math.abs(r - rgb[1]) + math.abs(g - rgb[2]) + math.abs(b - rgb[3])
  110.       if difference < minDifference then
  111.         minDifference = difference
  112.         closestColor = code
  113.       end
  114.     end
  115.     return closestColor
  116.   end
  117.  
  118.   local function blendColors(c1, c2)
  119.     return {
  120.       r = (c1.r + c2.r) / 2,
  121.       g = (c1.g + c2.g) / 2,
  122.       b = (c1.b + c2.b) / 2
  123.     }
  124.   end
  125.  
  126.   for y = 1, height do
  127.     for x = 1, width do
  128.       local tl = resizedImage.pixels[y*2-1][x*2-1]
  129.       local tr = resizedImage.pixels[y*2-1][x*2]
  130.       local bl = resizedImage.pixels[y*2][x*2-1]
  131.       local br = resizedImage.pixels[y*2][x*2]
  132.  
  133.       local avgColor = blendColors(blendColors(tl, tr), blendColors(bl, br))
  134.       local intensity = (avgColor.r + avgColor.g + avgColor.b) / 3
  135.       local pattern = math.floor(intensity / 28.4) + 1
  136.  
  137.       local bgColor = getNearestColor(tl.r, tl.g, tl.b)
  138.       local fgColor = getNearestColor(br.r, br.g, br.b)
  139.  
  140.       if bgColor == fgColor then
  141.         if pattern > 5 then
  142.           fgColor = getNearestColor(math.min(br.r + 30, 255), math.min(br.g + 30, 255), math.min(br.b + 30, 255))
  143.         else
  144.           bgColor = getNearestColor(math.max(tl.r - 30, 0), math.max(tl.g - 30, 0), math.max(tl.b - 30, 0))
  145.         end
  146.       end
  147.  
  148.       output.setCursorPos(x, y)
  149.       output.setBackgroundColor(bgColor)
  150.       output.setTextColor(fgColor)
  151.       output.write(charSet[pattern])
  152.     end
  153.     if y % 5 == 0 then
  154.       os.queueEvent("yield")
  155.       os.pullEvent("yield")
  156.     end
  157.   end
  158. end
  159.  
  160. local function displayBMPCraftOSPC(image, output, colorMode)
  161.   local screenWidth, screenHeight = output.getSize()
  162.   local imageWidth, imageHeight = image.width, image.height
  163.  
  164.   local originalPalette = {}
  165.   for i = 0, 255 do
  166.     originalPalette[i] = {term.getPaletteColor(i)}
  167.   end
  168.   local originalGraphicsMode = term.getGraphicsMode()
  169.   local originalBackgroundColor = term.getBackgroundColor()
  170.   local originalTextColor = term.getTextColor()
  171.  
  172.   term.setGraphicsMode(colorMode)
  173.  
  174.   if colorMode == 2 then
  175.     local cubeSize = 5  -- Number of steps for each color component (R, G, B)
  176.     for i = 16, 255 do
  177.       local index = i - 16
  178.       local rIndex = math.floor(index / (cubeSize * cubeSize))
  179.       local gIndex = math.floor((index % (cubeSize * cubeSize)) / cubeSize)
  180.       local bIndex = index % cubeSize
  181.  
  182.       local r = math.floor(rIndex * 255 / (cubeSize - 1))
  183.       local g = math.floor(gIndex * 255 / (cubeSize - 1))
  184.       local b = math.floor(bIndex * 255 / (cubeSize - 1))
  185.       term.setPaletteColor(i, r / 255, g / 255, b / 255)
  186.     end
  187.   end
  188.  
  189.   local function adjustBrightness(value)
  190.     return math.min(255, math.max(0, math.floor(value * 1.2)))  -- Increase brightness by 20%
  191.   end
  192.  
  193.   local function mapColor(r, g, b)
  194.     r, g, b = adjustBrightness(r), adjustBrightness(g), adjustBrightness(b)
  195.     if colorMode == 2 then
  196.       -- Map to the custom color cube palette
  197.       local cubeSize = 5
  198.       local rIndex = math.floor(r / (255 / (cubeSize - 1)))
  199.       local gIndex = math.floor(g / (255 / (cubeSize - 1)))
  200.       local bIndex = math.floor(b / (255 / (cubeSize - 1)))
  201.  
  202.       return 16 + rIndex * cubeSize * cubeSize + gIndex * cubeSize + bIndex
  203.     else
  204.       return rgbToComputerCraftColor(r, g, b)
  205.     end
  206.   end
  207.  
  208.   for y = 0, screenHeight * 9 - 1 do
  209.     for x = 0, screenWidth * 6 - 1 do
  210.       local sourceX = math.floor(x * imageWidth / (screenWidth * 6))
  211.       local sourceY = math.floor(y * imageHeight / (screenHeight * 9))
  212.  
  213.       local pixel = image.pixels[sourceY + 1][sourceX + 1]
  214.       local color = mapColor(pixel.r, pixel.g, pixel.b)
  215.  
  216.       output.setPixel(x, y, color)
  217.     end
  218.     if y % 10 == 0 then
  219.       os.queueEvent("yield")
  220.       os.pullEvent("yield")
  221.     end
  222.   end
  223.  
  224.   print("Image displayed. Press any key to exit.")
  225.   os.pullEvent("key")
  226.  
  227.   term.setGraphicsMode(originalGraphicsMode)
  228.   for i = 0, 255 do
  229.     term.setPaletteColor(i, table.unpack(originalPalette[i]))
  230.   end
  231.  
  232.   term.setBackgroundColor(originalBackgroundColor)
  233.   term.setTextColor(originalTextColor)
  234.  
  235.   term.setBackgroundColor(colors.black)
  236.   term.setTextColor(colors.white)
  237.   term.clear()
  238.   term.setCursorPos(1, 1)
  239. end
  240.  
  241. local function downloadBMP(url, filename)
  242.   if not http then
  243.     error("HTTP API is not enabled. Enable it in ComputerCraft.cfg")
  244.   end
  245.  
  246.   print("Downloading " .. url .. "...")
  247.   local response = http.get(url, nil, true)
  248.   if not response then
  249.     error("Failed to download file")
  250.   end
  251.  
  252.   local file = fs.open(filename, "wb")
  253.   local content = response.readAll()
  254.   for i = 1, #content, 8192 do
  255.     file.write(content:sub(i, i + 8191))
  256.     os.queueEvent("yield")
  257.     os.pullEvent("yield")
  258.   end
  259.   file.close()
  260.   response.close()
  261.   print("Downloaded as " .. filename)
  262. end
  263.  
  264. print("CC-BMP : An Enhanced BMP Image Viewer For ComputerCraft Coded By nonogamer9!")
  265. print("Enter a local filename or URL:")
  266. local input = read()
  267.  
  268. local filename = input
  269. if string.sub(input, 1, 4) == "http" then
  270.   filename = "downloaded.bmp"
  271.   downloadBMP(input, filename)
  272. end
  273.  
  274. local image, err = readBMP(filename)
  275. if not image then
  276.   print("Error reading BMP file:", err)
  277.   return
  278. end
  279.  
  280. print("Select display mode:")
  281. print("1. Basic ComputerCraft screen")
  282. print("2. Mode 2 (Just a better resolution.)")
  283. print("3. CraftOS-PC 16-color Graphics Mode")
  284. print("4. CraftOS-PC 256-color Graphics Mode")
  285. print("5. Monitor")
  286.  
  287. local mode = tonumber(read())
  288.  
  289. local output
  290. if mode == 5 then
  291.   output = peripheral.find("monitor")
  292.   if not output then
  293.     print("No monitor found")
  294.     return
  295.   end
  296. else
  297.   output = term
  298. end
  299.  
  300. if mode == 1 then
  301.   displayBMP(image, output)
  302. elseif mode == 2 then
  303.   displayBMPMode2(image, output)
  304. elseif mode == 3 then
  305.   if term.setGraphicsMode then
  306.     displayBMPCraftOSPC(image, output, 1)  -- 16-color mode
  307.   else
  308.     print("CraftOS-PC Graphics Mode not supported")
  309.   end
  310. elseif mode == 4 then
  311.   if term.setGraphicsMode then
  312.     displayBMPCraftOSPC(image, output, 2)  -- 256-color mode
  313.   else
  314.     print("CraftOS-PC Graphics Mode not supported")
  315.   end
  316. elseif mode == 5 then
  317.   displayBMP(image, output)
  318. else
  319.   print("Invalid mode selected")
  320. end
  321.  
  322. print("Image displayed")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement