MineOS/lib/image.lua
2015-09-05 10:00:34 +03:00

405 lines
13 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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