mirror of
https://github.com/IgorTimofeev/MineOS.git
synced 2025-12-21 03:29:22 +01:00
405 lines
13 KiB
Lua
405 lines
13 KiB
Lua
local fs = require("filesystem")
|
||
local unicode = require("unicode")
|
||
local gpu = require("component").gpu
|
||
|
||
local image = {}
|
||
|
||
local transparentSymbol = "#"
|
||
|
||
--------------------Все, что касается сжатого формата изображений (у нас он назван "JPG")----------------------------------------------------------------------------------
|
||
|
||
-- OC image format .ocif by Pirnogion
|
||
-- Спасибо, Пир
|
||
-- Охуенный форматик
|
||
local ocif_signature1 = 0x896F6369
|
||
local ocif_signature2 = 0x00661A0A --7 bytes: 89 6F 63 69 66 1A 0A
|
||
local ocif_signature_expand = { string.char(0x89), string.char(0x6F), string.char(0x63), string.char(0x69), string.char(0x66), string.char(0x1A), string.char(0x0A) }
|
||
|
||
local BYTE = 8
|
||
local NULL_CHAR = 0
|
||
|
||
local imageAPI = {}
|
||
|
||
local function readBytes(file, bytes)
|
||
local readedByte = 0
|
||
local readedNumber = 0
|
||
for i = bytes, 1, -1 do
|
||
readedByte = string.byte( file:read(1) or NULL_CHAR )
|
||
readedNumber = readedNumber + bit32.lshift(readedByte, i*8-8)
|
||
end
|
||
|
||
return readedNumber
|
||
end
|
||
|
||
local function HEXtoRGB(color)
|
||
local rr = bit32.rshift( color, 16 )
|
||
local gg = bit32.rshift( bit32.band(color, 0x00ff00), 8 )
|
||
local bb = bit32.band(color, 0x0000ff)
|
||
|
||
return rr, gg, bb
|
||
end
|
||
|
||
local function encodePixel(hexcolor_fg, hexcolor_bg, char)
|
||
local rr_fg, gg_fg, bb_fg = HEXtoRGB( hexcolor_fg )
|
||
local rr_bg, gg_bg, bb_bg = HEXtoRGB( hexcolor_bg )
|
||
local ascii_char1, ascii_char2 = string.byte( char, 1, 2 )
|
||
|
||
ascii_char1 = ascii_char1 or NULL_CHAR
|
||
ascii_char2 = ascii_char2 or NULL_CHAR
|
||
|
||
return rr_fg, gg_fg, bb_fg, rr_bg, gg_bg, bb_bg, ascii_char1, ascii_char2
|
||
end
|
||
|
||
local function decodeChar(char1, char2)
|
||
if ( char1 ~= 0 and char2 ~= 0 ) then
|
||
return string.char( char1, char2 )
|
||
elseif ( char1 ~= 0) then
|
||
return string.char( char1 )
|
||
elseif ( char2 ~= 0 ) then
|
||
return string.char( char2 )
|
||
end
|
||
end
|
||
|
||
--Конвертируем массив классического "сырого" формата в сжатый и оптимизированный
|
||
function image.convertImagetoGroupedImage(PNGMassiv)
|
||
local newPNGMassiv = { ["backgrounds"] = {} }
|
||
|
||
--Перебираем весь массив стандартного PNG-вида по высоте
|
||
for j = 1, #PNGMassiv do
|
||
for i = 1, #PNGMassiv[j] do
|
||
local back = PNGMassiv[j][i][1]
|
||
local fore = PNGMassiv[j][i][2]
|
||
local symbol = PNGMassiv[j][i][3]
|
||
|
||
newPNGMassiv["backgrounds"][back] = newPNGMassiv["backgrounds"][back] or {}
|
||
newPNGMassiv["backgrounds"][back][fore] = newPNGMassiv["backgrounds"][back][fore] or {}
|
||
|
||
table.insert(newPNGMassiv["backgrounds"][back][fore], {i, j, symbol} )
|
||
|
||
back, fore, symbol = nil, nil, nil
|
||
end
|
||
end
|
||
|
||
return newPNGMassiv
|
||
end
|
||
|
||
--Чтение сжатого формата
|
||
local function loadJPG(path)
|
||
local kartinka = {}
|
||
local file = io.open(path, "rb")
|
||
|
||
local signature1, signature2 = readBytes(file, 4), readBytes(file, 3)
|
||
if ( signature1 ~= ocif_signature1 or signature2 ~= ocif_signature2 ) then
|
||
file:close()
|
||
return nil
|
||
end
|
||
|
||
kartinka.width = readBytes(file, 1)
|
||
kartinka.height = readBytes(file, 1)
|
||
kartinka.depth = readBytes(file, 1)
|
||
|
||
for y = 1, kartinka.height, 1 do
|
||
table.insert( kartinka, {} )
|
||
for x = 1, kartinka.width, 1 do
|
||
table.insert( kartinka[y], {} )
|
||
kartinka[y][x][2] = readBytes(file, 3)
|
||
kartinka[y][x][1] = readBytes(file, 3)
|
||
kartinka[y][x][3] = decodeChar(readBytes(file, 1), readBytes(file, 1))
|
||
end
|
||
end
|
||
|
||
file:close()
|
||
|
||
return kartinka
|
||
end
|
||
|
||
--Рисование сжатого формата
|
||
function image.drawJPG(x, y, image1)
|
||
x = x - 1
|
||
y = y - 1
|
||
|
||
local image2 = image.convertImageToGroupedImage(image1)
|
||
|
||
--Перебираем массив с фонами
|
||
for back, backValue in pairs(image2["backgrounds"]) do
|
||
gpu.setBackground(back)
|
||
for fore, foreValue in pairs(image2["backgrounds"][back]) do
|
||
gpu.setForeground(fore)
|
||
for pixel = 1, #image2["backgrounds"][back][fore] do
|
||
if image2["backgrounds"][back][fore][pixel][3] ~= transparentSymbol then
|
||
gpu.set(x + image2["backgrounds"][back][fore][pixel][1], y + image2["backgrounds"][back][fore][pixel][2], image2["backgrounds"][back][fore][pixel][3])
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
--Сохранение JPG в файл из существующего массива
|
||
function image.saveJPG(path, kartinka)
|
||
-- Удаляем файл, если есть
|
||
-- И делаем папку к нему
|
||
fs.remove(path)
|
||
fs.makeDirectory(fs.path(path))
|
||
|
||
local file = io.open(path, "w")
|
||
|
||
file:write( table.unpack(ocif_signature_expand) )
|
||
file:write( string.char( kartinka.width ) )
|
||
file:write( string.char( kartinka.height ) )
|
||
file:write( string.char( kartinka.depth ) )
|
||
|
||
for y = 1, kartinka.height do
|
||
for x = 1, kartinka.width do
|
||
local encodedPixel = { encodePixel( kartinka[y][x][2], kartinka[y][x][1], kartinka[y][x][3] ) }
|
||
for i = 1, #encodedPixel do
|
||
file:write( string.char( encodedPixel[i] ) )
|
||
end
|
||
encodedPixel = {nil, nil, nil}; encodedPixel = nil
|
||
end
|
||
end
|
||
|
||
file:close()
|
||
end
|
||
|
||
---------------------------Все, что касается несжатого формата (у нас он назван "PNG")-------------------------------------------------------
|
||
|
||
-- Перевод HEX-цвета из файла (из 00ff00 делает 0x00ff00)
|
||
local function HEXtoSTRING(color,withNull)
|
||
local stro4ka = string.format("%x",color)
|
||
local sStro4ka = unicode.len(stro4ka)
|
||
|
||
if sStro4ka < 6 then
|
||
stro4ka = string.rep("0", 6 - sStro4ka) .. stro4ka
|
||
end
|
||
|
||
if withNull then return "0x"..stro4ka else return stro4ka end
|
||
end
|
||
|
||
--Загрузка ПНГ
|
||
local function loadPNG(path)
|
||
local file = io.open(path, "r")
|
||
local newPNGMassiv = {}
|
||
|
||
local pixelCounter, lineCounter, dlinaStroki = 1, 1, nil
|
||
for line in file:lines() do
|
||
--Получаем длину строки
|
||
dlinaStroki = unicode.len(line)
|
||
--Сбрасываем счетчик пикселей
|
||
pixelCounter = 1
|
||
--Создаем новую строку
|
||
newPNGMassiv[lineCounter] = {}
|
||
--Перебираем пиксели
|
||
for i = 1, dlinaStroki, 16 do
|
||
--Транслируем всю хуйню в более понятную хуйню
|
||
local back = tonumber("0x"..unicode.sub(line, i, i + 5))
|
||
local fore = tonumber("0x"..unicode.sub(line, i + 7, i + 12))
|
||
local symbol = unicode.sub(line, i + 14, i + 14)
|
||
--Создаем новый пиксельс
|
||
newPNGMassiv[lineCounter][pixelCounter] = { back, fore, symbol }
|
||
--Увеличиваем пиксельсы
|
||
pixelCounter = pixelCounter + 1
|
||
--Очищаем оперативку
|
||
back, fore, symbol = nil, nil, nil
|
||
end
|
||
|
||
lineCounter = lineCounter + 1
|
||
end
|
||
|
||
--Закрываем файл
|
||
file:close()
|
||
--Очищаем оперативку
|
||
pixelCounter, lineCounter, dlinaStroki = nil, nil, nil
|
||
|
||
return newPNGMassiv
|
||
end
|
||
|
||
-- Сохранение существующего массива ПНГ в файл
|
||
function image.savePNG(path, MasterPixels)
|
||
-- Удаляем файл, если есть
|
||
-- И делаем папку к нему
|
||
fs.remove(path)
|
||
fs.makeDirectory(fs.path(path))
|
||
local f = io.open(path, "w")
|
||
|
||
for j=1, #MasterPixels do
|
||
for i=1,#MasterPixels[j] do
|
||
f:write(HEXtoSTRING(MasterPixels[j][i][1])," ",HEXtoSTRING(MasterPixels[j][i][2])," ",MasterPixels[j][i][3]," ")
|
||
end
|
||
f:write("\n")
|
||
end
|
||
|
||
f:close()
|
||
end
|
||
|
||
--Отрисовка ПНГ
|
||
function image.drawPNG(x, y, massivSudaPihay2)
|
||
--Уменьшаем значения кординат на 1, т.к. циклы начинаются с единицы
|
||
x = x - 1
|
||
y = y - 1
|
||
|
||
--Конвертируем "сырой" формат PNG в оптимизированный и сгруппированный по цветам
|
||
local massivSudaPihay = image.convertImageToGroupedImage(massivSudaPihay2)
|
||
|
||
--Перебираем массив с фонами
|
||
for back, backValue in pairs(massivSudaPihay["backgrounds"]) do
|
||
gpu.setBackground(back)
|
||
for fore, foreValue in pairs(massivSudaPihay["backgrounds"][back]) do
|
||
gpu.setForeground(fore)
|
||
for pixel = 1, #massivSudaPihay["backgrounds"][back][fore] do
|
||
if massivSudaPihay["backgrounds"][back][fore][pixel][3] ~= transparentSymbol then
|
||
gpu.set(x + massivSudaPihay["backgrounds"][back][fore][pixel][1], y + massivSudaPihay["backgrounds"][back][fore][pixel][2], massivSudaPihay["backgrounds"][back][fore][pixel][3])
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
---------------------Глобальные функции данного API, с ними мы и работаем---------------------------------------------------------
|
||
|
||
--Конвертируем массив классического "сырого" формата в сжатый и оптимизированный для более быстрой отрисовки
|
||
function image.convertImageToGroupedImage(PNGMassiv)
|
||
local newPNGMassiv = { ["backgrounds"] = {} }
|
||
--Перебираем весь массив стандартного PNG-вида по высоте
|
||
for j = 1, #PNGMassiv do
|
||
for i = 1, #PNGMassiv[j] do
|
||
newPNGMassiv["backgrounds"][PNGMassiv[j][i][1]] = newPNGMassiv["backgrounds"][PNGMassiv[j][i][1]] or {}
|
||
newPNGMassiv["backgrounds"][PNGMassiv[j][i][1]][PNGMassiv[j][i][2]] = newPNGMassiv["backgrounds"][PNGMassiv[j][i][1]][PNGMassiv[j][i][2]] or {}
|
||
table.insert(newPNGMassiv["backgrounds"][PNGMassiv[j][i][1]][PNGMassiv[j][i][2]], {i, j, PNGMassiv[j][i][3]} )
|
||
end
|
||
end
|
||
return newPNGMassiv
|
||
end
|
||
|
||
--Конвертер из PNG в JPG
|
||
function image.PNGtoJPG(PNGMassiv)
|
||
local JPGMassiv = PNGMassiv
|
||
local width, height = #PNGMassiv[1][1], #PNGMassiv[1]
|
||
|
||
JPGMassiv.width = width
|
||
JPGMassiv.height = height
|
||
JPGMassiv.depth = 8
|
||
|
||
return JPGMassiv
|
||
end
|
||
|
||
-- Просканировать файловую систему на наличие .PNG
|
||
-- И сохранить рядом с ними аналогичную копию в формате .JPG
|
||
-- Осторожно, функция для дебага и знающих людей
|
||
-- С кривыми ручками сюда не лезь
|
||
function image.convertAllPNGtoJPG(path)
|
||
local list = ecs.getFileList(path)
|
||
for key, file in pairs(list) do
|
||
if fs.isDirectory(path.."/"..file) then
|
||
image.convertAllPNGtoJPG(path.."/"..file)
|
||
else
|
||
if ecs.getFileFormat(file) == ".png" or ecs.getFileFormat(file) == ".PNG" then
|
||
print("Найден .PNG в директории \""..path.."/"..file.."\"")
|
||
print("Загружаю этот файл...")
|
||
PNGFile = loadPNG(path.."/"..file)
|
||
print("Загрузка завершена!")
|
||
print("Конвертация в JPG начата...")
|
||
JPGFile = image.PNGtoJPG(PNGFile)
|
||
print("Ковертация завершена!")
|
||
print("Сохраняю .JPG в той же папке...")
|
||
image.saveJPG(path.."/"..ecs.hideFileFormat(file)..".jpg", JPGFile)
|
||
print("Сохранение завершено!")
|
||
print(" ")
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
---------------------------------------------------------------------------------------------------------------------
|
||
|
||
--Загрузка любого изображения из доступных типов
|
||
function image.load(path)
|
||
local kartinka = {}
|
||
local fileFormat = ecs.getFileFormat(path)
|
||
if string.lower(fileFormat) == ".jpg" then
|
||
kartinka["format"] = ".jpg"
|
||
kartinka["image"] = loadJPG(path)
|
||
elseif string.lower(fileFormat) == ".png" then
|
||
kartinka["format"] = ".png"
|
||
kartinka["image"] = loadPNG(path)
|
||
else
|
||
error("Wrong file format! (not .png or .jpg)")
|
||
end
|
||
return kartinka
|
||
end
|
||
|
||
--Сохранение любого формата в нужном месте
|
||
function image.save(path, kartinka)
|
||
local fileFormat = ecs.getFileFormat(path)
|
||
|
||
if string.lower(fileFormat) == ".jpg" then
|
||
image.saveJPG(path, kartinka)
|
||
elseif string.lower(fileFormat) == ".png" then
|
||
image.savePNG(path, kartinka)
|
||
else
|
||
error("Wrong file format! (not .png or .jpg)")
|
||
end
|
||
end
|
||
|
||
--Отрисовка этого изображения
|
||
function image.draw(x, y, kartinka)
|
||
if kartinka.format == ".jpg" then
|
||
image.drawJPG(x, y, kartinka["image"])
|
||
elseif kartinka.format == ".png" then
|
||
image.drawPNG(x, y, kartinka["image"])
|
||
end
|
||
end
|
||
|
||
function image.screenshot(path)
|
||
--Вычисляем размер скрина
|
||
local xSize, ySize = gpu.getResolution()
|
||
|
||
local rawImage = {}
|
||
for y = 1, ySize do
|
||
rawImage[y] = {}
|
||
for x = 1, xSize do
|
||
local symbol, fore, back = gpu.get(x, y)
|
||
rawImage[y][x] = { back, fore, symbol }
|
||
symbol, fore, back = nil, nil, nil
|
||
end
|
||
end
|
||
|
||
rawImage.width = #rawImage[1]
|
||
rawImage.height = #rawImage
|
||
rawImage.depth = 8
|
||
|
||
image.save(path, rawImage)
|
||
end
|
||
|
||
---------------------------------------------------------------------------------------------------------------------
|
||
|
||
-- ecs.prepareToExit()
|
||
-- for i = 1, 30 do
|
||
-- print("Hello world bitches! " .. string.rep(tostring(math.random(100, 1000)) .. " ", 10))
|
||
-- end
|
||
|
||
-- image.draw(10, 2, image.load("System/OS/Icons/Love.png"))
|
||
|
||
-- local pathToScreen = "screenshot.jpg"
|
||
|
||
-- image.screenshot(pathToScreen)
|
||
-- ecs.prepareToExit()
|
||
-- ecs.centerText("xy", 0, "Сохранил скрин. Ща загружу фотку и нарисую его. Внимание!")
|
||
-- os.sleep(2)
|
||
-- ecs.prepareToExit()
|
||
-- image.draw(2, 2, image.load(pathToScreen))
|
||
|
||
return image
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|