Advertisement
nonogamer9

OC-BMP : An BMP Image Viewer For OpenComputers

Jan 24th, 2025 (edited)
124
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 30.07 KB | Software | 0 0
  1. local component = require("component")
  2. local event = require("event")
  3. local term = require("term")
  4. local gpu = component.gpu
  5. local internet = require("internet")
  6. local computer = require("computer")
  7. local unicode = require("unicode")
  8. local bit32 = require("bit32")
  9.  
  10. local function stringUnpack(format, str, pos)
  11.   local function unpackInt(str, pos, size, signed)
  12.     local val = 0
  13.     for i = 0, size - 1 do
  14.       val = val + str:byte(pos + i) * (256 ^ i)
  15.     end
  16.     if signed and val >= 2^(size*8-1) then
  17.       val = val - 2^(size*8)
  18.     end
  19.     return val, pos + size
  20.   end
  21.  
  22.   if format == "<I4" then
  23.     return unpackInt(str, pos, 4, false)
  24.   elseif format == "<I2" then
  25.     return unpackInt(str, pos, 2, false)
  26.   elseif format == "BBB" then
  27.     return str:byte(pos), str:byte(pos+1), str:byte(pos+2), pos+3
  28.   else
  29.     error("Unsupported format: " .. format)
  30.   end
  31. end
  32.  
  33. local function yieldIfNeeded()
  34.   if computer.uptime() % 0.1 < 0.01 then
  35.     os.sleep(0)
  36.   end
  37. end
  38.  
  39. local function downloadImage(url)
  40.   local maxAttempts = 3
  41.   local attempt = 0
  42.   local data = ""
  43.  
  44.   while attempt < maxAttempts do
  45.     attempt = attempt + 1
  46.     local response = internet.request(url)
  47.    
  48.     if response then
  49.       for chunk in response do
  50.         data = data .. chunk
  51.         yieldIfNeeded()
  52.       end
  53.      
  54.       if data ~= "" then
  55.         return data
  56.       end
  57.     end
  58.    
  59.     print("Attempt " .. attempt .. " failed. Retrying...")
  60.     os.sleep(2)
  61.   end
  62.  
  63.   error("Failed to download image after " .. maxAttempts .. " attempts")
  64. end
  65.  
  66. -- Mode 1 specific functions
  67. local function decodeBMPMode1(imageData)
  68.   local width = stringUnpack("<I4", imageData, 19)
  69.   local height = stringUnpack("<I4", imageData, 23)
  70.   local bitsPerPixel = stringUnpack("<I2", imageData, 29)
  71.   local compression = stringUnpack("<I4", imageData, 31)
  72.   local dataOffset = stringUnpack("<I4", imageData, 11)
  73.  
  74.   if bitsPerPixel ~= 24 or compression ~= 0 then
  75.     error("Unsupported BMP format. Only 24-bit uncompressed BMPs are supported.")
  76.   end
  77.  
  78.   local decodedImage = {}
  79.   local rowSize = math.floor((bitsPerPixel * width + 31) / 32) * 4
  80.   local xRatio = width / 160
  81.   local yRatio = height / 50
  82.  
  83.   for y = 0, 49 do
  84.     for x = 0, 159 do
  85.       local srcX = math.floor(x * xRatio)
  86.       local srcY = math.floor(y * yRatio)
  87.       local rowStart = dataOffset + (height - 1 - srcY) * rowSize
  88.       local pixelStart = rowStart + srcX * 3
  89.       local b, g, r = stringUnpack("BBB", imageData, pixelStart + 1)
  90.       table.insert(decodedImage, {r, g, b})
  91.       yieldIfNeeded()
  92.     end
  93.   end
  94.  
  95.   return decodedImage, 160, 50
  96. end
  97.  
  98. local function decodeTIImageMode1(imageData)
  99.   local width = stringUnpack("<I4", imageData, 5)
  100.   local height = stringUnpack("<I4", imageData, 9)
  101.   local pixelData = imageData:sub(21)
  102.  
  103.   local decodedImage = {}
  104.   for i = 1, #pixelData, 2 do
  105.     local pixel = stringUnpack("<I2", pixelData, i)
  106.     local r = bit32.rshift(bit32.band(pixel, 0x7C00), 10) * 8
  107.     local g = bit32.rshift(bit32.band(pixel, 0x03E0), 5) * 8
  108.     local b = bit32.band(pixel, 0x001F) * 8
  109.     table.insert(decodedImage, {r, g, b})
  110.     yieldIfNeeded()
  111.   end
  112.  
  113.   return decodedImage, width, height
  114. end
  115.  
  116. local function decodeImageMode1(imageData)
  117.   if not imageData or imageData == "" then
  118.     error("No image data received")
  119.   end
  120.  
  121.   local format = imageData:sub(1, 2)
  122.   if format == "BM" then
  123.     return decodeBMPMode1(imageData)
  124.   elseif format == "TI" then
  125.     return decodeTIImageMode1(imageData)
  126.   else
  127.     error("Unsupported image format")
  128.   end
  129. end
  130.  
  131. local function packColor(r, g, b)
  132.   return (r * 256 + g) * 256 + b
  133. end
  134.  
  135. local function displayImageMode1(decodedImage, x, y, width, height)
  136.   for i = 1, #decodedImage do
  137.     local pixel = decodedImage[i]
  138.     local px = (i - 1) % width + x
  139.     local py = math.floor((i - 1) / width) + y
  140.     local color = packColor(pixel[1], pixel[2], pixel[3])
  141.     gpu.setBackground(color)
  142.     gpu.set(px, py, " ")
  143.     yieldIfNeeded()
  144.   end
  145. end
  146.  
  147. -- Mode 2 specific functions
  148. local function decodeBMPMode2(imageData)
  149.     local width = stringUnpack("<I4", imageData, 19)
  150.     local height = stringUnpack("<I4", imageData, 23)
  151.     local bitsPerPixel = stringUnpack("<I2", imageData, 29)
  152.     local compression = stringUnpack("<I4", imageData, 31)
  153.     local dataOffset = stringUnpack("<I4", imageData, 11)
  154.  
  155.     if bitsPerPixel ~= 24 or compression ~= 0 then
  156.         error("Unsupported BMP format. Only 24-bit uncompressed BMPs are supported.")
  157.     end
  158.  
  159.     local screenWidth, screenHeight = gpu.getResolution()
  160.     local targetWidth = screenWidth * 2
  161.     local targetHeight = screenHeight * 4
  162.  
  163.     local decodedImage = {}
  164.     local rowSize = math.floor((bitsPerPixel * width + 31) / 32) * 4
  165.     local xRatio = width / targetWidth
  166.     local yRatio = height / targetHeight
  167.  
  168.     for y = 0, targetHeight - 1 do
  169.         for x = 0, targetWidth - 1 do
  170.             local srcX = math.floor(x * xRatio)
  171.             local srcY = math.floor(y * yRatio)
  172.             local rowStart = dataOffset + (height - 1 - srcY) * rowSize
  173.             local pixelStart = rowStart + srcX * 3
  174.             local b, g, r = stringUnpack("BBB", imageData, pixelStart + 1)
  175.             table.insert(decodedImage, {r, g, b})
  176.             yieldIfNeeded()
  177.         end
  178.     end
  179.  
  180.     return decodedImage, targetWidth, targetHeight
  181. end
  182.  
  183. local function getBrailleCharacter(pixels)
  184.     local brailleBase = 0x2800
  185.     local pattern = 0
  186.     local dotWeights = {1, 2, 4, 8, 16, 32, 64, 128}
  187.  
  188.     local function adjustBrightness(brightness)
  189.         return math.pow(brightness / 255, 0.5) * 255
  190.     end
  191.  
  192.     local thresholds = {}
  193.     for i = 1, 8 do
  194.         local brightness = (pixels[i][1] + pixels[i][2] + pixels[i][3]) / 3
  195.         thresholds[i] = adjustBrightness(brightness)
  196.     end
  197.  
  198.     local avgThreshold = 0
  199.     for i = 1, 8 do
  200.         avgThreshold = avgThreshold + thresholds[i]
  201.     end
  202.     avgThreshold = avgThreshold / 8
  203.  
  204.     for i = 1, 8 do
  205.         if thresholds[i] > avgThreshold then
  206.             pattern = pattern + dotWeights[i]
  207.         end
  208.     end
  209.  
  210.     return unicode.char(brailleBase + pattern)
  211. end
  212.  
  213. local function rgbToColor(r, g, b)
  214.     return (r * 65536) + (g * 256) + b
  215. end
  216.  
  217. local function averageColor(colors)
  218.     local r, g, b, count = 0, 0, 0, #colors
  219.     for _, color in ipairs(colors) do
  220.         r = r + color[1]
  221.         g = g + color[2]
  222.         b = b + color[3]
  223.     end
  224.     if count > 0 then
  225.         return {math.floor(r / count), math.floor(g / count), math.floor(b / count)}
  226.     else
  227.         return {0, 0, 0}
  228.     end
  229. end
  230.  
  231. local function blendColors(c1, c2, factor)
  232.     return {
  233.         math.floor(c1[1] * (1 - factor) + c2[1] * factor),
  234.         math.floor(c1[2] * (1 - factor) + c2[2] * factor),
  235.         math.floor(c1[3] * (1 - factor) + c2[3] * factor)
  236.     }
  237. end
  238.  
  239. local function displayImageMode2(decodedImage, x, y, width, height)
  240.     for py = 0, height / 4 - 1 do
  241.         for px = 0, width / 2 - 1 do
  242.             local pixels = {}
  243.             local colors = {}
  244.            
  245.             for dy = 0, 3 do
  246.                 for dx = 0, 1 do
  247.                     local ix = px * 2 + dx
  248.                     local iy = py * 4 + dy
  249.                     local index = iy * width + ix + 1
  250.                     local pixel = decodedImage[index]
  251.                     pixels[dy * 2 + dx + 1] = pixel
  252.                     table.insert(colors, pixel)
  253.                 end
  254.             end
  255.  
  256.             local char = getBrailleCharacter(pixels)
  257.             local fgColor = averageColor(colors)
  258.            
  259.             local darkestColor = colors[1]
  260.             for i = 2, #colors do
  261.                 if colors[i][1] + colors[i][2] + colors[i][3] < darkestColor[1] + darkestColor[2] + darkestColor[3] then
  262.                     darkestColor = colors[i]
  263.                 end
  264.             end
  265.             local avgColor = averageColor(colors)
  266.             local bgColor = blendColors(darkestColor, avgColor, 0.3)
  267.            
  268.             gpu.setForeground(rgbToColor(fgColor[1], fgColor[2], fgColor[3]))
  269.             gpu.setBackground(rgbToColor(bgColor[1], bgColor[2], bgColor[3]))
  270.             gpu.set(x + px, y + py, char)
  271.         end
  272.         yieldIfNeeded()
  273.     end
  274. end
  275.  
  276. -- Mode 3 specific functions
  277. local function decodeBMPMode3(imageData)
  278.     local width = stringUnpack("<I4", imageData, 19)
  279.     local height = stringUnpack("<I4", imageData, 23)
  280.     local bitsPerPixel = stringUnpack("<I2", imageData, 29)
  281.     local compression = stringUnpack("<I4", imageData, 31)
  282.     local dataOffset = stringUnpack("<I4", imageData, 11)
  283.  
  284.     if bitsPerPixel ~= 24 or compression ~= 0 then
  285.         error("Unsupported BMP format. Only 24-bit uncompressed BMPs are supported.")
  286.     end
  287.  
  288.     local screenWidth, screenHeight = gpu.getResolution()
  289.     local targetWidth = screenWidth * 2
  290.     local targetHeight = screenHeight * 4
  291.  
  292.     local decodedImage = {}
  293.     local rowSize = math.floor((bitsPerPixel * width + 31) / 32) * 4
  294.     local xRatio = width / targetWidth
  295.     local yRatio = height / targetHeight
  296.  
  297.     for y = 0, targetHeight - 1 do
  298.         for x = 0, targetWidth - 1 do
  299.             local srcX = math.floor(x * xRatio)
  300.             local srcY = math.floor(y * yRatio)
  301.             local rowStart = dataOffset + (height - 1 - srcY) * rowSize
  302.             local pixelStart = rowStart + srcX * 3
  303.             local b, g, r = stringUnpack("BBB", imageData, pixelStart + 1)
  304.             table.insert(decodedImage, {r, g, b})
  305.             yieldIfNeeded()
  306.         end
  307.     end
  308.  
  309.     return decodedImage, targetWidth, targetHeight
  310. end
  311.  
  312. local function getBrailleCharacter(pixels)
  313.     local brailleBase = 0x2800
  314.     local pattern = 0
  315.     local dotWeights = {1, 2, 4, 8, 16, 32, 64, 128}
  316.  
  317.     local function adjustBrightness(brightness)
  318.         return math.pow(brightness / 255, 0.5) * 255
  319.     end
  320.  
  321.     local thresholds = {}
  322.     for i = 1, 8 do
  323.         local brightness = (pixels[i][1] + pixels[i][2] + pixels[i][3]) / 3
  324.         thresholds[i] = adjustBrightness(brightness)
  325.     end
  326.  
  327.     local avgThreshold = 0
  328.     for i = 1, 8 do
  329.         avgThreshold = avgThreshold + thresholds[i]
  330.     end
  331.     avgThreshold = avgThreshold / 8
  332.  
  333.     for i = 1, 8 do
  334.         if thresholds[i] > avgThreshold then
  335.             pattern = pattern + dotWeights[i]
  336.         end
  337.     end
  338.  
  339.     return unicode.char(brailleBase + pattern)
  340. end
  341.  
  342. local function rgbToColor(r, g, b)
  343.     return (r * 65536) + (g * 256) + b
  344. end
  345.  
  346. local function averageColor(colors)
  347.     local r, g, b, count = 0, 0, 0, #colors
  348.     for _, color in ipairs(colors) do
  349.         r = r + color[1]
  350.         g = g + color[2]
  351.         b = b + color[3]
  352.     end
  353.     if count > 0 then
  354.         return {math.floor(r / count), math.floor(g / count), math.floor(b / count)}
  355.     else
  356.         return {0, 0, 0}
  357.     end
  358. end
  359.  
  360. local function displayImageMode3(decodedImage, x, y, width, height)
  361.     for py = 0, height / 4 - 1 do
  362.         for px = 0, width / 2 - 1 do
  363.             local pixels = {}
  364.             local colors = {}
  365.            
  366.             for dy = 0, 3 do
  367.                 for dx = 0, 1 do
  368.                     local ix = px * 2 + dx
  369.                     local iy = py * 4 + dy
  370.                     local index = iy * width + ix + 1
  371.                     local pixel = decodedImage[index]
  372.                     pixels[dy * 2 + dx + 1] = pixel
  373.                     table.insert(colors, pixel)
  374.                 end
  375.             end
  376.  
  377.             local char = getBrailleCharacter(pixels)
  378.             local fgColor = averageColor(colors)
  379.             local bgColor = averageColor({colors[1], colors[8]})
  380.            
  381.             gpu.setForeground(rgbToColor(fgColor[1], fgColor[2], fgColor[3]))
  382.             gpu.setBackground(rgbToColor(bgColor[1], bgColor[2], bgColor[3]))
  383.             gpu.set(x + px, y + py, char)
  384.         end
  385.         yieldIfNeeded()
  386.     end
  387. end
  388.  
  389. local function decodeBMPMode4(imageData)
  390.     local width = stringUnpack("<I4", imageData, 19)
  391.     local height = stringUnpack("<I4", imageData, 23)
  392.     local bitsPerPixel = stringUnpack("<I2", imageData, 29)
  393.     local compression = stringUnpack("<I4", imageData, 31)
  394.     local dataOffset = stringUnpack("<I4", imageData, 11)
  395.  
  396.     if bitsPerPixel ~= 24 or compression ~= 0 then
  397.         error("Unsupported BMP format. Only 24-bit uncompressed BMPs are supported.")
  398.     end
  399.  
  400.     local screenWidth, screenHeight = gpu.getResolution()
  401.     local targetWidth = screenWidth * 2
  402.     local targetHeight = screenHeight * 2
  403.  
  404.     local decodedImage = {}
  405.     local rowSize = math.floor((bitsPerPixel * width + 31) / 32) * 4
  406.     local xRatio = width / targetWidth
  407.     local yRatio = height / targetHeight
  408.  
  409.     for y = 0, targetHeight - 1 do
  410.         for x = 0, targetWidth - 1 do
  411.             local srcX = math.floor(x * xRatio)
  412.             local srcY = math.floor(y * yRatio)
  413.             local rowStart = dataOffset + (height - 1 - srcY) * rowSize
  414.             local pixelStart = rowStart + srcX * 3
  415.             local b, g, r = stringUnpack("BBB", imageData, pixelStart + 1)
  416.             table.insert(decodedImage, {r, g, b})
  417.             yieldIfNeeded()
  418.         end
  419.     end
  420.  
  421.     return decodedImage, targetWidth, targetHeight
  422. end
  423.  
  424. local function displayImageMode4(decodedImage, x, y, width, height)
  425.     for py = 0, height / 2 - 1 do
  426.         for px = 0, width / 2 - 1 do
  427.             local index1 = (py * 2) * width + (px * 2) + 1
  428.             local index2 = (py * 2) * width + (px * 2) + 2
  429.             local index3 = (py * 2 + 1) * width + (px * 2) + 1
  430.             local index4 = (py * 2 + 1) * width + (px * 2) + 2
  431.  
  432.             local pixel1 = decodedImage[index1]
  433.             local pixel2 = decodedImage[index2]
  434.             local pixel3 = decodedImage[index3]
  435.             local pixel4 = decodedImage[index4]
  436.  
  437.             local fgColor = averageColor({pixel1, pixel3})
  438.             local bgColor = averageColor({pixel2, pixel4})
  439.  
  440.             gpu.setForeground(rgbToColor(fgColor[1], fgColor[2], fgColor[3]))
  441.             gpu.setBackground(rgbToColor(bgColor[1], bgColor[2], bgColor[3]))
  442.             gpu.set(x + px, y + py, "▀")
  443.         end
  444.         yieldIfNeeded()
  445.     end
  446. end
  447.  
  448. local function decodeBMPMode5(imageData)
  449.     local width = stringUnpack("<I4", imageData, 19)
  450.     local height = stringUnpack("<I4", imageData, 23)
  451.     local bitsPerPixel = stringUnpack("<I2", imageData, 29)
  452.     local compression = stringUnpack("<I4", imageData, 31)
  453.     local dataOffset = stringUnpack("<I4", imageData, 11)
  454.  
  455.     if bitsPerPixel ~= 24 or compression ~= 0 then
  456.         error("Unsupported BMP format. Only 24-bit uncompressed BMPs are supported.")
  457.     end
  458.  
  459.     local screenWidth, screenHeight = gpu.getResolution()
  460.     local targetWidth = screenWidth * 2
  461.     local targetHeight = screenHeight * 2
  462.  
  463.     local decodedImage = {}
  464.     local rowSize = math.floor((bitsPerPixel * width + 31) / 32) * 4
  465.     local xRatio = width / targetWidth
  466.     local yRatio = height / targetHeight
  467.  
  468.     for y = 0, targetHeight - 1 do
  469.         for x = 0, targetWidth - 1 do
  470.             local srcX = math.floor(x * xRatio)
  471.             local srcY = math.floor(y * yRatio)
  472.             local rowStart = dataOffset + (height - 1 - srcY) * rowSize
  473.             local pixelStart = rowStart + srcX * 3
  474.             local b, g, r = stringUnpack("BBB", imageData, pixelStart + 1)
  475.             table.insert(decodedImage, {r, g, b})
  476.             yieldIfNeeded()
  477.         end
  478.     end
  479.  
  480.     return decodedImage, targetWidth, targetHeight
  481. end
  482.  
  483.  
  484. local function displayImageMode5(decodedImage, x, y, width, height)
  485.     for py = 0, height / 2 - 1 do
  486.         for px = 0, width / 2 - 1 do
  487.             local index1 = (py * 2) * width + (px * 2) + 1
  488.             local index2 = (py * 2) * width + (px * 2) + 2
  489.             local index3 = (py * 2 + 1) * width + (px * 2) + 1
  490.             local index4 = (py * 2 + 1) * width + (px * 2) + 2
  491.  
  492.             local pixels = {decodedImage[index1], decodedImage[index2], decodedImage[index3], decodedImage[index4]}
  493.             local avgColor = averageColor(pixels)
  494.  
  495.             local fgColor = {math.floor(avgColor[1] / 16), math.floor(avgColor[2] / 16), math.floor(avgColor[3] / 16)}
  496.             local bgColor = {math.floor(pixels[4][1] / 16), math.floor(pixels[4][2] / 16), math.floor(pixels[4][3] / 16)}
  497.  
  498.             gpu.setForeground(rgbToColor(fgColor[1] * 16, fgColor[2] * 16, fgColor[3] * 16))
  499.             gpu.setBackground(rgbToColor(bgColor[1] * 16, bgColor[2] * 16, bgColor[3] * 16))
  500.             gpu.set(x + px, y + py, "▄")
  501.         end
  502.         yieldIfNeeded()
  503.     end
  504. end
  505.  
  506. local function decodeBMPMode6(imageData)
  507.     local width = stringUnpack("<I4", imageData, 19)
  508.     local height = stringUnpack("<I4", imageData, 23)
  509.     local bitsPerPixel = stringUnpack("<I2", imageData, 29)
  510.     local compression = stringUnpack("<I4", imageData, 31)
  511.     local dataOffset = stringUnpack("<I4", imageData, 11)
  512.  
  513.     if bitsPerPixel ~= 24 or compression ~= 0 then
  514.         error("Unsupported BMP format. Only 24-bit uncompressed BMPs are supported.")
  515.     end
  516.  
  517.     local targetWidth = 640
  518.     local targetHeight = 200
  519.  
  520.     local decodedImage = {}
  521.     local rowSize = math.floor((bitsPerPixel * width + 31) / 32) * 4
  522.     local xRatio = width / targetWidth
  523.     local yRatio = height / targetHeight
  524.  
  525.     for y = 0, targetHeight - 1 do
  526.         for x = 0, targetWidth - 1 do
  527.             local srcX = math.floor(x * xRatio)
  528.             local srcY = math.floor(y * yRatio)
  529.             local rowStart = dataOffset + (height - 1 - srcY) * rowSize
  530.             local pixelStart = rowStart + srcX * 3
  531.             local b, g, r = stringUnpack("BBB", imageData, pixelStart + 1)
  532.             table.insert(decodedImage, {r, g, b})
  533.             yieldIfNeeded()
  534.         end
  535.     end
  536.  
  537.     return decodedImage, targetWidth, targetHeight
  538. end
  539.  
  540. local function getBrailleCharacterMode6(pixels)
  541.     local brailleBase = 0x2800
  542.     local pattern = 0
  543.     local dotWeights = {1, 2, 4, 8, 16, 32, 64, 128}
  544.  
  545.     local function adjustBrightness(brightness)
  546.         return math.pow(brightness / 255, 0.5) * 255
  547.     end
  548.  
  549.     local thresholds = {}
  550.     for i = 1, 8 do
  551.         local brightness = (pixels[i][1] + pixels[i][2] + pixels[i][3]) / 3
  552.         thresholds[i] = adjustBrightness(brightness)
  553.     end
  554.  
  555.     local avgThreshold = 0
  556.     for i = 1, 8 do
  557.         avgThreshold = avgThreshold + thresholds[i]
  558.     end
  559.     avgThreshold = avgThreshold / 8
  560.  
  561.     for i = 1, 8 do
  562.         if thresholds[i] > avgThreshold then
  563.             pattern = pattern + dotWeights[i]
  564.         end
  565.     end
  566.  
  567.     return unicode.char(brailleBase + pattern)
  568. end
  569.  
  570. local function getBrailleCharacterMode6(pixels)
  571.     local brailleBase = 0x2800
  572.     local pattern = 0
  573.     local dotWeights = {1, 2, 4, 8}
  574.  
  575.     local function adjustBrightness(brightness)
  576.         return math.pow(brightness / 255, 0.5) * 255
  577.     end
  578.  
  579.     local thresholds = {}
  580.     for i = 1, 4 do
  581.         local brightness = (pixels[i][1] + pixels[i][2] + pixels[i][3]) / 3
  582.         thresholds[i] = adjustBrightness(brightness)
  583.     end
  584.  
  585.     local avgThreshold = 0
  586.     for i = 1, 4 do
  587.         avgThreshold = avgThreshold + thresholds[i]
  588.     end
  589.     avgThreshold = avgThreshold / 4
  590.  
  591.     for i = 1, 4 do
  592.         if thresholds[i] > avgThreshold then
  593.             pattern = pattern + dotWeights[i]
  594.         end
  595.     end
  596.  
  597.     return unicode.char(brailleBase + pattern)
  598. end
  599.  
  600. local function displayImageMode6(decodedImage, x, y, width, height)
  601.     for py = 0, 49 do
  602.         for px = 0, 159 do
  603.             local pixels = {}
  604.             for dy = 0, 3 do
  605.                 for dx = 0, 1 do
  606.                     local ix = px * 4 + dx
  607.                     local iy = py * 4 + dy
  608.                     local index = iy * width + ix + 1
  609.                     table.insert(pixels, decodedImage[index])
  610.                 end
  611.             end
  612.  
  613.             local char = getBrailleCharacterMode6(pixels)
  614.             local fgColor = averageColor({pixels[1], pixels[2], pixels[3], pixels[4]})
  615.             local bgColor = averageColor({pixels[5], pixels[6], pixels[7], pixels[8]})
  616.            
  617.             gpu.setForeground(rgbToColor(fgColor[1], fgColor[2], fgColor[3]))
  618.             gpu.setBackground(rgbToColor(bgColor[1], bgColor[2], bgColor[3]))
  619.             gpu.set(x + px, y + py, char)
  620.         end
  621.         yieldIfNeeded()
  622.     end
  623. end
  624.  
  625. local function decodeBMPMode7(imageData)
  626.     local width = stringUnpack("<I4", imageData, 19)
  627.     local height = stringUnpack("<I4", imageData, 23)
  628.     local bitsPerPixel = stringUnpack("<I2", imageData, 29)
  629.     local compression = stringUnpack("<I4", imageData, 31)
  630.     local dataOffset = stringUnpack("<I4", imageData, 11)
  631.  
  632.     if bitsPerPixel ~= 24 or compression ~= 0 then
  633.         error("Unsupported BMP format. Only 24-bit uncompressed BMPs are supported.")
  634.     end
  635.  
  636.     local targetWidth = 640
  637.     local targetHeight = 400
  638.  
  639.     local decodedImage = {}
  640.     local rowSize = math.floor((bitsPerPixel * width + 31) / 32) * 4
  641.     local xRatio = width / targetWidth
  642.     local yRatio = height / targetHeight
  643.  
  644.     for y = 0, targetHeight - 1 do
  645.         for x = 0, targetWidth - 1 do
  646.             local srcX = math.floor(x * xRatio)
  647.             local srcY = math.floor(y * yRatio)
  648.             local rowStart = dataOffset + (height - 1 - srcY) * rowSize
  649.             local pixelStart = rowStart + srcX * 3
  650.             local b, g, r = stringUnpack("BBB", imageData, pixelStart + 1)
  651.             table.insert(decodedImage, {r, g, b})
  652.             yieldIfNeeded()
  653.         end
  654.     end
  655.  
  656.     return decodedImage, targetWidth, targetHeight
  657. end
  658.  
  659. local function getBrailleCharacterMode7(pixels)
  660.     local brailleBase = 0x2800
  661.     local pattern = 0
  662.     local dotWeights = {1, 2, 4, 8, 16, 32, 64, 128}
  663.  
  664.     local function adjustBrightness(brightness)
  665.         return math.pow(brightness / 255, 0.7) * 255
  666.     end
  667.  
  668.     local thresholds = {}
  669.     for i = 1, 8 do
  670.         local brightness = (pixels[i][1] * 0.299 + pixels[i][2] * 0.587 + pixels[i][3] * 0.114)
  671.         thresholds[i] = adjustBrightness(brightness)
  672.     end
  673.  
  674.     local avgThreshold = 0
  675.     for i = 1, 8 do
  676.         avgThreshold = avgThreshold + thresholds[i]
  677.     end
  678.     avgThreshold = avgThreshold / 8
  679.  
  680.     for i = 1, 8 do
  681.         if thresholds[i] > avgThreshold then
  682.             pattern = pattern + dotWeights[i]
  683.         end
  684.     end
  685.  
  686.     return unicode.char(brailleBase + pattern)
  687. end
  688.  
  689. local function rgbToLabMode7(r, g, b)
  690.     r, g, b = r / 255, g / 255, b / 255
  691.     local x = 0.4124564 * r + 0.3575761 * g + 0.1804375 * b
  692.     local y = 0.2126729 * r + 0.7151522 * g + 0.0721750 * b
  693.     local z = 0.0193339 * r + 0.1191920 * g + 0.9503041 * b
  694.  
  695.     local function f(t)
  696.         if t > 0.008856 then
  697.             return math.pow(t, 1/3)
  698.         else
  699.             return 7.787 * t + 16/116
  700.         end
  701.     end
  702.  
  703.     x, y, z = f(x / 0.95047), f(y), f(z / 1.08883)
  704.  
  705.     return 116 * y - 16, 500 * (x - y), 200 * (y - z)
  706. end
  707.  
  708. local function colorDistanceMode7(c1, c2)
  709.     local l1, a1, b1 = rgbToLabMode7(c1[1], c1[2], c1[3])
  710.     local l2, a2, b2 = rgbToLabMode7(c2[1], c2[2], c2[3])
  711.     local dl, da, db = l1 - l2, a1 - a2, b1 - b2
  712.     return math.sqrt(dl * dl + da * da + db * db)
  713. end
  714.  
  715. local function findClosestColorMode7(color, palette)
  716.     local minDistance = math.huge
  717.     local closestColor = palette[1]
  718.     for _, paletteColor in ipairs(palette) do
  719.         local distance = colorDistanceMode7(color, paletteColor)
  720.         if distance < minDistance then
  721.             minDistance = distance
  722.             closestColor = paletteColor
  723.         end
  724.     end
  725.     return closestColor
  726. end
  727.  
  728. local function createDitherPaletteMode7()
  729.     local palette = {}
  730.     for r = 0, 5 do
  731.         for g = 0, 5 do
  732.             for b = 0, 5 do
  733.                 table.insert(palette, {r * 51, g * 51, b * 51})
  734.             end
  735.         end
  736.     end
  737.     for i = 0, 15 do
  738.         table.insert(palette, {i * 17, i * 17, i * 17})
  739.     end
  740.     return palette
  741. end
  742.  
  743. local function displayImageMode7(decodedImage, x, y, width, height)
  744.     local ditherPalette = createDitherPaletteMode7()
  745.     local screenWidth, screenHeight = 160, 50  -- Fixed for tier 3 GPU
  746.    
  747.     local function getDitheredColor(ix, iy)
  748.         local index = iy * width + ix + 1
  749.         local pixel = decodedImage[index] or {0, 0, 0}
  750.  
  751.         local ditherMatrix = {
  752.             {0, 8, 2, 10},
  753.             {12, 4, 14, 6},
  754.             {3, 11, 1, 9},
  755.             {15, 7, 13, 5}
  756.         }
  757.  
  758.         local ditherValue = ditherMatrix[iy % 4 + 1][ix % 4 + 1] / 16
  759.         local ditheredColor = {
  760.             math.min(255, pixel[1] + (ditherValue - 0.5) * 32),
  761.             math.min(255, pixel[2] + (ditherValue - 0.5) * 32),
  762.             math.min(255, pixel[3] + (ditherValue - 0.5) * 32)
  763.         }
  764.  
  765.         return findClosestColorMode7(ditheredColor, ditherPalette)
  766.     end
  767.    
  768.     for py = 0, screenHeight - 1 do
  769.         for px = 0, screenWidth - 1 do
  770.             local pixels = {}
  771.             local colors = {}
  772.            
  773.             for dy = 0, 3 do
  774.                 for dx = 0, 1 do
  775.                     local ix = px * 4 + dx
  776.                     local iy = py * 8 + dy
  777.                     local index = iy * width + ix + 1
  778.                     local pixel = decodedImage[index] or {0, 0, 0}  -- Default to black if out of bounds
  779.                     pixels[dy * 2 + dx + 1] = pixel
  780.                     table.insert(colors, pixel)
  781.                 end
  782.             end
  783.  
  784.             local char = getBrailleCharacterMode7(pixels)
  785.             local avgColor = averageColor(colors)
  786.             local fgColor = findClosestColorMode7(avgColor, ditherPalette)
  787.  
  788.             local ditheredColor = getDitheredColor(px * 4, py * 8)
  789.            
  790.             gpu.setForeground(rgbToColor(fgColor[1], fgColor[2], fgColor[3]))
  791.             gpu.setBackground(rgbToColor(ditheredColor[1], ditheredColor[2], ditheredColor[3]))
  792.             gpu.set(x + px, y + py, char)
  793.         end
  794.         yieldIfNeeded()
  795.     end
  796. end
  797.  
  798. local function OC_BMP()
  799.     term.clear()
  800.     print("░█▀█░█▀▀░░░░░█▀▄░█▄█░█▀█")
  801.     print("░█░█░█░░░▄▄▄░█▀▄░█░█░█▀▀")
  802.     print("░▀▀▀░▀▀▀░░░░░▀▀░░▀░▀░▀░░")
  803.     print("\nOC-BMP : An BMP Image Viewer For OpenComputers Coded By nonogamer9! Version 1.4")
  804.     print("\nSelect mode:")
  805.     print("1. Mode 1 (Normal Tier 3 GPU resolution of 160x50 with 256 colors and a color palette of 16 colors)")
  806.     print("2. Mode 2 (Custom resolution of 320x200 with 256 colors and a color palette of 16 colors)")
  807.     print("3. Mode 3 (Custom resolution of 320x200 with full 24-bit color and a color palette of 16 colors)")
  808.     print("4. Mode 4 (Double Tier 3 GPU resolution of 320x100 with 256 colors and a color palette of 16 colors)")
  809.     print("5. Mode 5 (Pseudo-Color Mode 320x100 with 4096 colors and a color palette of 32 colors)")
  810.     print("6. Mode 6 (High-Resolution Mode of 640x200 with 256 colors and a color palette of 16 colors)")
  811.     print("7. Mode 7 (Ultra-High Resolution Mode of 640x400 with 24 bit color and a color palette of 256 colors)")
  812.    
  813.     local mode = tonumber(io.read())
  814.    
  815.     if mode ~= 1 and mode ~= 2 and mode ~= 3 and mode ~= 4 and mode ~= 5 and mode ~= 6 and mode ~= 7 then
  816.         print("Invalid mode. Defaulting to mode 1 (160x50)")
  817.         mode = 1
  818.     end
  819.  
  820.     print("\nEnter image URL:")
  821.     local url = io.read()
  822.    
  823.     local success, result = pcall(function()
  824.         print("Downloading image...")
  825.         local imageData = downloadImage(url)
  826.        
  827.         print("Decoding image...")
  828.         local decodedImage, width, height
  829.        
  830.         if mode == 1 then
  831.             decodedImage, width, height = decodeBMPMode1(imageData)
  832.         elseif mode == 2 then
  833.             decodedImage, width, height = decodeBMPMode2(imageData)
  834.         elseif mode == 3 then
  835.             decodedImage, width, height = decodeBMPMode3(imageData)
  836.         elseif mode == 4 then
  837.             decodedImage, width, height = decodeBMPMode4(imageData)
  838.         elseif mode == 5 then
  839.             decodedImage, width, height = decodeBMPMode5(imageData)
  840.         elseif mode == 6 then
  841.             decodedImage, width, height = decodeBMPMode6(imageData)
  842.         elseif mode == 7 then
  843.             decodedImage, width, height = decodeBMPMode7(imageData)
  844.         end
  845.        
  846.         print("Displaying image...")
  847.         local screenWidth, screenHeight = gpu.getResolution()
  848.         local xPos, yPos
  849.         if mode == 1 then
  850.             xPos = math.floor((screenWidth - width) / 2)
  851.             yPos = math.floor((screenHeight - height) / 2)
  852.         elseif mode == 2 or mode == 3 then
  853.             xPos = math.floor((screenWidth - width / 2) / 2)
  854.             yPos = math.floor((screenHeight - height / 4) / 2)
  855.         elseif mode == 4 or mode == 5 then
  856.             xPos = math.floor((screenWidth - width / 2) / 2)
  857.             yPos = math.floor((screenHeight - height / 2) / 2)
  858.         elseif mode == 6 then
  859.             xPos = math.floor((screenWidth - width / 4) / 2)
  860.             yPos = math.floor((screenHeight - height / 4) / 2)
  861.         elseif mode == 7 then
  862.             xPos = math.floor((screenWidth - 160) / 2)
  863.             yPos = math.floor((screenHeight - 50) / 2)
  864.         end
  865.  
  866.         if mode == 1 then
  867.             displayImageMode1(decodedImage, xPos, yPos, width, height)
  868.         elseif mode == 2 then
  869.             displayImageMode2(decodedImage, xPos, yPos, width, height)
  870.         elseif mode == 3 then
  871.             displayImageMode3(decodedImage, xPos, yPos, width, height)
  872.         elseif mode == 4 then
  873.             displayImageMode4(decodedImage, xPos, yPos, width, height)
  874.         elseif mode == 5 then
  875.             displayImageMode5(decodedImage, xPos, yPos, width, height)
  876.         elseif mode == 6 then
  877.             displayImageMode6(decodedImage, xPos, yPos, width, height)
  878.         elseif mode == 7 then
  879.             displayImageMode7(decodedImage, xPos, yPos, width, height)
  880.         end
  881.        
  882.         print("Press any key to exit")
  883.         event.pull("key_down")
  884.     end)
  885.  
  886.     if not success then
  887.         print("Error: " .. tostring(result))
  888.         print("Press any key to exit")
  889.         event.pull("key_down")
  890.     end
  891.    
  892.     term.clear()
  893. end
  894.  
  895. OC_BMP()
  896.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement