Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local originalTerm = term.native()
- local GraphicsEmulator = {
- mode = 0,
- pixelBuffer = {},
- bankedPalette = {},
- SCALE_X = 6,
- SCALE_Y = 9,
- width = 0,
- height = 0
- }
- -- Initialize dimensions and buffer
- function GraphicsEmulator:init()
- local tw, th = originalTerm.getSize()
- self.width = tw
- self.height = th
- -- Initialize the 256-color pixel buffer (0-indexed coordinates)
- local pw, ph = tw * self.SCALE_X, th * self.SCALE_Y
- for y = 0, ph - 1 do
- self.pixelBuffer[y] = {}
- for x = 0, pw - 1 do
- self.pixelBuffer[y][x] = 0
- end
- end
- end
- -- 1. Banking: Save current text-mode palette
- function GraphicsEmulator:bankPalette()
- for i = 0, 15 do
- local color = 2^i
- local r, g, b = originalTerm.getPaletteColor(color)
- self.bankedPalette[color] = {r, g, b}
- end
- end
- -- 2. Restore: Put back the original text-mode palette
- function GraphicsEmulator:restorePalette()
- for color, rgb in pairs(self.bankedPalette) do
- originalTerm.setPaletteColor(color, table.unpack(rgb))
- end
- end
- -- 3. Dynamic Palette: Scan the 256-color buffer to find the best 16 colors
- function GraphicsEmulator:refreshDynamicPalette()
- if self.mode ~= 2 then return end
- -- Divide the 256 colors into 16 "blocks" of 16 colors each
- for block = 0, 15 do
- local freq = {}
- local startIdx = block * 16
- local endIdx = startIdx + 15
- -- Analyze current pixel buffer for this specific block
- for y = 0, (self.height * self.SCALE_Y) - 1 do
- local row = self.pixelBuffer[y]
- for x = 0, (self.width * self.SCALE_X) - 1 do
- local c = row[x]
- if c >= startIdx and c <= endIdx then
- freq[c] = (freq[c] or 0) + 1
- end
- end
- end
- -- Find the most frequent color in this range
- local winner = startIdx
- local maxCount = -1
- for c = startIdx, endIdx do
- if (freq[c] or 0) > maxCount then
- maxCount = freq[c] or 0
- winner = c
- end
- end
- -- Map this representative color to the hardware's 16-color slot
- -- Note: term.getPaletteColor(winner) works because we are in mode 2
- local r, g, b = term.getPaletteColor(winner)
- originalTerm.setPaletteColor(2^block, r, g, b)
- end
- end
- -- 4. Mode Switcher: Handles the transition and banking automatically
- function GraphicsEmulator:setMode(newMode)
- -- Entering Graphics Mode from Text Mode
- if newMode ~= 0 and self.mode == 0 then
- self:bankPalette()
- -- Returning to Text Mode
- elseif newMode == 0 and self.mode ~= 0 then
- self:restorePalette()
- end
- self.mode = newMode
- term.setGraphicsMode(newMode)
- end
- -- 5. Cell Updater: Converts a 6x9 pixel block into a single blit character
- function GraphicsEmulator:updateCell(cellX, cellY)
- local subPixels = {}
- local colorFreq = {}
- -- Downsample 6x9 logical area into 2x3 visual sub-pixels
- for zy = 0, 2 do
- for zx = 0, 1 do
- local x = cellX * self.SCALE_X + (zx * 3)
- local y = cellY * self.SCALE_Y + (zy * 3)
- -- Simple sampling: take the top-left pixel of each 3x3 zone
- local zoneColor = (self.pixelBuffer[y] and self.pixelBuffer[y][x]) or 0
- table.insert(subPixels, zoneColor)
- colorFreq[zoneColor] = (colorFreq[zoneColor] or 0) + 1
- end
- end
- -- Background is the most frequent color in the 6 zones
- local bgColor = subPixels[1]
- local maxC = -1
- for c, count in pairs(colorFreq) do
- if count > maxC then maxC = count; bgColor = c end
- end
- -- Foreground is the first color that isn't the background
- local fgColor = bgColor
- for i = 1, 6 do
- if subPixels[i] ~= bgColor then
- fgColor = subPixels[i]
- break
- end
- end
- -- Construct the character code (128-159)
- local charCode = 128
- local bitValues = {1, 2, 4, 8, 16, 32}
- for i = 1, 6 do
- if subPixels[i] ~= bgColor then
- charCode = charCode + bitValues[i]
- end
- end
- if charCode > 159 then charCode = 159 end
- -- Map 256-colors to the 0-F hex range for blit
- local fIdx = math.floor(fgColor / 16)
- local bIdx = math.floor(bgColor / 16)
- local fChar = string.format("%x", fIdx)
- local bChar = string.format("%x", bIdx)
- originalTerm.setCursorPos(cellX + 1, cellY + 1)
- if fChar == bChar or charCode == 128 then
- originalTerm.blit(" ", bChar, bChar)
- else
- originalTerm.blit(string.char(charCode), fChar, bChar)
- end
- end
- -- 6. Render Frame: Refreshes the whole screen
- function GraphicsEmulator:renderFrame()
- -- First, freeze to prevent flickering
- if term.setFrozen then term.setFrozen(true) end
- -- Recalculate which 16 colors represent the frame best
- self:refreshDynamicPalette()
- -- Update every text cell
- for y = 0, self.height - 1 do
- for x = 0, self.width - 1 do
- self:updateCell(x, y)
- end
- end
- if term.setFrozen then term.setFrozen(false) end
- end
Advertisement
Add Comment
Please, Sign In to add comment