-- bimg_advanced.lua -- 增强版 BIMG 图像/动画播放库 -- 功能: -- 1. 支持文件路径、URL 和直接数据播放 -- 2. 支持循环播放 -- 3. 提供播放控制功能 -- 4. 支持多显示器配置 -- 5. 支持自定义终端对象 local bimg = {} local currentAnimation = nil local running = false local httpEnabled = false -- 检查并启用HTTP功能 if http and http.get then httpEnabled = true end -- 内部函数:绘制单帧 local function drawFrame(frame, termObj) for y, row in ipairs(frame) do termObj.setCursorPos(1, y) termObj.blit(table.unpack(row)) end if frame.palette then for i = 0, #frame.palette do local c = frame.palette[i] if type(c) == "table" then termObj.setPaletteColor(2^i, table.unpack(c)) else termObj.setPaletteColor(2^i, c) end end end end -- 内部函数:加载图像数据 local function loadImageData(data) if type(data) == "string" then -- 如果是字符串,尝试反序列化 local success, result = pcall(textutils.unserialize, data) if success then return result else error("Invalid image data: " .. result) end elseif type(data) == "table" then -- 如果是table,直接使用 return data else error("Unsupported image data type") end end -- 从文件加载图像 function bimg.loadFromFile(path) local file, err = fs.open(shell.resolve(path), "rb") if not file then error("Could not open file: " .. err) end local data = file.readAll() file.close() return loadImageData(data) end -- 从URL加载图像 function bimg.loadFromURL(url) if not httpEnabled then error("HTTP API is not available") end local response = http.get(url) if not response then error("Failed to fetch from URL: " .. url) end local data = response.readAll() response.close() return loadImageData(data) end -- 内部函数:校准多显示器 local function calibrateMonitors(width, height) term.clear() term.setCursorPos(1, 1) print('This image needs monitors to be calibrated before being displayed.') print('Please right-click each monitor in order, from the top left corner') print('to the bottom right corner, going right first, then down.') local monitors = {} local names = {} for y = 1, height do monitors[y] = {} for x = 1, width do local _, oy = term.getCursorPos() -- 绘制校准界面 for ly = 1, height do term.setCursorPos(3, oy + ly - 1) term.clearLine() for lx = 1, width do term.blit('\x8F ', (lx == x and ly == y) and '00' or '77', 'ff') end end term.setCursorPos(3, oy + height) term.write('(' .. x .. ', ' .. y .. ')') term.setCursorPos(1, oy) -- 等待用户点击显示器 repeat local _, name = os.pullEvent('monitor_touch') monitors[y][x] = name until not names[name] names[monitors[y][x]] = true sleep(0.25) end end settings.set('sanjuuni.multimonitor', monitors) settings.save() print('Calibration complete. Settings have been saved for future use.') return monitors end -- 播放图像/动画 function bimg.play(source, options) options = options or {} local termObj = options.terminal or term local loop = options.loop or false local isURL = options.isURL or false -- 停止当前播放 bimg.stop() -- 加载图像数据 local img if type(source) == "string" then if isURL then img = bimg.loadFromURL(source) else img = bimg.loadFromFile(source) end else img = loadImageData(source) end -- 设置运行标志 running = true currentAnimation = { img = img, term = termObj, running = true, loop = loop } local function playFrames() repeat if img.multiMonitor then local width, height = img.multiMonitor.width, img.multiMonitor.height local monitors = settings.get('sanjuuni.multimonitor') -- 如果需要校准显示器 if not monitors or #monitors < height or #monitors[1] < width then monitors = calibrateMonitors(width, height) end -- 播放多显示器动画 for i = 1, #img, width * height do if not currentAnimation or not currentAnimation.running then break end -- 在所有显示器上绘制当前帧 for y = 1, height do for x = 1, width do local frameIndex = i + (y-1) * width + x-1 if frameIndex <= #img then local monitor = peripheral.wrap(monitors[y][x]) if monitor then drawFrame(img[frameIndex], monitor) end end end end -- 等待帧间隔 if img.animation then sleep(img[i].duration or img.secondsPerFrame or 0.05) else break end end else -- 单显示器播放 termObj.clear() -- 播放每一帧 for _, frame in ipairs(img) do if not currentAnimation or not currentAnimation.running then break end drawFrame(frame, termObj) -- 等待帧间隔 if img.animation then sleep(frame.duration or img.secondsPerFrame or 0.05) else break end end end -- 如果不是循环播放,则退出 if not currentAnimation or not currentAnimation.loop then break end until not currentAnimation or not currentAnimation.running end -- 播放结束后的清理 local function cleanup() if currentAnimation and currentAnimation.running then local termObj = currentAnimation.term -- 重置终端状态 termObj.setBackgroundColor(colors.black) termObj.setTextColor(colors.white) termObj.clear() termObj.setCursorPos(1, 1) -- 重置调色板 for i = 0, 15 do termObj.setPaletteColor(2^i, term.nativePaletteColor(2^i)) end end running = false currentAnimation = nil end -- 在新线程中播放 parallel.waitForAny( function() local success, err = pcall(playFrames) if not success then printError("Error playing animation: " .. err) end cleanup() end, function() while currentAnimation and currentAnimation.running do os.pullEvent("bimg_stop") currentAnimation.running = false end end ) end -- 停止播放 function bimg.stop() if currentAnimation then currentAnimation.running = false os.queueEvent("bimg_stop") end running = false end -- 检查是否正在播放 function bimg.isPlaying() return running end -- 获取当前播放状态 function bimg.getStatus() if not currentAnimation then return { playing = false, looping = false, frameCount = 0, currentFrame = 0 } end return { playing = currentAnimation.running, looping = currentAnimation.loop, frameCount = #currentAnimation.img, currentFrame = currentAnimation.currentFrame or 0 } end return bimg