Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ---------------------------------------- OpenComputers Image Format (OCIF) -----------------------------------------------------------
- --[[
- Автор: Pornogion
- VK: https://vk.com/id88323331
- Соавтор: IT
- VK: https://vk.com/id7799889
- ]]
- local serialization = {}
- -- Important: pretty formatting will allow presenting non-serializable values
- -- but may generate output that cannot be unserialized back.
- function serialization.serialize(value, pretty)
- local kw = {["and"]=true, ["break"]=true, ["do"]=true, ["else"]=true,
- ["elseif"]=true, ["end"]=true, ["false"]=true, ["for"]=true,
- ["function"]=true, ["goto"]=true, ["if"]=true, ["in"]=true,
- ["local"]=true, ["nil"]=true, ["not"]=true, ["or"]=true,
- ["repeat"]=true, ["return"]=true, ["then"]=true, ["true"]=true,
- ["until"]=true, ["while"]=true}
- local id = "^[%a_][%w_]*$"
- local ts = {}
- local function s(v, l)
- local t = type(v)
- if t == "nil" then
- return "nil"
- elseif t == "boolean" then
- return v and "true" or "false"
- elseif t == "number" then
- if v ~= v then
- return "0/0"
- elseif v == math.huge then
- return "math.huge"
- elseif v == -math.huge then
- return "-math.huge"
- else
- return tostring(v)
- end
- elseif t == "string" then
- return string.format("%q", v):gsub("\\\n","\\n")
- elseif t == "table" and pretty and getmetatable(v) and getmetatable(v).__tostring then
- return tostring(v)
- elseif t == "table" then
- if ts[v] then
- if pretty then
- return "recursion"
- else
- error("tables with cycles are not supported")
- end
- end
- ts[v] = true
- local i, r = 1, nil
- local f
- if pretty then
- local ks, sks, oks = {}, {}, {}
- for k in pairs(v) do
- if type(k) == "number" then
- table.insert(ks, k)
- elseif type(k) == "string" then
- table.insert(sks, k)
- else
- table.insert(oks, k)
- end
- end
- table.sort(ks)
- table.sort(sks)
- for _, k in ipairs(sks) do
- table.insert(ks, k)
- end
- for _, k in ipairs(oks) do
- table.insert(ks, k)
- end
- local n = 0
- f = table.pack(function()
- n = n + 1
- local k = ks[n]
- if k ~= nil then
- return k, v[k]
- else
- return nil
- end
- end)
- else
- f = table.pack(pairs(v))
- end
- for k, v in table.unpack(f) do
- if r then
- r = r .. "," .. (pretty and ("\n" .. string.rep(" ", l)) or "")
- else
- r = "{"
- end
- local tk = type(k)
- if tk == "number" and k == i then
- i = i + 1
- r = r .. s(v, l + 1)
- else
- if tk == "string" and not kw[k] and string.match(k, id) then
- r = r .. k
- else
- r = r .. "[" .. s(k, l + 1) .. "]"
- end
- r = r .. "=" .. s(v, l + 1)
- end
- end
- ts[v] = nil -- allow writing same table more than once
- return (r or "{") .. "}"
- else
- if pretty then
- return tostring(t)
- else
- error("unsupported type: " .. t)
- end
- end
- end
- local result = s(value, 1)
- local limit = type(pretty) == "number" and pretty or 10
- if pretty then
- local truncate = 0
- while limit > 0 and truncate do
- truncate = string.find(result, "\n", truncate + 1, true)
- limit = limit - 1
- end
- if truncate then
- return result:sub(1, truncate) .. "..."
- end
- end
- return result
- end
- function serialization.unserialize(data)
- checkArg(1, data, "string")
- local result, reason = load("return " .. data, "=data", _, {math={huge=math.huge}})
- if not result then
- return nil, reason
- end
- local ok, output = pcall(result)
- if not ok then
- return nil, output
- end
- return output
- end
- local colorlib = {}
- local serialization = require("serialization")
- local function isNan(x)
- return x~=x
- end
- --RGB model
- function colorlib.HEXtoRGB(color)
- color = math.floor(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
- function colorlib.RGBtoHEX(rr, gg, bb)
- return bit32.lshift(rr, 16) + bit32.lshift(gg, 8) + bb
- end
- --HSB model
- function colorlib.RGBtoHSB(rr, gg, bb)
- local max = math.max(rr, math.max(gg, bb))
- local min = math.min(rr, math.min(gg, bb))
- local delta = max - min
- local h = 0
- if ( max == rr and gg >= bb) then h = 60*(gg-bb)/delta end
- if ( max == rr and gg <= bb ) then h = 60*(gg-bb)/delta + 360 end
- if ( max == gg ) then h = 60*(bb-rr)/delta + 120 end
- if ( max == bb ) then h = 60*(rr-gg)/delta + 240 end
- local s = 0
- if ( max ~= 0 ) then s = 1-(min/max) end
- local b = max*100/255
- if isNan(h) then h = 0 end
- return h, s*100, b
- end
- function colorlib.HSBtoRGB(h, s, v)
- if h > 359 then h = 0 end
- local rr, gg, bb = 0, 0, 0
- local const = 255
- s = s/100
- v = v/100
- local i = math.floor(h/60)
- local f = h/60 - i
- local p = v*(1-s)
- local q = v*(1-s*f)
- local t = v*(1-(1-f)*s)
- if ( i == 0 ) then rr, gg, bb = v, t, p end
- if ( i == 1 ) then rr, gg, bb = q, v, p end
- if ( i == 2 ) then rr, gg, bb = p, v, t end
- if ( i == 3 ) then rr, gg, bb = p, q, v end
- if ( i == 4 ) then rr, gg, bb = t, p, v end
- if ( i == 5 ) then rr, gg, bb = v, p, q end
- return math.floor(rr * const), math.floor(gg * const), math.floor(bb * const)
- end
- function colorlib.HEXtoHSB(color)
- local rr, gg, bb = colorlib.HEXtoRGB(color)
- local h, s, b = colorlib.RGBtoHSB( rr, gg, bb )
- return h, s, b
- end
- function colorlib.HSBtoHEX(h, s, b)
- local rr, gg, bb = colorlib.HSBtoRGB(h, s, b)
- local color = colorlib.RGBtoHEX(rr, gg, bb)
- return color
- end
- --Смешивание двух цветов на основе альфа-канала второго
- function colorlib.alphaBlend(firstColor, secondColor, alphaChannel)
- local invertedAlphaChannelDividedBy255 = (255 - alphaChannel) / 255
- alphaChannel = alphaChannel / 255
- local firstColorRed, firstColorGreen, firstColorBlue = colorlib.HEXtoRGB(firstColor)
- local secondColorRed, secondColorGreen, secondColorBlue = colorlib.HEXtoRGB(secondColor)
- return colorlib.RGBtoHEX(
- secondColorRed * invertedAlphaChannelDividedBy255 + firstColorRed * alphaChannel,
- secondColorGreen * invertedAlphaChannelDividedBy255 + firstColorGreen * alphaChannel,
- secondColorBlue * invertedAlphaChannelDividedBy255 + firstColorBlue * alphaChannel
- )
- end
- --Получение среднего цвета между перечисленными. К примеру, между черным и белым выдаст серый.
- function colorlib.getAverageColor(...)
- local colors = {...}
- local averageRed, averageGreen, averageBlue = 0, 0, 0
- for i = 1, #colors do
- local r, g, b = colorlib.HEXtoRGB(colors[i])
- averageRed, averageGreen, averageBlue = averageRed + r, averageGreen + g, averageBlue + b
- end
- return colorlib.RGBtoHEX(math.floor(averageRed / #colors), math.floor(averageGreen / #colors), math.floor(averageBlue / #colors))
- end
- -----------------------------------------------------------------------------------------------------------------------
- local palette = {
- 0x000000, 0x000040, 0x000080, 0x0000BF, 0x0000FF, 0x002400, 0x002440, 0x002480, 0x0024BF, 0x0024FF, 0x004900, 0x004940, 0x004980, 0x0049BF, 0x0049FF, 0x006D00,
- 0x006D40, 0x006D80, 0x006DBF, 0x006DFF, 0x009200, 0x009240, 0x009280, 0x0092BF, 0x0092FF, 0x00B600, 0x00B640, 0x00B680, 0x00B6BF, 0x00B6FF, 0x00DB00, 0x00DB40,
- 0x00DB80, 0x00DBBF, 0x00DBFF, 0x00FF00, 0x00FF40, 0x00FF80, 0x00FFBF, 0x00FFFF, 0x0F0F0F, 0x1E1E1E, 0x2D2D2D, 0x330000, 0x330040, 0x330080, 0x3300BF, 0x3300FF,
- 0x332400, 0x332440, 0x332480, 0x3324BF, 0x3324FF, 0x334900, 0x334940, 0x334980, 0x3349BF, 0x3349FF, 0x336D00, 0x336D40, 0x336D80, 0x336DBF, 0x336DFF, 0x339200,
- 0x339240, 0x339280, 0x3392BF, 0x3392FF, 0x33B600, 0x33B640, 0x33B680, 0x33B6BF, 0x33B6FF, 0x33DB00, 0x33DB40, 0x33DB80, 0x33DBBF, 0x33DBFF, 0x33FF00, 0x33FF40,
- 0x33FF80, 0x33FFBF, 0x33FFFF, 0x3C3C3C, 0x4B4B4B, 0x5A5A5A, 0x660000, 0x660040, 0x660080, 0x6600BF, 0x6600FF, 0x662400, 0x662440, 0x662480, 0x6624BF, 0x6624FF,
- 0x664900, 0x664940, 0x664980, 0x6649BF, 0x6649FF, 0x666D00, 0x666D40, 0x666D80, 0x666DBF, 0x666DFF, 0x669200, 0x669240, 0x669280, 0x6692BF, 0x6692FF, 0x66B600,
- 0x66B640, 0x66B680, 0x66B6BF, 0x66B6FF, 0x66DB00, 0x66DB40, 0x66DB80, 0x66DBBF, 0x66DBFF, 0x66FF00, 0x66FF40, 0x66FF80, 0x66FFBF, 0x66FFFF, 0x696969, 0x787878,
- 0x878787, 0x969696, 0x990000, 0x990040, 0x990080, 0x9900BF, 0x9900FF, 0x992400, 0x992440, 0x992480, 0x9924BF, 0x9924FF, 0x994900, 0x994940, 0x994980, 0x9949BF,
- 0x9949FF, 0x996D00, 0x996D40, 0x996D80, 0x996DBF, 0x996DFF, 0x999200, 0x999240, 0x999280, 0x9992BF, 0x9992FF, 0x99B600, 0x99B640, 0x99B680, 0x99B6BF, 0x99B6FF,
- 0x99DB00, 0x99DB40, 0x99DB80, 0x99DBBF, 0x99DBFF, 0x99FF00, 0x99FF40, 0x99FF80, 0x99FFBF, 0x99FFFF, 0xA5A5A5, 0xB4B4B4, 0xC3C3C3, 0xCC0000, 0xCC0040, 0xCC0080,
- 0xCC00BF, 0xCC00FF, 0xCC2400, 0xCC2440, 0xCC2480, 0xCC24BF, 0xCC24FF, 0xCC4900, 0xCC4940, 0xCC4980, 0xCC49BF, 0xCC49FF, 0xCC6D00, 0xCC6D40, 0xCC6D80, 0xCC6DBF,
- 0xCC6DFF, 0xCC9200, 0xCC9240, 0xCC9280, 0xCC92BF, 0xCC92FF, 0xCCB600, 0xCCB640, 0xCCB680, 0xCCB6BF, 0xCCB6FF, 0xCCDB00, 0xCCDB40, 0xCCDB80, 0xCCDBBF, 0xCCDBFF,
- 0xCCFF00, 0xCCFF40, 0xCCFF80, 0xCCFFBF, 0xCCFFFF, 0xD2D2D2, 0xE1E1E1, 0xF0F0F0, 0xFF0000, 0xFF0040, 0xFF0080, 0xFF00BF, 0xFF00FF, 0xFF2400, 0xFF2440, 0xFF2480,
- 0xFF24BF, 0xFF24FF, 0xFF4900, 0xFF4940, 0xFF4980, 0xFF49BF, 0xFF49FF, 0xFF6D00, 0xFF6D40, 0xFF6D80, 0xFF6DBF, 0xFF6DFF, 0xFF9200, 0xFF9240, 0xFF9280, 0xFF92BF,
- 0xFF92FF, 0xFFB600, 0xFFB640, 0xFFB680, 0xFFB6BF, 0xFFB6FF, 0xFFDB00, 0xFFDB40, 0xFFDB80, 0xFFDBBF, 0xFFDBFF, 0xFFFF00, 0xFFFF40, 0xFFFF80, 0xFFFFBF, 0xFFFFFF,
- }
- function colorlib.convert24BitTo8Bit(hex24)
- local encodedIndex = nil
- local colorMatchFactor = nil
- local colorMatchFactor_min = math.huge
- local red24, green24, blue24 = colorlib.HEXtoRGB(hex24)
- for colorIndex, colorPalette in ipairs(palette) do
- local redPalette, greenPalette, bluePalette = colorlib.HEXtoRGB(colorPalette)
- colorMatchFactor = (redPalette-red24)^2 + (greenPalette-green24)^2 + (bluePalette-blue24)^2
- if (colorMatchFactor < colorMatchFactor_min) then
- encodedIndex = colorIndex
- colorMatchFactor_min = colorMatchFactor
- end
- end
- return encodedIndex - 1
- -- return searchClosestColor(1, #palette, hex24)
- end
- function colorlib.convert8BitTo24Bit(hex8)
- return palette[hex8 + 1]
- end
- function colorlib.debugColorCompression(color)
- local compressedColor = colorlib.convert24BitTo8Bit(color)
- local decompressedColor = colorlib.convert8BitTo24Bit(compressedColor)
- print("Исходный цвет: " .. string.format("0x%06X", color))
- print("Сжатый цвет: " .. string.format("0x%02X", compressedColor))
- print("Расжатый цвет: " .. string.format("0x%06X", decompressedColor))
- end
- -----------------------------------------------------------------------------------------------------------------------
- --------------------------------------- Подгрузка библиотек --------------------------------------------------------------
- -- Адаптивная загрузка необходимых библиотек и компонентов
- local libraries = {
- -- bit32 = "bit32",
- }
- --for library in pairs(libraries) do if not _G[library] then _G[library] = require(libraries[library]) end end; libraries = nil
- local unicode=require("unicode")
- local image = {}
- -------------------------------------------- Переменные -------------------------------------------------------------------
- --Константы программы
- local constants = {
- OCIFSignature = "OCIF",
- OCIF2Elements = {
- alphaStart = "A",
- symbolStart = "S",
- backgroundStart = "B",
- foregroundStart = "F",
- },
- elementCount = 4,
- byteSize = 8,
- nullChar = 0,
- rawImageLoadStep = 19,
- compressedFileFormat = ".pic",
- pngFileFormat = ".png",
- }
- ---------------------------------------- Локальные функции -------------------------------------------------------------------
- --Формула конвертации индекса массива изображения в абсолютные координаты пикселя изображения
- local function convertIndexToCoords(index, width)
- --Приводим индекс к корректному виду (1 = 1, 4 = 2, 7 = 3, 10 = 4, 13 = 5, ...)
- index = (index + constants.elementCount - 1) / constants.elementCount
- --Получаем остаток от деления индекса на ширину изображения
- local ostatok = index % width
- --Если остаток равен 0, то х равен ширине изображения, а если нет, то х равен остатку
- local x = (ostatok == 0) and width or ostatok
- --А теперь как два пальца получаем координату по Y
- local y = math.ceil(index / width)
- --Очищаем остаток из оперативки
- ostatok = nil
- --Возвращаем координаты
- return x, y
- end
- --Формула конвертации абсолютных координат пикселя изображения в индекс для массива изображения
- local function convertCoordsToIndex(x, y, width)
- return (width * (y - 1) + x) * constants.elementCount - constants.elementCount + 1
- end
- --Костыльное получение размера массива, ибо автор луа не позволяет
- --подсчитывать ненумерические индексы через #massiv
- --мда, мда
- --...
- --мда
- local function getArraySize(array)
- local size = 0
- for key in pairs(array) do
- size = size + 1
- end
- return size
- end
- --Получить количество байт, которое можно извлечь из указанного числа
- local function getCountOfBytes(number)
- if number == 0 or number == 1 then return 1 end
- return math.ceil(math.log(number, 256))
- end
- --Распидорасить число на составляющие байты
- local function extractBytesFromNumber(number, countOfBytesToExtract)
- local bytes = {}
- local byteCutter = 0xff
- for i = 1, countOfBytesToExtract do
- table.insert(bytes, 1, bit32.rshift(bit32.band(number, byteCutter), (i-1)*8))
- byteCutter = bit32.lshift(byteCutter, 8)
- end
- return table.unpack(bytes)
- end
- --Склеить байты и создать из них число
- local function mergeBytesToNumber(...)
- local bytes = {...}
- local finalNumber = bytes[1]
- for i = 2, #bytes do
- finalNumber = bit32.bor(bit32.lshift(finalNumber, 8), bytes[i])
- end
- return finalNumber
- end
- -- Сконвертировать все переданные байты в строку
- local function convertBytesToString(...)
- local bytes = {...}
- for i = 1, #bytes do
- bytes[i] = string.char(bytes[i])
- end
- return table.concat(bytes)
- end
- --Выделить бит-терминатор в первом байте UTF-8 символа: 1100 0010 --> 0010 0000
- local function selectTerminateBit_l()
- local prevByte = nil
- local prevTerminateBit = nil
- return function( byte )
- local x, terminateBit = nil
- if ( prevByte == byte ) then
- return prevTerminateBit
- end
- x = bit32.band( bit32.bnot(byte), 0x000000FF )
- x = bit32.bor( x, bit32.rshift(x, 1) )
- x = bit32.bor( x, bit32.rshift(x, 2) )
- x = bit32.bor( x, bit32.rshift(x, 4) )
- x = bit32.bor( x, bit32.rshift(x, 8) )
- x = bit32.bor( x, bit32.rshift(x, 16) )
- terminateBit = x - bit32.rshift(x, 1)
- prevByte = byte
- prevTerminateBit = terminateBit
- return terminateBit
- end
- end
- local selectTerminateBit = selectTerminateBit_l()
- --Прочитать n байтов из файла, возвращает прочитанные байты как число, если не удалось прочитать, то возвращает 0
- local function readBytes(file, count)
- local readedBytes = file:read(count)
- return mergeBytesToNumber(string.byte(readedBytes, 1, count))
- end
- --Подготавливает цвета и символ для записи в файл сжатого формата
- local function encodePixel(background, foreground, alpha, char)
- --Расхерачиваем жирные цвета в компактные цвета
- local ascii_background1, ascii_background2, ascii_background3 = colorlib.HEXtoRGB(background)
- local ascii_foreground1, ascii_foreground2, ascii_foreground3 = colorlib.HEXtoRGB(foreground)
- --Расхерачиваем жирный код юникод-символа в несколько миленьких ascii-кодов
- local ascii_char1, ascii_char2, ascii_char3, ascii_char4, ascii_char5, ascii_char6 = string.byte( char, 1, 6 )
- ascii_char1 = ascii_char1 or constants.nullChar
- --Возвращаем все расхераченное
- return ascii_background1, ascii_background2, ascii_background3, ascii_foreground1, ascii_foreground2, ascii_foreground3, alpha, ascii_char1, ascii_char2, ascii_char3, ascii_char4, ascii_char5, ascii_char6
- end
- --Декодирование UTF-8 символа
- local function decodeChar(file)
- local first_byte = readBytes(file, 1)
- local charcode_array = {first_byte}
- local len = 1
- local middle = selectTerminateBit(first_byte)
- if ( middle == 32 ) then
- len = 2
- elseif ( middle == 16 ) then
- len = 3
- elseif ( middle == 8 ) then
- len = 4
- elseif ( middle == 4 ) then
- len = 5
- elseif ( middle == 2 ) then
- len = 6
- end
- for i = 1, len-1 do
- table.insert( charcode_array, readBytes(file, 1) )
- end
- return string.char( table.unpack( charcode_array ) )
- end
- --Правильное конвертирование HEX-переменной в строковую
- local function HEXtoSTRING(color, bitCount, withNull)
- local stro4ka = string.format("%X",color)
- local sStro4ka = unicode.len(stro4ka)
- if sStro4ka < bitCount then
- stro4ka = string.rep("0", bitCount - sStro4ka) .. stro4ka
- end
- sStro4ka = nil
- if withNull then return "0x"..stro4ka else return stro4ka end
- end
- --Получение формата файла
- local function getFileFormat(path)
- local name = fs.name(path)
- local starting, ending = string.find(name, "(.)%.[%d%w]*$")
- if starting == nil then
- return nil
- else
- return unicode.sub(name, starting + 1, -1)
- end
- name, starting, ending = nil, nil, nil
- end
- --Прочесть сигнатуру файла и сравнить ее с константой
- local function readSignature(file)
- local readedSignature = file:read(4)
- if readedSignature ~= constants.OCIFSignature then
- file:close()
- error("Can't load file: wrong OCIF format signature (\""..readedSignature .. "\" ~= \"" ..constants.OCIFSignature .. "\")")
- end
- end
- --Записать сигнатуру в файл
- local function writeSignature(file)
- file:write(constants.OCIFSignature)
- end
- --Сжать все цвета в изображении в 8-битную палитру
- local function convertImageColorsTo8Bit(picture)
- for i = 1, #picture, 4 do
- picture[i] = colorlib.convert24BitTo8Bit(picture[i])
- picture[i + 1] = colorlib.convert24BitTo8Bit(picture[i + 1])
- if i % 505 == 0 then os.sleep(0) end
- end
- return picture
- end
- --Расжать все цвета в изображении в 24-битную палитру
- local function convertImageColorsTo24Bit(picture)
- for i = 1, #picture, 4 do
- picture[i] = colorlib.convert8BitTo24Bit(picture[i])
- picture[i + 1] = colorlib.convert8BitTo24Bit(picture[i + 1])
- if i % 505 == 0 then os.sleep(0) end
- end
- return picture
- end
- ------------------------------ Все, что касается формата OCIF1 ------------------------------------------------------------
- -- Запись в файл сжатого OCIF-формата изображения
- local function saveOCIF1(file, picture)
- local encodedPixel
- file:write( string.char( picture.width ) )
- file:write( string.char( picture.height ) )
- for i = 1, picture.width * picture.height * constants.elementCount, constants.elementCount do
- encodedPixel =
- {
- encodePixel(picture[i], picture[i + 1], picture[i + 2], picture[i + 3])
- }
- for j = 1, #encodedPixel do
- file:write( string.char( encodedPixel[j] ) )
- end
- end
- file:close()
- end
- --Чтение из файла сжатого OCIF-формата изображения, возвращает массив типа 2 (подробнее о типах см. конец файла)
- local function loadOCIF1(file)
- local picture = {}
- --Читаем ширину и высоту файла
- picture.width = readBytes(file, 1)
- picture.height = readBytes(file, 1)
- for i = 1, picture.width * picture.height do
- --Читаем бекграунд
- table.insert(picture, readBytes(file, 3))
- --Читаем форграунд
- table.insert(picture, readBytes(file, 3))
- --Читаем альфу
- table.insert(picture, readBytes(file, 1))
- --Читаем символ
- table.insert(picture, decodeChar( file ))
- end
- file:close()
- return picture
- end
- ------------------------------------------ Все, что касается формата OCIF2 ------------------------------------------------
- local function saveOCIF2(file, picture, compressColors)
- --Записываем ширину изображения
- file:write(string.char(picture.width))
- file:write(string.char(picture.height))
- --Группируем картинку
- local grouppedPucture = image.convertToGroupedImage(picture)
- --Перебираем все альфы
- for alpha in pairs(grouppedPucture) do
- --Получаем размер массива, содержащего символы
- local arraySize = getArraySize(grouppedPucture[alpha])
- local countOfBytesForArraySize = getCountOfBytes(arraySize)
- --Записываем в файл символ АльфаСтарта, размер массива альфы и само значение альфы
- file:write(
- constants.OCIF2Elements.alphaStart,
- string.char(countOfBytesForArraySize),
- convertBytesToString(extractBytesFromNumber(arraySize, countOfBytesForArraySize)),
- string.char(alpha)
- )
- for symbol in pairs(grouppedPucture[alpha]) do
- --Записываем заголовок
- file:write(constants.OCIF2Elements.symbolStart)
- --Записываем количество всех цветов текста и символ
- if compressColors then
- file:write(
- string.char(getArraySize(grouppedPucture[alpha][symbol])),
- convertBytesToString(string.byte(symbol, 1, 6))
- )
- else
- file:write(
- convertBytesToString(extractBytesFromNumber(getArraySize(grouppedPucture[alpha][symbol]), 3)),
- convertBytesToString(string.byte(symbol, 1, 6))
- )
- end
- for foreground in pairs(grouppedPucture[alpha][symbol]) do
- --Записываем заголовок
- file:write(constants.OCIF2Elements.foregroundStart)
- --Записываем количество цветов фона и цвет текста
- if compressColors then
- file:write(
- string.char(getArraySize(grouppedPucture[alpha][symbol][foreground])),
- string.char(foreground)
- )
- else
- file:write(
- convertBytesToString(extractBytesFromNumber(getArraySize(grouppedPucture[alpha][symbol][foreground]), 3)),
- convertBytesToString(extractBytesFromNumber(foreground, 3))
- )
- end
- for background in pairs(grouppedPucture[alpha][symbol][foreground]) do
- --Записываем заголовок и размер массива координат
- file:write(
- constants.OCIF2Elements.backgroundStart,
- convertBytesToString(extractBytesFromNumber(getArraySize(grouppedPucture[alpha][symbol][foreground][background]), 2))
- )
- --Записываем цвет фона
- if compressColors then
- file:write(string.char(background))
- else
- file:write(convertBytesToString(extractBytesFromNumber(background, 3)))
- end
- --Перебираем координаты
- for y in pairs(grouppedPucture[alpha][symbol][foreground][background]) do
- --Записываем заголовок координат, размер массива y и само значение y
- file:write(
- "Y",
- string.char(getArraySize(grouppedPucture[alpha][symbol][foreground][background][y])),
- string.char(y)
- )
- --Записываем ИКСЫЫЫ
- --Ы
- for i = 1, #grouppedPucture[alpha][symbol][foreground][background][y] do
- file:write(string.char(grouppedPucture[alpha][symbol][foreground][background][y][i]))
- end
- end
- end
- end
- end
- end
- file:close()
- end
- local function loadOCIF2(file, decompressColors, useOCIF4)
- local picture = {}
- --Читаем размер изображения
- local readedWidth = string.byte(file:read(1))
- local readedHeight = string.byte(file:read(1))
- picture.width = readedWidth
- picture.height = readedHeight
- local header, alpha, symbol, foreground, background, y, alphaSize, symbolSize, foregroundSize, backgroundSize, ySize = ""
- while true do
- header = file:read(1)
- if not header then break end
- -- print("----------------------")
- -- print("Заголовок: " .. header)
- if header == "A" then
- local countOfBytesForArraySize = string.byte(file:read(1))
- alphaSize = string.byte(file:read(countOfBytesForArraySize))
- alpha = string.byte(file:read(1))
- -- print("Количество байт под размер массива символов: " .. countOfBytesForArraySize)
- -- print("Размер массива символов: " .. alphaSize)
- -- print("Альфа: " .. alpha)
- elseif header == "S" then
- if decompressColors then
- symbolSize = string.byte(file:read(1))
- else
- symbolSize = mergeBytesToNumber(string.byte(file:read(3), 1, 3))
- end
- symbol = decodeChar(file)
- -- print("Размер массива цвета текста: " .. symbolSize)
- -- print("Символ: \"" .. symbol .. "\"")
- elseif header == "F" then
- if decompressColors then
- foregroundSize = string.byte(file:read(1))
- foreground = colorlib.convert8BitTo24Bit(string.byte(file:read(1)))
- else
- foregroundSize = mergeBytesToNumber(string.byte(file:read(3), 1, 3))
- foreground = mergeBytesToNumber(string.byte(file:read(3), 1, 3))
- end
- -- print("Размер массива цвета фона: " .. foregroundSize)
- -- print("Цвет текста: " .. foreground)
- elseif header == "B" then
- backgroundSize = mergeBytesToNumber(string.byte(file:read(2), 1, 2))
- if decompressColors then
- background = colorlib.convert8BitTo24Bit(string.byte(file:read(1)))
- else
- background = mergeBytesToNumber(string.byte(file:read(3), 1, 3))
- end
- -- print("Размер массива координат: " .. backgroundSize)
- -- print("Цвет фона: " .. background)
- --Поддержка загрузки формата OCIF3
- if not useOCIF4 then
- --Читаем координаты
- for i = 1, backgroundSize, 2 do
- local x = string.byte(file:read(1))
- local y = string.byte(file:read(1))
- local index = convertCoordsToIndex(x, y, readedWidth)
- -- print("Координата: " .. x .. "x" .. y .. ", индекс: "..index)
- picture[index] = background
- picture[index + 1] = foreground
- picture[index + 2] = alpha
- picture[index + 3] = symbol
- end
- end
- --Новый формат OCIF4
- elseif header == "Y" and useOCIF4 then
- ySize = string.byte(file:read(1))
- y = string.byte(file:read(1))
- -- print("Размер массива Y: " .. ySize)
- -- print("Текущий Y: " .. y)
- for i = 1, ySize do
- local x = string.byte(file:read(1))
- local index = convertCoordsToIndex(x, y, readedWidth)
- -- print("Координата: " .. x .. "x" .. y .. ", индекс: "..index)
- picture[index] = background
- picture[index + 1] = foreground
- picture[index + 2] = alpha
- picture[index + 3] = symbol
- end
- else
- error("Error while reading OCIF format: unknown Header type (" .. header .. ")")
- end
- end
- file:close()
- return picture
- end
- ------------------------------ Все, что касается формата RAW ------------------------------------------------------------
- --Сохранение в файл сырого формата изображения типа 2 (подробнее о типах см. конец файла)
- local function saveRaw(file, picture)
- file:write("\n")
- local xPos, yPos = 1, 1
- for i = 1, picture.width * picture.height * constants.elementCount, constants.elementCount do
- file:write( HEXtoSTRING(picture[i], 6), " ", HEXtoSTRING(picture[i + 1], 6), " ", HEXtoSTRING(picture[i + 2], 2), " ", picture[i + 3], " ")
- xPos = xPos + 1
- if xPos > picture.width then
- xPos = 1
- yPos = yPos + 1
- file:write("\n")
- end
- end
- file:close()
- end
- --Загрузка из файла сырого формата изображения типа 2 (подробнее о типах см. конец файла)
- local function loadRaw(file)
- --Читаем один байт "прост так"
- file:read(1)
- local picture = {}
- local background, foreground, alpha, symbol, sLine
- local lineCounter = 0
- for line in file:lines() do
- sLine = unicode.len(line)
- for i = 1, sLine, constants.rawImageLoadStep do
- background = "0x" .. unicode.sub(line, i, i + 5)
- foreground = "0x" .. unicode.sub(line, i + 7, i + 12)
- alpha = "0x" .. unicode.sub(line, i + 14, i + 15)
- symbol = unicode.sub(line, i + 17, i + 17)
- table.insert(picture, tonumber(background))
- table.insert(picture, tonumber(foreground))
- table.insert(picture, tonumber(alpha))
- table.insert(picture, symbol)
- end
- lineCounter = lineCounter + 1
- end
- picture.width = sLine / constants.rawImageLoadStep
- picture.height = lineCounter
- file:close()
- return picture
- end
- ----------------------------------- Вспомогательные функции программы ------------------------------------------------------------
- --Оптимизировать и сгруппировать по цветам картинку типа 2 (подробнее о типах см. конец файла)
- function image.convertToGroupedImage(picture)
- --Создаем массив оптимизированной картинки
- local optimizedPicture = {}
- --Задаем константы
- local xPos, yPos, background, foreground, alpha, symbol = 1, 1, nil, nil, nil, nil
- --Перебираем все элементы массива
- for i = 1, picture.width * picture.height * constants.elementCount, constants.elementCount do
- --Получаем символ из неоптимизированного массива
- background, foreground, alpha, symbol = picture[i], picture[i + 1], picture[i + 2], picture[i + 3]
- --Группируем картинку по цветам
- optimizedPicture[alpha] = optimizedPicture[alpha] or {}
- optimizedPicture[alpha][symbol] = optimizedPicture[alpha][symbol] or {}
- optimizedPicture[alpha][symbol][foreground] = optimizedPicture[alpha][symbol][foreground] or {}
- optimizedPicture[alpha][symbol][foreground][background] = optimizedPicture[alpha][symbol][foreground][background] or {}
- optimizedPicture[alpha][symbol][foreground][background][yPos] = optimizedPicture[alpha][symbol][foreground][background][yPos] or {}
- table.insert(optimizedPicture[alpha][symbol][foreground][background][yPos], xPos)
- --Если xPos достигает width изображения, то сбросить на 1, иначе xPos++
- xPos = (xPos == picture.width) and 1 or xPos + 1
- --Если xPos равняется 1, то yPos++, а если нет, то похуй
- yPos = (xPos == 1) and yPos + 1 or yPos
- end
- --Возвращаем оптимизированный массив
- return optimizedPicture
- end
- -- Функция оптимизации цвета текста и символов у картинки, уменьшает число GPU-операций при отрисовке из буфера
- -- Вызывается только при сохранении файла, так что на быстродействии не сказывается,
- -- а в целом штука очень и очень полезная. Фиксит криворукость художников.
- function image.optimize(picture)
- local i1, i2, i3 = 0, 0, 0
- for i = 1, #picture, constants.elementCount do
- --Уменьшаем нагрузку на ЦОПЕ
- i1, i2, i3 = i + 1, i + 2, i + 3
- --Если цвет фона равен цвету текста, и используется псевдографические полупиксели
- if picture[i] == picture[i1] and (picture[i3] == "▄" or picture[i3] == "▀") then
- picture[i3] = " "
- end
- --Если символ равен пролбелу, т.е. цвет текста не учитывается
- if picture[i3] == " " then
- picture[i1] = 0x000000
- end
- end
- return picture
- end
- --Получить пиксель из изображения по указанным координатам
- function image.get(picture, x, y)
- if x >= 1 and y >= 1 and x <= picture.width and y <= picture.height then
- local index = convertCoordsToIndex(x, y, picture.width)
- return picture[index], picture[index + 1], picture[index + 2], picture[index + 3]
- else
- return nil
- end
- end
- --Установить пиксель в изображении по указанным координатам
- function image.set(picture, x, y, background, foreground, alpha, symbol, debug)
- if x >= 1 and y >= 1 and x <= picture.width and y <= picture.height then
- local index = convertCoordsToIndex(x, y, picture.width)
- picture[index] = background or 0xFF00FF
- picture[index + 1] = foreground or 0xFF00FF
- picture[index + 2] = alpha or 0x00
- picture[index + 3] = symbol or " "
- return picture
- else
- error("Can't set pixel because it's located out of image coordinates: x = " .. x .. ", y = " .. y)
- end
- end
- ------------------------------------------ Методы трансформирования изображения ------------------------------------------------
- --Вставка ряда пикселей
- function image.insertRow(picture, y, rowArray)
- local index = convertCoordsToIndex(1, y, picture.width)
- for i = 1, #rowArray, 4 do
- table.insert(picture, index, rowArray[i + 3])
- table.insert(picture, index, rowArray[i + 2])
- table.insert(picture, index, rowArray[i + 1])
- table.insert(picture, index, rowArray[i])
- index = index + 4
- end
- picture.height = picture.height + 1
- return picture
- end
- function image.insertColumn(picture, x, columnArray)
- local index = convertCoordsToIndex(x, 1, picture.width)
- for i = 1, #columnArray, 4 do
- table.insert(picture, index, columnArray[i + 3])
- table.insert(picture, index, columnArray[i + 2])
- table.insert(picture, index, columnArray[i + 1])
- table.insert(picture, index, columnArray[i])
- index = index + picture.width * 4 + 4
- end
- picture.width = picture.width + 1
- return picture
- end
- --Удаление ряда пикселей
- function image.removeRow(picture, y)
- local index = convertCoordsToIndex(1, y, picture.width)
- for i = 1, picture.width * 4 do table.remove(picture, index) end
- picture.height = picture.height - 1
- return picture
- end
- --Удаление колонки пикселей
- function image.removeColumn(picture, x)
- local index = convertCoordsToIndex(x, 1, picture.width)
- for i = 1, picture.height do
- for j = 1, 4 do table.remove(picture, index) end
- index = index + (picture.width) * 4 - 4
- end
- picture.width = picture.width - 1
- return picture
- end
- --Получение ряда пикселей
- function image.getRow(picture, y)
- local row, background, foreground, alpha, symbol = {width = picture.width, height = 1}
- for x = 1, picture.width do
- background, foreground, alpha, symbol = image.get(picture, x, y)
- table.insert(row, background)
- table.insert(row, foreground)
- table.insert(row, alpha)
- table.insert(row, symbol)
- end
- return row
- end
- --Получение колонки пикселей
- function image.getColumn(picture, x)
- local column, background, foreground, alpha, symbol = {width = 1, height = picture.height}
- for y = 1, picture.height do
- background, foreground, alpha, symbol = image.get(picture, x, y)
- table.insert(column, background)
- table.insert(column, foreground)
- table.insert(column, alpha)
- table.insert(column, symbol)
- end
- return column
- end
- ----------------------------------------- Строковая обработка изображений -------------------------------------------------------------------
- --Преобразовать изображение в строковую интерпретацию, которая может быть вставлена в код
- --Удобно, если не хочется возиться с файловой системой
- function image.toString(picture)
- local stringedPicture = {}
- picture = convertImageColorsTo8Bit(picture)
- table.insert(stringedPicture, string.format("%02X", picture.width))
- table.insert(stringedPicture, string.format("%02X", picture.height))
- for i = 1, #picture, 4 do
- table.insert(stringedPicture, string.format("%02X", picture[i]))
- table.insert(stringedPicture, string.format("%02X", picture[i + 1]))
- table.insert(stringedPicture, string.format("%02X", picture[i + 2]))
- table.insert(stringedPicture, picture[i + 3])
- end
- picture = convertImageColorsTo24Bit(picture)
- return table.concat(stringedPicture)
- end
- --Получить изображение из строковой интерпретации, созданной ранее
- function image.fromString(stringedPicture)
- local picture = {}
- local subIndex = 1
- picture.width = tonumber("0x" .. unicode.sub(stringedPicture, subIndex, subIndex + 1)); subIndex = subIndex + 2
- picture.height = tonumber("0x" .. unicode.sub(stringedPicture, subIndex, subIndex + 1)); subIndex = subIndex + 2
- for pixel = 1, picture.width * picture.height do
- table.insert(picture, tonumber("0x" .. unicode.sub(stringedPicture, subIndex, subIndex + 1))); subIndex = subIndex + 2
- table.insert(picture, tonumber("0x" .. unicode.sub(stringedPicture, subIndex, subIndex + 1))); subIndex = subIndex + 2
- table.insert(picture, tonumber("0x" .. unicode.sub(stringedPicture, subIndex, subIndex + 1))); subIndex = subIndex + 2
- table.insert(picture, unicode.sub(stringedPicture, subIndex, subIndex)); subIndex = subIndex + 1
- end
- picture = convertImageColorsTo24Bit(picture)
- return picture
- end
- ----------------------------------------- Основные функции программы -------------------------------------------------------------------
- --Сохранить изображение любого поддерживаемого формата
- function image.save(path, picture, encodingMethod)
- result=""
- encodingMethod = encodingMethod or 4
- --Создать папку под файл, если ее нет
- --fs.makeDirectory(fs.path(path))
- --Получаем формат указанного файла
- --local fileFormat = getFileFormat(path)
- local fileFormat = ".pic"
- --Проверяем соответствие формата файла
- if fileFormat == constants.compressedFileFormat then
- --Оптимизируем картинку
- picture = image.optimize(picture)
- --Открываем файл
- -- local file = io.open(path, "w") костыльное переопределение команд file:write и file:read
- file={}
- function file.write(self,...)
- local mas={...}
- for i=1,#mas do
- result=result..mas[i]
- end
- end
- function file.close()
- end--]]
- --Записываем сигнатуру
- writeSignature(file)
- --Разбираемся с кодировкой
- if encodingMethod == 0 or string.lower(encodingMethod) == "raw" then
- file:write(string.char(encodingMethod))
- saveRaw(file, picture)
- elseif encodingMethod == 1 or string.lower(encodingMethod) == "ocif1" then
- file:write(string.char(encodingMethod))
- saveOCIF1(file, picture)
- elseif encodingMethod == 2 or string.lower(encodingMethod) == "ocif2" then
- file:write(string.char(encodingMethod))
- saveOCIF2(file, picture)
- elseif encodingMethod == 3 or string.lower(encodingMethod) == "ocif3" then
- error("Saving in encoding method 3 is deprecated and no longer supported. Use method 4 instead of it.")
- elseif encodingMethod == 4 or string.lower(encodingMethod) == "ocif4" then
- file:write(string.char(encodingMethod))
- picture = convertImageColorsTo8Bit(picture)
- saveOCIF2(file, picture, true)
- picture = convertImageColorsTo24Bit(picture)
- elseif encodingMethod == 6 then
- file:close()
- file = io.open(path, "w")
- file:write(image.toString(picture))
- file:close()
- else
- file:close()
- error("Unsupported encoding method.\n")
- end
- else
- file:close()
- error("Unsupported file format.\n")
- end
- return result
- end
- --Загрузить изображение любого поддерживаемого формата
- function image.load(str)
- --Кинуть ошибку, если такого файла не существует
- -- if not fs.exists(path) then error("File \""..path.."\" does not exists.\n") end
- --Получаем формат указанного файла
- local fileFormat = getFileFormat(path)
- --Проверяем соответствие формата файла
- if fileFormat == constants.compressedFileFormat then
- --local file = io.open(path, "rb")
- file={pos=1}
- function file.read(self,count)
- count=count or 1
- local op=file.pos
- file.pos=file.pos+count-1
- return string.sub(str,op,file.pos)
- end
- function file.close()
- end--]]
- --Читаем сигнатуру файла
- readSignature(file)
- --Читаем метод обработки изображения
- local encodingMethod = string.byte(file:read(1))
- --Читаем файлы в зависимости от метода
- --print("Загружаю файл типа " .. encodingMethod)
- if encodingMethod == 0 then
- return image.optimize(loadRaw(file))
- elseif encodingMethod == 1 then
- return image.optimize(loadOCIF1(file))
- elseif encodingMethod == 2 then
- return image.optimize(loadOCIF2(file))
- elseif encodingMethod == 3 then
- return image.optimize(loadOCIF2(file, true))
- elseif encodingMethod == 4 then
- return image.optimize(loadOCIF2(file, true, true))
- else
- file:close()
- error("Unsupported encoding method: " .. encodingMethod .. "\n")
- end
- --Поддержка ПНГ-формата
- elseif fileFormat == constants.pngFileFormat then
- return image.loadPng(path)
- else
- error("Unsupported file format: " .. fileFormat .. "\n")
- end
- end
- function load(str)
- return image.load(str)
- end
- function save(tbl)
- local picture = {}
- local foreground, background, symbol
- picture.width, picture.height = tbl.width, tbl.height
- for j = 1, picture.height do
- for i = 1, picture.width do
- --symbol, foreground, background = component.gpu.get(i, j)
- table.insert(picture, tbl[(j-1)*picture.width+i].background)
- table.insert(picture, tbl[(j-1)*picture.width+i].foreground)
- table.insert(picture, 0x00)
- table.insert(picture, tbl[(j-1)*picture.width+i].letter)
- end
- end
- return image.save("/disk/disk/trnd2.pic", picture)
- -- return image.toString(picture)
- end
- --tbl={width=3,height=2,
- --{background=0xff0000,foreground=0x000000,letter=" "},{background=0xff0000,foreground=0x000000,letter=" "},{background=0xff0000,foreground=0x000000,letter=" "},
- --{background=0xff0000,foreground=0x000000,letter=" "},{background=0xff0000,foreground=0x000000,letter=")"},{background=0xff0000,foreground=0x000000,letter=" "},
- --}
- --f=io.open("trnd.pic","w")
- --f:write(save(tbl))
- --_G.tmpRND=save(tbl)
- --f:close()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement