Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local function readBMP(filename)
- local file = fs.open(filename, "rb")
- if not file then
- return nil, "Failed to open file"
- end
- local header = file.read(54)
- if #header < 54 or string.sub(header, 1, 2) ~= "BM" then
- file.close()
- return nil, "Not a valid BMP file"
- end
- local bpp = string.byte(header, 29)
- if bpp ~= 24 then
- file.close()
- return nil, "Only 24-bit BMPs supported"
- end
- local width = string.byte(header, 19) + string.byte(header, 20) * 256
- + string.byte(header, 21) * 65536 + string.byte(header, 22) * 16777216
- local height = string.byte(header, 23) + string.byte(header, 24) * 256
- + string.byte(header, 25) * 65536 + string.byte(header, 26) * 16777216
- local rowSize = width * 3
- local padding = (4 - (rowSize % 4)) % 4
- local pixels = {}
- for y = height, 1, -1 do
- pixels[y] = {}
- for x = 1, width do
- local b = file.read(1)
- local g = file.read(1)
- local r = file.read(1)
- pixels[y][x] = {
- r = string.byte(r),
- g = string.byte(g),
- b = string.byte(b)
- }
- end
- file.read(padding)
- if y % 10 == 0 then
- os.queueEvent("yield")
- os.pullEvent("yield")
- end
- end
- file.close()
- return {width = width, height = height, pixels = pixels}
- end
- local function resizeImage(image, newWidth, newHeight)
- local resized = {width = newWidth, height = newHeight, pixels = {}}
- for y = 1, newHeight do
- resized.pixels[y] = {}
- for x = 1, newWidth do
- local srcX = math.floor(x * image.width / newWidth)
- local srcY = math.floor(y * image.height / newHeight)
- resized.pixels[y][x] = image.pixels[srcY][srcX]
- end
- if y % 10 == 0 then
- os.queueEvent("yield")
- os.pullEvent("yield")
- end
- end
- return resized
- end
- local colors = {
- [0x1] = {240, 240, 240}, [0x2] = {242, 178, 51}, [0x4] = {229, 127, 216},
- [0x8] = {153, 178, 242}, [0x10] = {222, 222, 108}, [0x20] = {127, 204, 25},
- [0x40] = {242, 178, 204}, [0x80] = {76, 76, 76}, [0x100] = {153, 153, 153},
- [0x200] = {76, 153, 178}, [0x400] = {178, 102, 229}, [0x800] = {51, 102, 204},
- [0x1000] = {127, 102, 76}, [0x2000] = {87, 166, 78}, [0x4000] = {204, 76, 76},
- [0x8000] = {17, 17, 17}
- }
- local function rgbToComputerCraftColor(r, g, b)
- local closestColor = 0x1
- local minDifference = math.huge
- for code, rgb in pairs(colors) do
- local difference = math.sqrt((r - rgb[1])^2 + (g - rgb[2])^2 + (b - rgb[3])^2)
- if difference < minDifference then
- minDifference = difference
- closestColor = code
- end
- end
- return closestColor
- end
- local function displayBMP(image, output)
- local width, height = output.getSize()
- local resizedImage = resizeImage(image, width, height)
- for y = 1, height do
- for x = 1, width do
- local pixel = resizedImage.pixels[y][x]
- local color = rgbToComputerCraftColor(pixel.r, pixel.g, pixel.b)
- output.setCursorPos(x, y)
- output.setBackgroundColor(color)
- output.write(" ")
- end
- if y % 5 == 0 then
- os.queueEvent("yield")
- os.pullEvent("yield")
- end
- end
- end
- local function displayBMPMode2(image, output)
- local width, height = output.getSize()
- local resizedImage = resizeImage(image, width * 2, height * 2)
- local charSet = {" ", "\131", "\132", "\133", "\135", "\136", "\137", "\138", "\139", "\140"}
- local function getNearestColor(r, g, b)
- local closestColor = 0x1
- local minDifference = math.huge
- for code, rgb in pairs(colors) do
- local difference = math.abs(r - rgb[1]) + math.abs(g - rgb[2]) + math.abs(b - rgb[3])
- if difference < minDifference then
- minDifference = difference
- closestColor = code
- end
- end
- return closestColor
- end
- local function blendColors(c1, c2)
- return {
- r = (c1.r + c2.r) / 2,
- g = (c1.g + c2.g) / 2,
- b = (c1.b + c2.b) / 2
- }
- end
- for y = 1, height do
- for x = 1, width do
- local tl = resizedImage.pixels[y*2-1][x*2-1]
- local tr = resizedImage.pixels[y*2-1][x*2]
- local bl = resizedImage.pixels[y*2][x*2-1]
- local br = resizedImage.pixels[y*2][x*2]
- local avgColor = blendColors(blendColors(tl, tr), blendColors(bl, br))
- local intensity = (avgColor.r + avgColor.g + avgColor.b) / 3
- local pattern = math.floor(intensity / 28.4) + 1
- local bgColor = getNearestColor(tl.r, tl.g, tl.b)
- local fgColor = getNearestColor(br.r, br.g, br.b)
- if bgColor == fgColor then
- if pattern > 5 then
- fgColor = getNearestColor(math.min(br.r + 30, 255), math.min(br.g + 30, 255), math.min(br.b + 30, 255))
- else
- bgColor = getNearestColor(math.max(tl.r - 30, 0), math.max(tl.g - 30, 0), math.max(tl.b - 30, 0))
- end
- end
- output.setCursorPos(x, y)
- output.setBackgroundColor(bgColor)
- output.setTextColor(fgColor)
- output.write(charSet[pattern])
- end
- if y % 5 == 0 then
- os.queueEvent("yield")
- os.pullEvent("yield")
- end
- end
- end
- local function displayBMPCraftOSPC(image, output, colorMode)
- local screenWidth, screenHeight = output.getSize()
- local imageWidth, imageHeight = image.width, image.height
- local originalPalette = {}
- for i = 0, 255 do
- originalPalette[i] = {term.getPaletteColor(i)}
- end
- local originalGraphicsMode = term.getGraphicsMode()
- local originalBackgroundColor = term.getBackgroundColor()
- local originalTextColor = term.getTextColor()
- -- Setup palette for 256-color mode if needed
- if colorMode == 2 or colorMode == 3 then
- term.setGraphicsMode(2)
- local cubeSize = 5 -- Palette steps for RGB cube
- for i = 16, 255 do
- local index = i - 16
- local rIndex = math.floor(index / (cubeSize * cubeSize))
- local gIndex = math.floor((index % (cubeSize * cubeSize)) / cubeSize)
- local bIndex = index % cubeSize
- local r = math.floor(rIndex * 255 / (cubeSize - 1))
- local g = math.floor(gIndex * 255 / (cubeSize - 1))
- local b = math.floor(bIndex * 255 / (cubeSize - 1))
- term.setPaletteColor(i, r / 255, g / 255, b / 255)
- end
- else
- term.setGraphicsMode(colorMode)
- end
- local function adjustBrightness(value)
- return math.min(255, math.max(0, math.floor(value * 1.2))) -- Increase brightness by 20%
- end
- local function mapColor(r, g, b)
- r, g, b = adjustBrightness(r), adjustBrightness(g), adjustBrightness(b)
- local cubeSize = 5
- local rIndex = math.floor(r / (255 / (cubeSize - 1)))
- local gIndex = math.floor(g / (255 / (cubeSize - 1)))
- local bIndex = math.floor(b / (255 / (cubeSize - 1)))
- return 16 + rIndex * cubeSize * cubeSize + gIndex * cubeSize + bIndex
- end
- if colorMode == 3 then
- -- Dithered 256-color mode (new mode)
- local width = screenWidth * 6
- local height = screenHeight * 9
- local resizedImage = resizeImage(image, width, height)
- -- Convert pixels to floats for error diffusion
- local buffer = {}
- for y = 1, height do
- buffer[y] = {}
- for x = 1, width do
- buffer[y][x] = {
- r = resizedImage.pixels[y][x].r,
- g = resizedImage.pixels[y][x].g,
- b = resizedImage.pixels[y][x].b
- }
- end
- end
- local function paletteColor(index)
- local r, g, b = term.getPaletteColor(index)
- return {r = r * 255, g = g * 255, b = b * 255}
- end
- for y = 1, height do
- for x = 1, width do
- local oldPixel = buffer[y][x]
- local newIndex = mapColor(oldPixel.r, oldPixel.g, oldPixel.b)
- local newPixel = paletteColor(newIndex)
- output.setPixel(x - 1, y - 1, newIndex) -- 0-based coordinates
- local err = {
- r = oldPixel.r - newPixel.r,
- g = oldPixel.g - newPixel.g,
- b = oldPixel.b - newPixel.b
- }
- if x + 1 <= width then
- buffer[y][x + 1].r = buffer[y][x + 1].r + err.r * 7 / 16
- buffer[y][x + 1].g = buffer[y][x + 1].g + err.g * 7 / 16
- buffer[y][x + 1].b = buffer[y][x + 1].b + err.b * 7 / 16
- end
- if x - 1 >= 1 and y + 1 <= height then
- buffer[y + 1][x - 1].r = buffer[y + 1][x - 1].r + err.r * 3 / 16
- buffer[y + 1][x - 1].g = buffer[y + 1][x - 1].g + err.g * 3 / 16
- buffer[y + 1][x - 1].b = buffer[y + 1][x - 1].b + err.b * 3 / 16
- end
- if y + 1 <= height then
- buffer[y + 1][x].r = buffer[y + 1][x].r + err.r * 5 / 16
- buffer[y + 1][x].g = buffer[y + 1][x].g + err.g * 5 / 16
- buffer[y + 1][x].b = buffer[y + 1][x].b + err.b * 5 / 16
- end
- if x + 1 <= width and y + 1 <= height then
- buffer[y + 1][x + 1].r = buffer[y + 1][x + 1].r + err.r * 1 / 16
- buffer[y + 1][x + 1].g = buffer[y + 1][x + 1].g + err.g * 1 / 16
- buffer[y + 1][x + 1].b = buffer[y + 1][x + 1].b + err.b * 1 / 16
- end
- end
- if y % 10 == 0 then
- os.queueEvent("yield")
- os.pullEvent("yield")
- end
- end
- else
- -- Normal 16 or 256 color mode
- local width = screenWidth * 6
- local height = screenHeight * 9
- local resizedImage = resizeImage(image, width, height)
- for y = 0, height - 1 do
- for x = 0, width - 1 do
- local sourceX = math.floor(x * image.width / width)
- local sourceY = math.floor(y * image.height / height)
- local pixel = image.pixels[sourceY + 1][sourceX + 1]
- local color = mapColor(pixel.r, pixel.g, pixel.b)
- output.setPixel(x, y, color)
- end
- if y % 10 == 0 then
- os.queueEvent("yield")
- os.pullEvent("yield")
- end
- end
- end
- print("Image displayed. Press any key to exit.")
- os.pullEvent("key")
- term.setGraphicsMode(originalGraphicsMode)
- for i = 0, 255 do
- term.setPaletteColor(i, table.unpack(originalPalette[i]))
- end
- term.setBackgroundColor(originalBackgroundColor)
- term.setTextColor(originalTextColor)
- term.setBackgroundColor(colors.black)
- term.setTextColor(colors.white)
- term.clear()
- term.setCursorPos(1, 1)
- end
- local function downloadBMP(url, filename)
- if not http then
- error("HTTP API is not enabled. Enable it in ComputerCraft.cfg")
- end
- print("Downloading " .. url .. "...")
- local response = http.get(url, nil, true)
- if not response then
- error("Failed to download file")
- end
- local file = fs.open(filename, "wb")
- local content = response.readAll()
- for i = 1, #content, 8192 do
- file.write(content:sub(i, i + 8191))
- os.queueEvent("yield")
- os.pullEvent("yield")
- end
- file.close()
- response.close()
- print("Downloaded as " .. filename)
- end
- print("CC-BMP : An Enhanced BMP Image Viewer For ComputerCraft Coded By nonogamer9!")
- print("Enter a local filename or URL:")
- local input = read()
- local filename = input
- if string.sub(input, 1, 4) == "http" then
- filename = "downloaded.bmp"
- downloadBMP(input, filename)
- end
- local image, err = readBMP(filename)
- if not image then
- print("Error reading BMP file:", err)
- return
- end
- print("Select display mode:")
- print("1. Basic ComputerCraft screen")
- print("2. Mode 2 (Just a better resolution.)")
- print("3. CraftOS-PC 16-color Graphics Mode")
- print("4. CraftOS-PC 256-color Graphics Mode")
- print("5. Monitor")
- print("6. CraftOS-PC 256-color Dithered Mode") -- New mode
- local mode = tonumber(read())
- local output
- if mode == 5 then
- output = peripheral.find("monitor")
- if not output then
- print("No monitor found")
- return
- end
- else
- output = term
- end
- if mode == 1 then
- displayBMP(image, output)
- elseif mode == 2 then
- displayBMPMode2(image, output)
- elseif mode == 3 then
- if term.setGraphicsMode then
- displayBMPCraftOSPC(image, output, 1) -- 16-color mode
- else
- print("CraftOS-PC Graphics Mode not supported")
- end
- elseif mode == 4 then
- if term.setGraphicsMode then
- displayBMPCraftOSPC(image, output, 2) -- 256-color mode
- else
- print("CraftOS-PC Graphics Mode not supported")
- end
- elseif mode == 6 then
- if term.setGraphicsMode then
- displayBMPCraftOSPC(image, output, 3) -- Our new dithered 256-color mode
- else
- print("CraftOS-PC Graphics Mode not supported")
- end
- elseif mode == 5 then
- displayBMP(image, output)
- else
- print("Invalid mode selected")
- end
- print("Image displayed")
Advertisement
Add Comment
Please, Sign In to add comment