diff --git a/Applications/Photoshop/Photoshop.lua b/Applications/Photoshop/Photoshop.lua index 1159d4ce..d3817531 100644 --- a/Applications/Photoshop/Photoshop.lua +++ b/Applications/Photoshop/Photoshop.lua @@ -1,654 +1,345 @@ -local component = require("component") -local event = require("event") -local term = require("term") -local unicode = require("unicode") -local ecs = require("ECSAPI") -local fs = require("filesystem") -local context = require("context") + +------------------------------------------------ Копирайт -------------------------------------------------------------- + +local copyright = [[ + + Photoshop v3.0 (закрытая бета) + + Автор: IT + Контакты: https://vk.com/id7799889 + Соавтор: Pornogion + Контакты: https://vk.com/id88323331 + +]] + +------------------------------------------------ Библиотеки -------------------------------------------------------------- + +--Не требующиеся для MineOS +--local ecs = require("ECSAPI") +--local fs = require("filesystem") +--local unicode = require("unicode") +--local context = require("context") + +--Обязательные local colorlib = require("colorlib") local palette = require("palette") -local computer = require("computer") -local seri = require("serialization") -local keyboard = require("keyboard") +local event = require("event") local image = require("image") - local gpu = component.gpu -local arg = {...} +------------------------------------------------ Переменные -------------------------------------------------------------- --------------------------------------ПЕРЕМЕННЫЕ------------------------------------------------------ - -local xSize,ySize = gpu.getResolution() - -local drawImageFromX = 9 -local drawImageFromY = 3 - -local imageWidth = 8 -local imageHeight = 4 - -local transparentSymbol = "#" -local transparentBackground = 0xffffff -local transparentForeground = 0xcccccc - -local background = 0x000000 -local foreground = 0xffffff -local symbol = " " - -local toolbarColor = 0x535353 -local padColor = 0x262626 -local shadowColor = 0x1d1d1d -local toolbarTextColor = 0xcccccc -local toolbarPressColor = 0x3d3d3d -local consoleColor1 = 0x3d3d3d -local consoleColor2 = 0x999999 - - -local historyY = 2 -local rightToolbarWidth = 18 -local xRightToolbar = xSize - rightToolbarWidth + 1 - -local currentFile - -local rightToolbarWidthTextLimit = rightToolbarWidth - 2 - -local currentLayer = 1 -local layersY = historyY + math.floor(ySize / 2) - 2 -local layersDisplayLimit = math.floor((ySize - layersY - 1) / 2) -local drawLayersFrom = 1 -local layersIsVisibleSymbol = "●" -local layersIsNotVisibleSymbol = "◯" -local layersLimit = 20 ---◯ ● - -local currentInstrument = 2 -local instruments={ - {"Pipette","P"}, - {"Brush","B"}, - {"Eraser","E"}, - {"Text","T"}, -} -local topButtons = { - {"Файл"}, - {"Инструменты"}, - {"Фильтры"}, +--Массив главного изображения +local masterPixels = { + width = 0, + height = 0, } -local buttons = {"▲","▼","D","J","N","R"} +--Базовая цветовая схема программы +local colors = { + toolbar = 0x535353, + toolbarInfo = 0x3d3d3d, + toolbarButton = 0x3d3d3d, + toolbarButtonText = 0xeeeeee, + drawingArea = 0x262626, + console = 0x3d3d3d, + consoleText = 0x999999, + transparencyWhite = 0xffffff, + transparencyGray = 0xcccccc, + transparencyVariable = 0xffffff, + oldBackground = 0x0, + oldForeground = 0x0, + topMenu = 0xeeeeee, + topMenuText = 0x262626, +} -local consoleEnabled = true -local consoleWidth = xSize - 6 - rightToolbarWidth -local consoleText = "Программа запущена, консоль отладки включена" +--Различные константы и размеры тулбаров и кликабельных зон +local sizes = { + widthOfLeftBar = 6, +} +sizes.heightOfTopBar = 3 +sizes.xSize, sizes.ySize = gpu.getResolution() +sizes.xStartOfDrawingArea = sizes.widthOfLeftBar + 1 +sizes.xEndOfDrawingArea = sizes.xSize +sizes.yStartOfDrawingArea = 2 + sizes.heightOfTopBar +sizes.yEndOfDrawingArea = sizes.ySize - 1 +sizes.widthOfDrawingArea = sizes.xEndOfDrawingArea - sizes.xStartOfDrawingArea + 1 +sizes.heightOfDrawingArea = sizes.yEndOfDrawingArea - sizes.yStartOfDrawingArea + 1 +sizes.heightOfLeftBar = sizes.ySize - 1 -local pixels = {} -local MasterPixels = {} - ---------------------------------------ФУНКЦИИ----------------------------------------------------- - ---ОТРИСОВКА ПОЛОСЫ ПРОКРУТКИ -function newScrollBar(x,y,height,countOfAllElements,displayingFrom,displayingTo,backColor,frontColor) - local diapason = displayingTo - displayingFrom + 1 - local percent = diapason / countOfAllElements - local sizeOfScrollBar = math.ceil(percent * height) - local displayBarFrom = math.floor(y + height*(displayingFrom-1)/countOfAllElements) - - ecs.square(x,y,1,height,backColor) - ecs.square(x,displayBarFrom,1,sizeOfScrollBar,frontColor) +--Для изображения +local function reCalculateImageSizes() + sizes.widthOfImage = masterPixels.width + sizes.heightOfImage = masterPixels.height + sizes.sizeOfPixelData = 4 + sizes.xStartOfImage = 9 + sizes.yStartOfImage = 6 + sizes.xEndOfImage = sizes.xStartOfImage + sizes.widthOfImage - 1 + sizes.yEndOfImage = sizes.yStartOfImage + sizes.heightOfImage - 1 end +reCalculateImageSizes() ---ОБЪЕКТЫ +--Инструменты +sizes.heightOfInstrument = 3 +sizes.yStartOfInstruments = 2 + sizes.heightOfTopBar +local instruments = { + -- {"⮜", "Move"}, + -- {"✄", "Crop"}, + {"✎", "Brush"}, + {"❎", "Eraser"}, + {"⃟", "Fill"}, + {"Ⓣ", "Text"}, +} +local currentInstrument = 1 +local currentBackground = 0x6649ff +local currentForeground = 0x3ff80 +local currentAlpha = 0x00 +local currentSymbol = " " +local currentBrushSize = 1 +local savePath + +--Верхний тулбар +local topToolbar = {{"PS", ecs.colors.blue}, {"Файл"}, {"Изображение"}, {"Инструменты"}, {"О программе"}} + +------------------------------------------------ Функции отрисовки -------------------------------------------------------------- + +--Объекты для тача local obj = {} -local function newObj(class,name,key,value) +local function newObj(class, name, ...) obj[class] = obj[class] or {} - obj[class][name] = obj[class][name] or {} - obj[class][name][key] = value + obj[class][name] = {...} end -newObj("tools","imageZone","x1",drawImageFromX);newObj("tools","imageZone","x2",drawImageFromX+imageWidth-1);newObj("tools","imageZone","y1",drawImageFromY);newObj("tools","imageZone","y2",drawImageFromY+imageHeight-1) - -local function clearScreen(color) - gpu.setBackground(color) - term.clear() -end - -local function drawTransparency() - gpu.setBackground(transparentBackground) - gpu.setForeground(transparentForeground) - gpu.fill(drawImageFromX,drawImageFromY,imageWidth,imageHeight,transparentSymbol) -end - -local function createSampleLayer() - local massiv = {} - for j = 1,imageHeight do - massiv[j] = {} - for i = 1,imageWidth do - massiv[j][i] = { transparentBackground, transparentForeground, transparentSymbol } - end - end - return massiv -end - ---ОБЪЕДИНИТЬ ВСЕ СЛОИ В ОДИН САМЫЙ ЖИРНЫЙ ПИЗДОСЛОЙ -local function mergeLayersToMasterPixels() - - local sPixels = #pixels - MasterPixels = createSampleLayer() - - local layerCounter = sPixels - while layerCounter >= 1 do - - if pixels[layerCounter][3] then - for y=1,imageHeight do - if pixels[layerCounter][2][y] then - for x=1,imageWidth do - if pixels[layerCounter][2][y][x] then - MasterPixels[y][x] = {pixels[layerCounter][2][y][x][1],pixels[layerCounter][2][y][x][2],pixels[layerCounter][2][y][x][3]} - end - end - end - end - end - - layerCounter = layerCounter - 1 - end - -end - -local function createMassiv() - MasterPixels = {} - pixels = { - {"Слой 1",{},true} - } -end - -newObj("tools", "imageZone2", "x1", 7); newObj("tools", "imageZone2", "x2", xSize - rightToolbarWidth); newObj("tools", "imageZone2", "y1", 2); newObj("tools", "imageZone2", "y2", ySize - 1) - -local function drawFromMassiv(clearScreenOrNot) - - if clearScreenOrNot then ecs.square(obj["tools"]["imageZone2"]["x1"], obj["tools"]["imageZone2"]["y1"], obj["tools"]["imageZone2"]["x2"] - obj["tools"]["imageZone2"]["x1"] + 1, obj["tools"]["imageZone2"]["y2"] - obj["tools"]["imageZone2"]["y1"] + 1, padColor) end - - obj["tools"]["imageZone"] = {} - newObj("tools","imageZone","x1",drawImageFromX);newObj("tools","imageZone","x2",drawImageFromX+imageWidth-1);newObj("tools","imageZone","y1",drawImageFromY);newObj("tools","imageZone","y2",drawImageFromY+imageHeight-1) - - - local x = drawImageFromX - 1 - local y = drawImageFromY - 1 - - --[[local PLUSY = drawImageFromY + imageHeight - local PLUSX = drawImageFromX + imageWidth]] - - mergeLayersToMasterPixels() - - for i=1,imageHeight do - for j=1,imageWidth do - - local xOnScreen = drawImageFromX + j - 1 - local yOnScreen = drawImageFromY + i - 1 - - if xOnScreen >= obj["tools"]["imageZone2"]["x1"] and xOnScreen <= obj["tools"]["imageZone2"]["x2"] and yOnScreen >= obj["tools"]["imageZone2"]["y1"] and yOnScreen <= obj["tools"]["imageZone2"]["y2"] then - - if MasterPixels[i][j][3] ~= transparentSymbol then - - --Оптимизация - - if MasterPixels[i][j][1] ~= gpu.getBackground() then - gpu.setBackground(MasterPixels[i][j][1]) - end - - if MasterPixels[i][j][2] ~= gpu.getForeground() then - gpu.setForeground(MasterPixels[i][j][2]) - end - - gpu.set(x+j, y+i, MasterPixels[i][j][3]) - - else - - if transparentBackground ~= gpu.getBackground() then - gpu.setBackground(transparentBackground) - end - - if transparentForeground ~= gpu.getForeground() then - gpu.setForeground(transparentForeground) - end - - gpu.set(x+j, y+i, transparentSymbol) - - end - - --[[ТЕНЬ, БЛЯДЬ - gpu.setBackground(shadowColor) - - if PLUSY <= obj["tools"]["imageZone2"]["y2"] then - gpu.set(xOnScreen,PLUSY," ") - end - - if PLUSX <= obj["tools"]["imageZone2"]["x2"]-1 then - gpu.fill(PLUSX,yOnScreen,2,1," ") - end]] - end - end - end -end - -local function changePixelInMassiv(x,y,layer,background,foreground,symbol) - pixels[layer][2][y] = pixels[layer][2][y] or {} - pixels[layer][2][y][x] = pixels[layer][2][y][x] or {} - pixels[layer][2][y][x][1] = background - pixels[layer][2][y][x][2] = foreground - pixels[layer][2][y][x][3] = symbol -end - -local function drawInstruments(xStart,yStart) - for i=1,#instruments do - local posY = yStart+i*4-4 - local cyka = toolbarColor - - if currentInstrument == i then cyka = toolbarPressColor end - ecs.square(1,posY,6,3,cyka) - gpu.setForeground(toolbarTextColor) - gpu.set(xStart+1,posY+1,instruments[i][2]) - newObj("instruments",i,"x1",xStart);newObj("instruments",i,"x2",xStart+3);newObj("instruments",i,"y1",posY);newObj("instruments",i,"y2",posY+2) - end -end - -local function drawMemory() - local totalMemory = computer.totalMemory() / 1024 - local freeMemory = computer.freeMemory() / 1024 - local usedMemory = totalMemory - freeMemory - - local stro4ka = math.ceil(usedMemory).."/"..math.floor(totalMemory).."KB" - - local posX = xRightToolbar - unicode.len(stro4ka) - 1 - - ecs.colorTextWithBack(posX,ySize,consoleColor2,consoleColor1,stro4ka) -end - -local function console(x,y) - ecs.square(x,y,consoleWidth,1,consoleColor1) - gpu.setForeground(consoleColor2) - gpu.set(x+1,y,consoleText) - - drawMemory() -end - -local function drawTopToolbar() - ecs.square(1,1,xSize,1,toolbarColor) - ecs.colorText(3,1,ecs.colors.lightBlue,"PS") - - local posX = 7 - local spaceBetween = 2 - gpu.setForeground(toolbarTextColor) - for i=1,#topButtons do - gpu.set(posX,1,topButtons[i][1]) - local length = unicode.len(topButtons[i][1]) - newObj("top",i,"x1",posX-1);newObj("top",i,"x2",posX+length);newObj("top",i,"y1",1);newObj("top",i,"y2",1);newObj("top",i,"name",topButtons[i][1]) - - posX = posX + length + spaceBetween - end -end - -local function drawLeftToolbar() - ecs.square(1,2,6,xSize,toolbarColor) - - --ЦВЕТА - ecs.square(3,ySize-3,3,2,foreground) - ecs.square(2,ySize-4,3,2,background) - ecs.colorTextWithBack(3,ySize-1,toolbarTextColor,toolbarColor,"←→") - - drawInstruments(2,2) - - if consoleEnabled then console(7,ySize) end - - newObj("colors",1,"x1",2);newObj("colors",1,"x2",4);newObj("colors",1,"y1",ySize-4);newObj("colors",1,"y2",ySize-3) - newObj("colors",2,"x1",3);newObj("colors",2,"x2",5);newObj("colors",2,"y1",ySize-2);newObj("colors",2,"y2",ySize-2) - newObj("colors",3,"x1",5);newObj("colors",3,"x2",5);newObj("colors",3,"y1",ySize-3);newObj("colors",3,"y2",ySize-3) - - newObj("swapper",1,"x1",3);newObj("swapper",1,"x2",4);newObj("swapper",1,"y1",ySize-1);newObj("swapper",1,"y2",ySize-1) -end - - -newObj("layersZone",1,"x1",xRightToolbar);newObj("layersZone",1,"y1",layersY + 1);newObj("layersZone",1,"x2",xSize-1);newObj("layersZone",1,"y2",layersY + layersDisplayLimit*2+1) - - ---РИСОВАТЬ СЛОИ СПРАВА -local function drawLayers(from) - - obj["layers"] = {} - - local sLayers = #pixels - local posY = layersY + 2 - - local heigthOfGovno = layersDisplayLimit*2 - - --СЕРАЯ ОЧИСТКА ВСЕГО СПРАВА - ecs.square(xRightToolbar,posY,rightToolbarWidth-1,heigthOfGovno,toolbarColor) - - --ВЕРНОЕ ОТОБРАЖЕНИЕ СКРОЛЛБАРА, РАСЧЕТ, ДОКУДОВА ОНО БУДЕТ ЕБОШИТЬ - local to = sLayers - if sLayers > layersDisplayLimit then - to = drawLayersFrom + layersDisplayLimit - 1 - end - newScrollBar(xSize,posY-1,heigthOfGovno+1,sLayers,drawLayersFrom,to,padColor,ecs.colors.lightBlue) - - --СОЗДАНИЕ ХОРОШИХ ПЕРЕМННЫХ, ЧТОБЫ В ЦИКЛЕ НЕ СОЗДАВАЛИСЬ ПЛЮСИКИ - local dlyaCiklaO4istka = rightToolbarWidth - 4 - local dlyaCiklaText = xRightToolbar + 4 - local dlyaCiklaVision = xRightToolbar + 1 - local dlyaCiklaVisionPoloskaSprava = dlyaCiklaVision + 1 - local dlyaCiklaStartOfSelectionBlue = xRightToolbar+3 - - --СОЗДАНИЕ РАЗДЕЛИТЕЛЯ НУЖНОЙ ДЛИНЫ ПО ТИПУ STRING.REP() - local separatorLength = rightToolbarWidth-4 - local separator = "" - for i=1,separatorLength do - separator = separator .. "─" - end - --ФУНКЦИЯ ОТРИСОВКИ РАЗДЕЛИТЕЛЯ - local function drawLayersLine(y,type) - gpu.setForeground(toolbarPressColor) - gpu.setBackground(toolbarColor) - if type == "top" then - gpu.set(xRightToolbar,y,"──┬"..separator) - elseif type == "mid" then - gpu.set(xRightToolbar,y,"──┼"..separator) +local function drawTransparentPixel(xPos, yPos, i, j) + if j % 2 == 0 then + if i % 2 == 0 then + colors.transparencyVariable = colors.transparencyWhite else - gpu.set(xRightToolbar,y,"──┴"..separator) - end - end - - drawLayersLine(posY-1,"top") - - --ОТРИСОВКА ВСЕХ СЛОЕВ СПРАВА - local counter = 1 - for i=from,(from+layersDisplayLimit-1) do - - if pixels[i] then - - --СОЗДАНИЕ НАЗВАНИЯ СЛОЯ И ОТРИСОВКА СИНЕНЬКОГО ИЛИ НЕТ - local stroka = ecs.stringLimit("end",pixels[i][1],separatorLength-2,true) - if currentLayer == i then - ecs.square( dlyaCiklaStartOfSelectionBlue, posY, dlyaCiklaO4istka, 1, ecs.colors.blue ) - ecs.colorText(dlyaCiklaText,posY,0xffffff,stroka) - else - gpu.setForeground(toolbarTextColor) - gpu.setBackground(toolbarColor) - gpu.set(dlyaCiklaText,posY,stroka) - end - - --ОТРИСОВКА ВИДИМОГО ИЛИ НЕВИДИМОГО ГЛАЗКА - gpu.setBackground(toolbarPressColor) - gpu.setForeground(toolbarTextColor) - if pixels[i][3] then - gpu.set(dlyaCiklaVision,posY,layersIsVisibleSymbol) - else - gpu.set(dlyaCiklaVision,posY,layersIsNotVisibleSymbol) - end - - --ОТРИСОВКА РАЗДЕЛИТЕЛЕЙ ПО УСЛОВИЯМ - if counter < sLayers and counter < layersDisplayLimit then - drawLayersLine(posY + 1, "mid") - else - drawLayersLine(posY + 1, "bot") - end - gpu.set(dlyaCiklaVisionPoloskaSprava, posY, "│") - - --СОЗДАНИЕ ОБЪЕКТОВ - newObj("layers",i,"x1",xRightToolbar+3);newObj("layers",i,"x2",xSize);newObj("layers",i,"y",posY) - newObj("layerEyes",i,"x1",xRightToolbar+1);newObj("layerEyes",i,"x2",xRightToolbar+1);newObj("layerEyes",i,"y",posY) - - posY = posY + 2 - counter = counter + 1 - end - - end - - --РИСОВАНИЕ КНОПОЧЕК УПРАВЛЕНИЯ СЛОЯМИ - obj["layerButtons"] = {} - ecs.square(xRightToolbar, ySize, rightToolbarWidth, 1, toolbarPressColor + 0x111111) - - --ЭТО ШОБ КОД СОКРАТИТЬ, Я ЖЕ ТИПА ПРО - local function drawLayerButton(xPos,name,good) - if not good then - ecs.colorText(xPos,ySize,0x000000,name) - else - ecs.colorText(xPos,ySize,toolbarTextColor,name) - newObj("layerButtons",name,"x1",xPos);newObj("layerButtons",name,"x2",xPos);newObj("layerButtons",name,"y",ySize) - end - end - - local xPos = xRightToolbar + 1 - for i = 1, #buttons do - - if i == 1 then - - if currentLayer <= 1 then - drawLayerButton(xPos,buttons[i],false) - else - drawLayerButton(xPos,buttons[i],true) - end - - elseif i == 2 then - - if currentLayer >= sLayers then - drawLayerButton(xPos,buttons[i],false) - else - drawLayerButton(xPos,buttons[i],true) - end - - elseif i == 3 then - - drawLayerButton(xPos,buttons[i],true) - - elseif i == 4 then - - if sLayers > 1 and currentLayer < sLayers then - drawLayerButton(xPos,buttons[i],true) - else - drawLayerButton(xPos,buttons[i],false) - end - - elseif i == 5 then - - if sLayers >= layersLimit then - drawLayerButton(xPos,buttons[i],false) - else - drawLayerButton(xPos,buttons[i],true) - end - - elseif i == 6 then - - if sLayers <= 1 then - drawLayerButton(xPos,buttons[i],false) - else - drawLayerButton(xPos,buttons[i],true) - end - - end - - xPos = xPos + 3 - end - - -end - -local function drawLayersBEZVOPROSOVCYKA() - local sLayers = #pixels - drawLayersFrom = sLayers - layersDisplayLimit + 1 - if sLayers < layersDisplayLimit then drawLayersFrom = 1 end - drawLayers(drawLayersFrom) -end - -local function drawRightToolbar() - ecs.square(xRightToolbar,2,rightToolbarWidth,ySize-1,toolbarColor) - - ecs.square(xRightToolbar,historyY,rightToolbarWidth,1,toolbarPressColor) - ecs.colorText(xRightToolbar+1,historyY,toolbarTextColor,"Тут будут пар-ры кисти") - - ecs.square(xRightToolbar,layersY,rightToolbarWidth,1,toolbarPressColor) - ecs.colorText(xRightToolbar+1,layersY,toolbarTextColor,"Слои") - - drawLayers(drawLayersFrom) -end - -local function scrollLayers(direction) - if direction == 1 then - drawLayersFrom = drawLayersFrom - 1 - if drawLayersFrom < 1 then - drawLayersFrom = 1 - else - drawLayers(drawLayersFrom) + colors.transparencyVariable = colors.transparencyGray end else - drawLayersFrom = drawLayersFrom + 1 - if drawLayersFrom + layersDisplayLimit -1 > #pixels then - drawLayersFrom = drawLayersFrom - 1 - + if i % 2 == 0 then + colors.transparencyVariable = colors.transparencyGray else - --ЭТО ШОБ НЕ СКРОЛЛИТЬ НИЖЕ КАРОЧ, А ТО ВЫЛЕЗЕТ И БУДЕТ 1 ЭЛЕМЕНТ ТОЛЬКА КАРОЧ - if drawLayersFrom > #pixels - layersDisplayLimit + 1 then - drawLayersFrom = drawLayersFrom - 1 - end - - drawLayers(drawLayersFrom) + colors.transparencyVariable = colors.transparencyWhite end end + gpu.setBackground(colors.transparencyVariable) + gpu.set(xPos, yPos, " ") end ---ДОБАВИТЬ НОВЫЙ ЭЛЕМЕНТ В ИСТОРИЮ ЭТОГО КАЛОПРОИЗВОДСТВА -local function addElementToLayers(discription) - table.insert(pixels, 1, {"Слой "..(#pixels+1), {}, true}) - - --НУ ТУТ И ТАК ФСО ЯСНА, БЛЯДЬ - currentLayer = 1 +local function drawBackground() + ecs.square(sizes.xStartOfDrawingArea, sizes.yStartOfDrawingArea, sizes.widthOfDrawingArea, sizes.heightOfDrawingArea, colors.drawingArea) end -local function duplicateLayer(from) - local hehe = {"Копия "..pixels[from][1], {}, true} - for key,val in pairs(pixels[from][2]) do - hehe[2][key] = val +local function drawInstruments() + local yPos = sizes.yStartOfInstruments + for i = 1, #instruments do + if currentInstrument == i then + ecs.square(1, yPos, sizes.widthOfLeftBar, sizes.heightOfInstrument, colors.toolbarButton) + else + ecs.square(1, yPos, sizes.widthOfLeftBar, sizes.heightOfInstrument, colors.toolbar) + end + ecs.colorText(3, yPos + 1, colors.toolbarButtonText, instruments[i][1]) + + newObj("Instruments", i, 1, yPos, sizes.widthOfLeftBar, yPos + sizes.heightOfInstrument - 1) + + yPos = yPos + sizes.heightOfInstrument + end +end + +local function drawColors() + local xPos, yPos = 2, sizes.ySize - 4 + ecs.square(xPos, yPos, 3, 2, currentBackground) + ecs.square(xPos + 3, yPos + 1, 1, 2, currentForeground) + ecs.square(xPos + 1, yPos + 2, 2, 1, currentForeground) + ecs.colorTextWithBack(xPos + 1, yPos + 3, 0xaaaaaa, colors.toolbar, "←→") + + newObj("Colors", 1, xPos, yPos, xPos + 2, yPos + 1) + newObj("Colors", 2, xPos + 3, yPos + 1, xPos + 3, yPos + 2) + newObj("Colors", 3, xPos + 1, yPos + 2, xPos + 3, yPos + 2) + newObj("Colors", 4, xPos + 1, yPos + 3, xPos + 2, yPos + 3) +end + +local function drawLeftBar() + ecs.square(1, 2, sizes.widthOfLeftBar, sizes.heightOfLeftBar, colors.toolbar) + drawInstruments() + drawColors() +end + +local function drawTopMenu() + ecs.square(1, 1, sizes.xSize, 1, colors.topMenu) + local xPos = 3 + + for i = 1, #topToolbar do + ecs.colorText(xPos, 1, topToolbar[i][2] or colors.topMenuText, topToolbar[i][1]) + + if i > 1 then + newObj("TopMenu", topToolbar[i][1], xPos, 1, xPos + unicode.len(topToolbar[i][1]) - 1, 1) + end + + xPos = xPos + unicode.len(topToolbar[i][1]) + 2 + end +end + +local function drawTopBar() + + local topBarInputs = { {"Размер кисти", currentBrushSize}, {"Прозрачность", math.floor(currentAlpha)}} + + ecs.square(1, 2, sizes.xSize, sizes.heightOfTopBar, colors.toolbar) + local xPos, yPos = 3, 3 + local limit = 8 + + --ecs.error("сукак") + + for i = 1, #topBarInputs do + ecs.colorTextWithBack(xPos, yPos, 0xeeeeee, colors.toolbar, topBarInputs[i][1]) + + xPos = xPos + unicode.len(topBarInputs[i][1]) + 1 + ecs.inputText(xPos, yPos, limit, tostring(topBarInputs[i][2]), 0xffffff, 0x262626, true) + + newObj("TopBarInputs", i, xPos, yPos, xPos + limit - 1, yPos, limit) + + if i == 2 then xPos = xPos + 3 end + + xPos = xPos + limit + 2 end - table.insert(pixels, from, hehe) - hehe = nil end -local function moveLayer(from,direction) - local hehe = {} - for key,val in pairs(pixels[from]) do - hehe[key] = val +local function createEmptyMasterPixels() + --Очищаем мастерпиксельс и задаем ширину с высотой + masterPixels = {} + masterPixels.width = sizes.widthOfImage + masterPixels.height = sizes.heightOfImage + --Создаем пустой мастерпиксельс + for j = 1, sizes.heightOfImage * sizes.widthOfImage do + table.insert(masterPixels, 0x000000) + table.insert(masterPixels, 0x000000) + table.insert(masterPixels, 0xFF) + table.insert(masterPixels, " ") end +end +--Формула конвертации итератора массива в абсолютные координаты пикселя изображения +local function convertIteratorToCoords(iterator) + --Приводим итератор к корректному виду (1 = 1, 5 = 2, 9 = 3, 13 = 4, 17 = 5, ...) + iterator = (iterator + sizes.sizeOfPixelData - 1) / sizes.sizeOfPixelData + --Получаем остаток от деления итератора на ширину изображения + local ostatok = iterator % sizes.widthOfImage + --Если остаток равен 0, то х равен ширине изображения, а если нет, то х равен остатку + local x = (ostatok == 0) and sizes.widthOfImage or ostatok + --А теперь как два пальца получаем координату по Y + local y = math.ceil(iterator / sizes.widthOfImage) + --Очищаем остаток из оперативки + ostatok = nil + --Возвращаем координаты + return x, y +end + +--Формула конвертации абсолютных координат пикселя изображения в итератор для массива +local function convertCoordsToIterator(x, y) + --Конвертируем координаты в итератор + return (sizes.widthOfImage * (y - 1) + x) * sizes.sizeOfPixelData - sizes.sizeOfPixelData + 1 +end + +local function console(text) + ecs.square(sizes.xStartOfDrawingArea, sizes.ySize, sizes.widthOfDrawingArea, 1, colors.console) + local _, total, used = ecs.getInfoAboutRAM() + ecs.colorText(sizes.xEndOfDrawingArea - 15, sizes.ySize, colors.consoleText, used.."/"..total.." KB RAM") + gpu.set(sizes.xStartOfDrawingArea + 1, sizes.ySize, text) + _, total, used = nil, nil, nil +end + +local function drawPixel(x, y, i, j, iterator) + --Получаем данные о пикселе + local background, foreground, alpha, symbol = masterPixels[iterator], masterPixels[iterator + 1], masterPixels[iterator + 2], masterPixels[iterator + 3] + --Если пиксель не прозрачный + if alpha == 0x00 then + gpu.setBackground(background) + gpu.setForeground(foreground) + gpu.set(x, y, symbol) + --Если пиксель прозрачнее непрозрачного + elseif alpha > 0x00 then + --Рисуем прозрачный пиксель + drawTransparentPixel(x, y, i, j) + --Ебать я красавчик! Даже без гпу.гет() сделал! + gpu.setBackground(colorlib.alphaBlend(colors.transparencyVariable, background, alpha)) + gpu.setForeground(foreground) + gpu.set(x, y, symbol) + end + background, foreground, alpha, symbol = nil, nil, nil, nil +end + +local function drawImage() + --Стартовые нужности + local xPixel, yPixel = 1, 1 + local xPos, yPos = sizes.xStartOfImage, sizes.yStartOfImage + --Перебираем массив мастерпиксельса + for i = 1, #masterPixels, 4 do + --Если пиксель входит в разрешенную зону рисования + if xPos >= sizes.xStartOfDrawingArea and xPos <= sizes.xEndOfDrawingArea and yPos >= sizes.yStartOfDrawingArea and yPos <= sizes.yEndOfDrawingArea then + --Рисуем пиксель + drawPixel(xPos, yPos, xPixel, yPixel, i) + end + --Всякие расчеты координат + xPixel = xPixel + 1 + xPos = xPos + 1 + if xPixel > sizes.widthOfImage then xPixel = 1; xPos = sizes.xStartOfImage; yPixel = yPixel + 1; yPos = yPos + 1 end + end +end + +local function drawBackgroundAndImage() + drawBackground() + drawImage() +end + +local function drawAll() + --Очищаем экран + ecs.prepareToExit() + --И консольку! + console("Весь интерфейс перерисован!") + --Рисуем тулбары + drawBackground() + drawLeftBar() + drawTopBar() + drawTopMenu() + --Рисуем картинку + drawBackgroundAndImage() +end + +------------------------------------------------ Функции расчета -------------------------------------------------------------- + +local function move(direction) if direction == "up" then - pixels[from] = pixels[from - 1] - pixels[from - 1] = hehe - currentLayer = currentLayer - 1 - else - pixels[from] = pixels[from + 1] - pixels[from + 1] = hehe - currentLayer = currentLayer + 1 + sizes.yStartOfImage = sizes.yStartOfImage - 2 + elseif direction == "down" then + sizes.yStartOfImage = sizes.yStartOfImage + 2 + elseif direction == "left" then + sizes.xStartOfImage = sizes.xStartOfImage - 2 + elseif direction == "right" then + sizes.xStartOfImage = sizes.xStartOfImage + 2 end - - hehe = nil - - drawFromMassiv() + drawBackgroundAndImage() end -local function joinLayer(from) - local fromPlusOne = from + 1 - for j=1,imageHeight do - if pixels[from][2][j] then - for i = 1,imageWidth do - if pixels[from][2][j][i] then - pixels[fromPlusOne][2][j] = pixels[fromPlusOne][2][j] or {} - pixels[fromPlusOne][2][j][i] = pixels[from][2][j][i] - end - end - end - end - pixels[fromPlusOne][1] = pixels[from][1].." и "..pixels[fromPlusOne][1] - table.remove(pixels,from) -end - -local function deleteLayer(layer) - table.remove(pixels,layer) - if currentLayer > #pixels then currentLayer = #pixels end - - drawLayers(drawLayersFrom) - drawFromMassiv() +local function setPixel(iterator, background, foreground, alpha, symbol) + masterPixels[iterator] = background + masterPixels[iterator + 1] = foreground + masterPixels[iterator + 2] = alpha + masterPixels[iterator + 3] = symbol end local function swapColors() - local tempColor = foreground - foreground = background - background = tempColor + local tempColor = currentForeground + currentForeground = currentBackground + currentBackground = tempColor tempColor = nil - consoleText = "Цвета переключены. Первичный цвет "..string.format("%x",background)..", вторичный "..string.format("%x",foreground) - drawLeftToolbar() + drawColors() + console("Цвета поменяны местами.") end ---Сохранение изображения в нужном формате -local function save(path, format) - mergeLayersToMasterPixels() - image.save(path, MasterPixels) - -- if format == ".png" then - -- image.savePNG(path, MasterPixels) - -- elseif format == ".jpg" then - -- image.saveJPG(path, image.PNGtoJPG(MasterPixels)) - -- end -end - -local function open(path) - - --Загружаем картинку и получаем все, что нужно о ней - local loadedImage = image.load(path) - local kartinka = loadedImage.image - local format = loadedImage["format"] - - --Всякие вещи - local loadedImageWidth - local loadedImageHeight - createMassiv() - - if format == ".png" then - loadedImageHeight = #kartinka - loadedImageWidth = #kartinka[1] - pixels[1][2] = kartinka - elseif format == ".jpg" then - --local PNGKartinka = image.JPGtoPNG(kartinka) - loadedImageHeight = #kartinka - loadedImageWidth = #kartinka[1] - pixels[1][2] = kartinka - else - ecs.error("Ошибка чтения формата файла!") - return - end - - imageWidth, imageHeight = loadedImageWidth, loadedImageHeight -end - ---ОТРИСОВАТЬ ВАЩЕ ВСЕ ЧТО ТОЛЬКО МОЖНО -local function drawAll() - clearScreen(padColor) - drawLeftToolbar() - drawTopToolbar() - drawRightToolbar() - drawFromMassiv() -end - ---А ЭТО КАРОЧ ИЗ ЮНИКОДА В СИМВОЛ - ВРОДЕ РАБОТАЕТ, НО ВСЯКОЕ БЫВАЕТ -local function convertCodeToSymbol(code) - local symbol = nil - if code ~= 0 and code ~= 13 and code ~= 8 then - symbol = unicode.char(code) - if keyboard.isShiftPressed then symbol = unicode.upper(symbol) end - end - return symbol -end - ---КРАСИВАЯ ШТУЧКА ДЛЯ ВВОДА ТЕКСТА -local function inputText(x,y,limit,textColor) - - textColor = textColor or background - +local function inputText(x, y, limit) local oldPixels = ecs.rememberOldPixels(x,y-1,x+limit-1,y+1) local text = "" @@ -657,11 +348,11 @@ local function inputText(x,y,limit,textColor) local function drawThisShit() for i=1,inputPos do ecs.invertedText(x + i - 1, y + 1, "─") - ecs.adaptiveText(x + i - 1, y - 1, " ", background) + ecs.adaptiveText(x + i - 1, y - 1, " ", currentBackground) end ecs.invertedText(x + inputPos - 1, y + 1, "▲")--"▲","▼" ecs.invertedText(x + inputPos - 1, y - 1, "▼") - ecs.adaptiveText(x,y,ecs.stringLimit("start",text,limit,false),textColor) + ecs.adaptiveText(x, y, ecs.stringLimit("start", text, limit, false), currentBackground) end drawThisShit() @@ -681,7 +372,7 @@ local function inputText(x,y,limit,textColor) elseif e[4] == 28 then break else - local symbol = convertCodeToSymbol(e[3]) + local symbol = ecs.convertCodeToSymbol(e[3]) if symbol ~= nil then text = text .. symbol if unicode.len(text) < limit then @@ -694,7 +385,7 @@ local function inputText(x,y,limit,textColor) if e[3] then text = text .. e[3] if unicode.len(text) < limit then - inputPos = inputPos + 1 + inputPos = inputPos + unicode.len(e[3]) end drawThisShit() end @@ -702,420 +393,441 @@ local function inputText(x,y,limit,textColor) end ecs.drawOldPixels(oldPixels) + if text == "" then text = " " end return text end ---СОХРАНЕНИЕ ТЕКСТА В МАССИВ ПИКСЕЛЕЙ, НО ЕСЛИ ЧЕТ ПРЕВЫШАЕТ ИЛИ ЕЩЕ КАКАЯ ХУЙНЯ - ТО СОСИ! -local function saveTextToPixels(x,y,text) +local function saveTextToPixels(x, y, text) local sText = unicode.len(text) - pixels[currentLayer][2][y] = pixels[currentLayer][2][y] or {} - for i=1,sText do - local xPlusIMinus1 = x + i - 1 - local kuso4ek = unicode.sub(text,i,i) - if pixels[currentLayer][2][y][xPlusIMinus1] then - pixels[currentLayer][2][y][xPlusIMinus1][2] = background - pixels[currentLayer][2][y][xPlusIMinus1][3] = kuso4ek - else - pixels[currentLayer][2][y][xPlusIMinus1] = {} - pixels[currentLayer][2][y][xPlusIMinus1][1] = foreground - pixels[currentLayer][2][y][xPlusIMinus1][2] = background - pixels[currentLayer][2][y][xPlusIMinus1][3] = kuso4ek + local iterator + x = x - 1 + for i = 1, sText do + if x + i > sizes.widthOfImage then break end + iterator = convertCoordsToIterator(x + i, y) + setPixel(iterator, masterPixels[iterator], currentBackground, masterPixels[iterator + 2], unicode.sub(text, i, i)) + end +end + +local function flipVertical(massiv) + local newMassiv = {} + newMassiv.width, newMassiv.height = massiv.width, massiv.height + + local iterator = #masterPixels + while iterator >= 1 do + table.insert(newMassiv, masterPixels[iterator - 3]) + table.insert(newMassiv, masterPixels[iterator - 2]) + table.insert(newMassiv, masterPixels[iterator - 1]) + table.insert(newMassiv, masterPixels[iterator]) + + masterPixels[iterator], masterPixels[iterator - 1], masterPixels[iterator - 2], masterPixels[iterator - 3] = nil, nil, nil, nil + + iterator = iterator - 4 + end + + return newMassiv +end + +local function flipHorizontal( picture ) + local blockSize = picture.width * 4 + + local buffer = nil + local startBlock = nil + local endPixel = nil + + for j=1, picture.height, 1 do + startBlock = picture.width * 4 * (j-1) + + for pixel=4, blockSize/2, 4 do + endPixel = blockSize-(pixel-4) + + --Foreground + buffer = picture[pixel-3+startBlock] + picture[pixel-3+startBlock] = picture[endPixel-3+startBlock] + picture[endPixel-3+startBlock] = buffer + + --Background + buffer = picture[pixel-2+startBlock] + picture[pixel-2+startBlock] = picture[endPixel-2+startBlock] + picture[endPixel-2+startBlock] = buffer + + --Alpha + buffer = picture[pixel-1+startBlock] + picture[pixel-1+startBlock] = picture[endPixel-1+startBlock] + picture[endPixel-1+startBlock] = buffer + + --Char + buffer = picture[pixel-0+startBlock] + picture[pixel-0+startBlock] = picture[endPixel-0+startBlock] + picture[endPixel-0+startBlock] = buffer end end end -local function newFile() - imageWidth = 0 - imageHeight = 0 +local function doFlip(horizontal) + if horizontal then + flipHorizontal(masterPixels) + else + masterPixels = flipVertical(masterPixels) + end + drawImage() +end - createMassiv() - currentLayer = 1 - drawAll() +local function invertColors() + for i = 1, #masterPixels, 4 do + masterPixels[i] = 0xFFFFFF - masterPixels[i] + masterPixels[i + 1] = 0xFFFFFF - masterPixels[i + 1] + end + drawImage() +end - local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Новый документ"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Ширина"}, {"Input", 0x262626, 0x880000, "Высота"}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "Ok!"}}) +local function blackAndWhite() + for i = 1, #masterPixels, 4 do + local hh, ss, bb = colorlib.HEXtoHSB(masterPixels[i]); ss = 0 + masterPixels[i] = colorlib.HSBtoHEX(hh, ss, bb) + + hh, ss, bb = colorlib.HEXtoHSB(masterPixels[i + 1]); ss = 0 + masterPixels[i + 1] = colorlib.HSBtoHEX(hh, ss, bb) + end + drawImage() +end - if data[1] == "" or data[1] == nil or data[1] == " " then data[1] = 51 end - if data[2] == "" or data[2] == nil or data[2] == " " then data[2] = 19 end +local function new() + local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, false, {"EmptyLine"}, {"CenterText", 0x262626, "Новый документ"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Ширина"}, {"Input", 0x262626, 0x880000, "Высота"}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "Ok!"}}) - imageWidth = tonumber(data[1]) or 51 - imageHeight = tonumber(data[2]) or 19 + data[1] = tonumber(data[1]) or 51 + data[2] = tonumber(data[2]) or 19 - createMassiv() + sizes.widthOfImage, sizes.heightOfImage = data[1], data[2] + sizes.xStartOfImage = 9 + sizes.yStartOfImage = 6 + sizes.xEndOfImage = sizes.xStartOfImage + sizes.widthOfImage - 1 + sizes.yEndOfImage = sizes.yStartOfImage + sizes.heightOfImage - 1 + + createEmptyMasterPixels() drawAll() end -local function filter(mode) +--Обычная рекурсивная заливка +local function fill(x, y, startColor, fillColor) + local function doFill(xStart, yStart) + local iterator = convertCoordsToIterator(xStart, yStart) - for y = 1, imageHeight do - if pixels[currentLayer][2][y] then - for x =1 ,imageWidth do - if pixels[currentLayer][2][y][x] then - if mode == "invert" then - pixels[currentLayer][2][y][x] = {0xffffff - pixels[currentLayer][2][y][x][1], 0xffffff - pixels[currentLayer][2][y][x][2], pixels[currentLayer][2][y][x][3]} + --Завершаем функцию, если цвет в массиве не такой, какой мы заливаем + if masterPixels[iterator] ~= startColor or masterPixels[iterator] == fillColor then return end + + --Заливаем в память + masterPixels[iterator] = fillColor + masterPixels[iterator + 2] = currentAlpha + + doFill(xStart + 1, yStart) + doFill(xStart - 1, yStart) + doFill(xStart, yStart + 1) + doFill(xStart, yStart - 1) + + iterator = nil + end + doFill(x, y) +end + +--Кисть +local function brush(x, y, background, foreground, alpha, symbol) + --Смещение влево и вправо относительно указанного центра кисти + local position = math.floor(currentBrushSize / 2) + local newIterator + --Сдвигаем х и у на смещение + x, y = x - position, y - position + --Считаем ширину/высоту кисти + local brushSize = position * 2 + 1 + --Перебираем кисть по ширине и высоте + for cyka = 1, brushSize do + for pidor = 1, brushSize do + --Если этот кусочек входит в границы рисовабельной зоны, то + if x >= 1 and x <= sizes.widthOfImage and y >= 1 and y <= sizes.heightOfImage then + + --Считаем новый итератор для кусочка кисти + newIterator = convertCoordsToIterator(x, y) + + --Если указанная прозрачность не максимальна + if alpha < 0xFF then + --Если пиксель в массиве ни хуя не прозрачный, то оставляем его таким же, разве что цвет меняем на сблендированный + if masterPixels[newIterator + 2] == 0x00 then + local gettedBackground = colorlib.alphaBlend(masterPixels[newIterator], background, alpha) + setPixel(newIterator, gettedBackground, foreground, 0x00, symbol) + --А если прозрачный else - local hex1 = pixels[currentLayer][2][y][x][1] - local hex2 = pixels[currentLayer][2][y][x][2] - local h, s, b = colorlib.HEXtoHSB(hex1); s = 0 - hex1 = colorlib.HSBtoHEX(h, s, b) - - h, s, b = colorlib.HEXtoHSB(hex2); s = 0 - hex2 = colorlib.HSBtoHEX(h, s, b) - - pixels[currentLayer][2][y][x] = {hex1, hex2, pixels[currentLayer][2][y][x][3]} + --Если его прозоачность максимальная + if masterPixels[newIterator + 2] == 0xFF then + setPixel(newIterator, background, foreground, alpha, symbol) + --Если не максимальная + else + local newAlpha = masterPixels[newIterator + 2] - (0xFF - alpha) + if newAlpha < 0x00 then newAlpha = 0x00 end + setPixel(newIterator, background, foreground, newAlpha, symbol) + end end + --Если указанная прозрачность максимальна, т.е. равна 0xFF + else + setPixel(newIterator, 0x000000, 0x000000, 0xFF, " ") end + --Рисуем пиксель из мастерпиксельса + drawPixel(x + sizes.xStartOfImage - 1, y + sizes.yStartOfImage - 1, x, y, newIterator) end + + x = x + 1 end + x = x - brushSize + y = y + 1 end end ---------------------------------------ПРОЖКА------------------------------------------------------- +------------------------------------------------ Старт программы -------------------------------------------------------------- -if arg[1] == "-o" or arg[1] == "open" then - open(arg[2]) - currentFile = arg[2] - drawAll() -elseif arg[1] == "-n" or arg[1] == "new" then - imageWidth = arg[2] - imageHeight = arg[3] - createMassiv() - currentLayer = 1 - drawAll() -else - newFile() -end +--Создаем пустой мастерпиксельс +--createEmptyMasterPixels() ---ecs.palette(5,5,0xff0000) - -local breakLags = false +--Рисуем весь интерфейс +drawAll() +new() while true do - - local eventData = {event.pull()} - breakLags = false - - if eventData[1] == "touch" or eventData[1] == "drag" then - - local coordInMassivX = eventData[3] - drawImageFromX + 1 - local coordInMassivY = eventData[4] - drawImageFromY + 1 - - if eventData[5] == 0 then - consoleText = "Левый клик мышью, x = "..eventData[3]..", y = "..eventData[4] - console(7,ySize) - if ecs.clickedAtArea(eventData[3],eventData[4],obj["tools"]["imageZone"]["x1"],obj["tools"]["imageZone"]["y1"],obj["tools"]["imageZone"]["x2"],obj["tools"]["imageZone"]["y2"]) then + local e = {event.pull()} + if e[1] == "touch" or e[1] == "drag" then + --Левый клик + if e[5] == 0 then + --Если кликнули на рисовабельную зонку + if ecs.clickedAtArea(e[3], e[4], sizes.xStartOfImage, sizes.yStartOfImage, sizes.xEndOfImage, sizes.yEndOfImage) then - if pixels[currentLayer][3] then - --ЕСЛИ КИСТЬ - if currentInstrument == 2 then - - ecs.colorTextWithBack(eventData[3],eventData[4],foreground,background,symbol) - - changePixelInMassiv(coordInMassivX,coordInMassivY,currentLayer,background,foreground,symbol) - - elseif currentInstrument == 3 then - - ecs.colorTextWithBack(eventData[3],eventData[4],transparentForeground,transparentBackground,transparentSymbol) - - changePixelInMassiv(coordInMassivX,coordInMassivY,currentLayer,transparentBackground,transparentForeground,transparentSymbol) - elseif currentInstrument == 4 then + local x, y = e[3] - sizes.xStartOfImage + 1, e[4] - sizes.yStartOfImage + 1 + local iterator = convertCoordsToIterator(x, y) - local limit = imageWidth - coordInMassivX + 1 - local text = inputText(eventData[3],eventData[4],limit) - if text == "" then text = transparentSymbol end + --Кисть + if currentInstrument == 1 then + + --Если нажата клавиша альт + if keyboard.isKeyDown(56) then + local _, _, gettedBackground = gpu.get(e[3], e[4]) + currentBackground = gettedBackground + drawColors() + + --Если обычная кисть, просто кисть, вообще всем кистям кисть + else + + brush(x, y, currentBackground, currentForeground, currentAlpha, currentSymbol) - local sText = unicode.len(text) - if sText > limit then text = unicode.sub(text,1,limit) end - - saveTextToPixels(coordInMassivX,coordInMassivY,text) - drawFromMassiv() - elseif currentInstrument == 1 then - local symbol, _, back = gpu.get(eventData[3], eventData[4]) - if symbol ~= transparentSymbol then - background = back - drawLeftToolbar() - end + --Пишем что-то в консоли + console("Кисть: клик на точку "..e[3].."x"..e[4]..", координаты в изображении: "..x.."x"..y..", индекс массива изображения: "..iterator) end + --Ластик + elseif currentInstrument == 2 then + + brush(x, y, currentBackground, currentForeground, 0xFF, currentSymbol) + + console("Ластик: клик на точку "..e[3].."x"..e[4]..", координаты в изображении: "..x.."x"..y..", индекс массива изображения: "..iterator) + + --Текст + elseif currentInstrument == 4 then + local limit = sizes.widthOfImage - x + 1 + local text = inputText(e[3], e[4], limit) + saveTextToPixels(x, y, text) + drawImage() + + --Заливка + elseif currentInstrument == 3 then + + fill(x, y, masterPixels[iterator], currentBackground) + + drawImage() - breakLags = true - else - ecs.error("Каким раком я тебе буду рисовать на выключенном слое, тупой ты сын спидозной шлюхи?") end + iterator, x, y = nil, nil, nil + end - for key,val in pairs(obj["instruments"]) do - if breakLags then break end - if ecs.clickedAtArea(eventData[3],eventData[4],obj["instruments"][key]["x1"],obj["instruments"][key]["y1"],obj["instruments"][key]["x2"],obj["instruments"][key]["y2"]) then + --Цвета + for key in pairs(obj["Colors"]) do + if ecs.clickedAtArea(e[3], e[4], obj["Colors"][key][1], obj["Colors"][key][2], obj["Colors"][key][3], obj["Colors"][key][4]) then + if key == 1 then + currentBackground = palette.draw("auto", "auto", currentBackground) or currentBackground + drawColors() + elseif key == 2 or key == 3 then + currentForeground = palette.draw("auto", "auto", currentForeground) or currentForeground + drawColors() + elseif key == 4 then + ecs.colorTextWithBack(obj["Colors"][key][1], obj["Colors"][key][2], 0xFF0000, colors.toolbar, "←→") + os.sleep(0.2) + swapColors() + end + break + end + end + + --Инструменты + for key in pairs(obj["Instruments"]) do + if ecs.clickedAtArea(e[3], e[4], obj["Instruments"][key][1], obj["Instruments"][key][2], obj["Instruments"][key][3], obj["Instruments"][key][4]) then currentInstrument = key - consoleText = "Выбран инструмент "..instruments[key][1] - drawLeftToolbar() - - breakLags = true - end - end - - for key,val in pairs(obj["colors"]) do - if breakLags then break end - if ecs.clickedAtArea(eventData[3],eventData[4],obj["colors"][key]["x1"],obj["colors"][key]["y1"],obj["colors"][key]["x2"],obj["colors"][key]["y2"]) then - local CYKA = {gpu.get(eventData[3],eventData[4])} - local chosenColor = palette.draw("auto","auto", CYKA[3]) - --ecs.error("ЦВЕТ ИЗМЕНИЛСЯ НА "..tostring(chosenColor)) - if chosenColor then - if key == 1 then - background = chosenColor - consoleText = "Основной цвет изменен на 0x"..string.format("%x",chosenColor) - else - foreground = chosenColor - consoleText = "Вторичный цвет изменен на 0x"..string.format("%x",chosenColor) - end - end - drawLeftToolbar() - - breakLags = true + drawInstruments() break end end - if ecs.clickedAtArea(eventData[3],eventData[4],obj["swapper"][1]["x1"],obj["swapper"][1]["y1"],obj["swapper"][1]["x2"],obj["swapper"][1]["y2"]) then - ecs.colorTextWithBack(3,ySize-1,0xff0000,toolbarColor,"←→") - os.sleep(0.3) - swapColors() - end - - for key,val in pairs(obj["layers"]) do - if breakLags then break end - if ecs.clickedAtArea(eventData[3],eventData[4],obj["layers"][key]["x1"],obj["layers"][key]["y"],obj["layers"][key]["x2"],obj["layers"][key]["y"]) then - if currentLayer ~= key then - currentLayer = key - else - local limit = xSize - xRightToolbar - 5 - ecs.square(xRightToolbar+4, obj["layers"][key]["y"], limit, 1, ecs.colors.blue) - local text = inputText(xRightToolbar + 4, obj["layers"][key]["y"], limit, 0xffffff) - if text == "" then - text = pixels[key][1] - else - pixels[key][1] = text - end - end - - drawLayers(drawLayersFrom) - breakLags = true - end - end - - for key,val in pairs(obj["layerEyes"]) do - if breakLags then break end - if ecs.clickedAtArea(eventData[3],eventData[4],obj["layerEyes"][key]["x1"],obj["layerEyes"][key]["y"],obj["layerEyes"][key]["x2"],obj["layerEyes"][key]["y"]) then - if pixels[key][3] then - pixels[key][3] = false - else - pixels[key][3] = true - end - - drawLayers(drawLayersFrom) - drawFromMassiv() - - breakLags = true - end - end - - for key,val in pairs(obj["top"]) do - if breakLags then break end - if ecs.clickedAtArea(eventData[3],eventData[4],obj["top"][key]["x1"],obj["top"][key]["y1"],obj["top"][key]["x2"],obj["top"][key]["y2"]) then - ecs.colorTextWithBack(obj["top"][key]["x1"],obj["top"][key]["y1"],toolbarTextColor,toolbarPressColor," "..obj["top"][key]["name"].." ") - - --------------------------------------------- - - if obj["top"][key]["name"] == "Файл" then - local action = context.menu(obj["top"][key]["x1"],obj["top"][key]["y1"]+1,{"Новый",false,"^N"},{"Открыть",false,"^O"},"-",{"Сохранить",not currentFile,"^S"},{"Сохранить как",false,"^!S"},"-",{"Выйти"}) - - if action == "Сохранить как" then - - local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Сохранить как"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Путь"}, {"Selector", 0x262626, 0x880000, "PNG", "JPG"}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK!"}}) - - if data[1] == "" or data[1] == " " or data[1] == nil then data[1] = "NewImage" end - - data[2] = "."..string.lower(data[2]) - - data[1] = data[1]..data[2] - - currentFile = data[1] - save(currentFile, data[2]) - consoleText = "Файл сохранен как "..currentFile - console(7, ySize) - - elseif action == "Открыть" then - - local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Открыть"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Путь"}, {"EmptyLine"}, {"Button", 0xbbbbbb, 0xffffff, "OK!"}) - - if data[1] ~= "" and data[1] ~= " " and data[1] ~= nil then - if fs.exists(data[1]) then - local fileFormat = ecs.getFileFormat(data[1]) - if fileFormat == ".png" or fileFormat == ".jpg" then - clearScreen(padColor) - drawLeftToolbar() - drawTopToolbar() - drawRightToolbar() - open(data[1]) - drawFromMassiv() - consoleText = "Открыт файл "..data[1] - console(7, ySize) - else - ecs.error("Формат файла не распознан.") - end - else - ecs.error("Файл "..data[1].." не существует.") - end - else - ecs.error("Что за хуйню ты ввел?") - end - elseif action == "Сохранить" then - local fileFormat = ecs.getFileFormat(currentFile) - save(currentFile, fileFormat) - consoleText = "Файл перезаписан как "..currentFile - console(7, ySize) - elseif action == "Выйти" then - ecs.clearScreen(0x000000) - gpu.setForeground(0xffffff) - gpu.set(1, 1, "") - return 0 - elseif action == "Новый" then - newFile() - end - - elseif obj["top"][key]["name"] == "Фильтры" then - local action = context.menu(obj["top"][key]["x1"],obj["top"][key]["y1"]+1, {"Инверсия цвета"}, {"Черно-белый фильтр"}) - if action == "Инверсия цвета" then - filter("invert") - elseif action == "Черно-белый фильтр" then - filter("b&w") - end - drawFromMassiv() - elseif obj["top"][key]["name"] == "Инструменты" then - local action = context.menu(obj["top"][key]["x1"],obj["top"][key]["y1"]+1, {"Пипетка", false, "Alt"}, {"Кисть", false, "B"}, {"Ластик", false, "E"}, {"Текст", false, "T"}) - - if action == "Пипетка" then - currentInstrument = 1 - elseif action == "Кисть" then - currentInstrument = 2 - elseif action == "Ластик" then - currentInstrument = 3 - elseif action == "Текст" then - currentInstrument = 4 - end - - drawLeftToolbar() - end - - --------------------------------------------- - - drawTopToolbar() + --Верхний меню-бар + for key in pairs(obj["TopMenu"]) do + if ecs.clickedAtArea(e[3], e[4], obj["TopMenu"][key][1], obj["TopMenu"][key][2], obj["TopMenu"][key][3], obj["TopMenu"][key][4]) then + ecs.square(obj["TopMenu"][key][1] - 1, obj["TopMenu"][key][2], unicode.len(key) + 2, 1, ecs.colors.blue) + ecs.colorText(obj["TopMenu"][key][1], obj["TopMenu"][key][2], 0xffffff, key) + local action - breakLags = true - end - end - - for key,val in pairs(obj["layerButtons"]) do - if breakLags then break end - if ecs.clickedAtArea(eventData[3],eventData[4],obj["layerButtons"][key]["x1"],obj["layerButtons"][key]["y"],obj["layerButtons"][key]["x2"],obj["layerButtons"][key]["y"]) then - ecs.colorTextWithBack(obj["layerButtons"][key]["x1"],obj["layerButtons"][key]["y"],0xff0000,toolbarPressColor+0x111111,key) - os.sleep(0.3) - if key == buttons[5] then - addElementToLayers(discription) - drawLayers(drawLayersFrom) - elseif key == buttons[6] then - deleteLayer(currentLayer) - elseif key == buttons[3] then - duplicateLayer(currentLayer) - drawLayers(drawLayersFrom) - elseif key == buttons[1] then - moveLayer(currentLayer,"up") - drawLayers(drawLayersFrom) - elseif key == buttons[2] then - moveLayer(currentLayer,"down") - drawLayers(drawLayersFrom) - elseif key == buttons[4] then - joinLayer(currentLayer) - drawLayers(drawLayersFrom) + if key == "Файл" then + action = context.menu(obj["TopMenu"][key][1] - 1, obj["TopMenu"][key][2] + 1, {"Новый"}, {"Открыть"}, "-", {"Сохранить", (savePath == nil)}, {"Сохранить как"}, "-", {"Выход"}) + elseif key == "Изображение" then + action = context.menu(obj["TopMenu"][key][1] - 1, obj["TopMenu"][key][2] + 1, {"Отразить по горизонтали"}, {"Отразить по вертикали"}, "-", {"Инвертировать цвета"}, {"Черно-белый фильтр"}) + elseif key == "Инструменты" then + action = context.menu(obj["TopMenu"][key][1] - 1, obj["TopMenu"][key][2] + 1, {"Кисть"}, {"Ластик"}, {"Заливка"}, {"Текст"}) + elseif key == "О программе" then + ecs.universalWindow("auto", "auto", 36, 0xeeeeee, true, {"EmptyLine"}, {"CenterText", 0x880000, "Photoshop v3.0 (public beta)"}, {"EmptyLine"}, {"CenterText", 0x262626, "Авторы:"}, {"CenterText", 0x555555, "Тимофеев Игорь"}, {"CenterText", 0x656565, "vk.com/id7799889"}, {"CenterText", 0x656565, "Трифонов Глеб"}, {"CenterText", 0x656565, "vk.com/id88323331"}, {"EmptyLine"}, {"CenterText", 0x262626, "Тестеры:"}, {"CenterText", 0x656565, "Шестаков Тимофей"}, {"CenterText", 0x656565, "vk.com/id113499693"}, {"CenterText", 0x656565, "Вечтомов Роман"}, {"CenterText", 0x656565, "vk.com/id83715030"}, {"CenterText", 0x656565, "Омелаенко Максим"}, {"CenterText", 0x656565, "vk.com/paladincvm"}, {"EmptyLine"},{"Button", {0xbbbbbb, 0xffffff, "OK"}}) end - breakLags = true + if action == "Выход" then + ecs.prepareToExit() + return + elseif action == "Отразить по горизонтали" then + doFlip(true) + elseif action == "Отразить по вертикали" then + doFlip(false) + elseif action == "Инвертировать цвета" then + invertColors() + elseif action == "Черно-белый фильтр" then + blackAndWhite() + elseif action == "Ластик" then + currentInstrument = 2 + drawInstruments() + elseif action == "Кисть" then + currentInstrument = 1 + drawInstruments() + elseif action == "Текст" then + currentInstrument = 4 + drawInstruments() + elseif action == "Заливка" then + currentInstrument = 3 + drawInstruments() + elseif action == "Новый" then + new() + + elseif action == "Сохранить как" then + local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Сохранить как"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Путь"}, {"Selector", 0x262626, 0x880000, ".PIC", ".RAWPIC"}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}}) + data[1] = data[1] or "Untitled" + data[2] = unicode.lower(data[2] or "PIC") + local fileName = data[1]..data[2] + image.save(fileName, masterPixels) + savePath = fileName + + elseif action == "Сохранить" then + image.save(savePath, masterPixels) + + elseif action == "Открыть" then + local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Открыть"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Путь"}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}}) + local fileFormat = ecs.getFileFormat(data[1]) + + if not data[1] then + ecs.error("Некорректное имя файла!") + elseif not fs.exists(data[1]) then + ecs.error("Файл\""..data[1].."\" не существует!") + elseif fileFormat ~= ".pic" and fileFormat ~= ".rawpic" then + ecs.error("Формат файла \""..fileFormat.."\" не поддерживается!") + else + masterPixels = image.load(data[1]) + reCalculateImageSizes() + drawImage() + end + end + + drawTopMenu() + break end end + + --Топбар + for key in pairs(obj["TopBarInputs"]) do + if ecs.clickedAtArea(e[3], e[4], obj["TopBarInputs"][key][1], obj["TopBarInputs"][key][2], obj["TopBarInputs"][key][3], obj["TopBarInputs"][key][4]) then + local input = ecs.inputText(obj["TopBarInputs"][key][1], obj["TopBarInputs"][key][2], obj["TopBarInputs"][key][5], "", 0xffffff, 0x262626) + input = tonumber(input) + + if input then + if key == 1 then + if input > 0 and input < 10 then currentBrushSize = input end + elseif key == 2 then + if input > 0 and input <= 255 then currentAlpha = input end + end + end + + drawTopBar() + + break + end + end + else + --Если кликнули на рисовабельную зонку + if ecs.clickedAtArea(e[3], e[4], sizes.xStartOfImage, sizes.yStartOfImage, sizes.xEndOfImage, sizes.yEndOfImage) then + + local x, y, width, height = e[3], e[4], 30, 12 + + --А это чтоб за края экрана не лезло + if y + height >= sizes.ySize then y = sizes.ySize - height end + if x + width + 1 >= sizes.xSize then x = sizes.xSize - width - 1 end + + currentBrushSize, currentAlpha = table.unpack(ecs.universalWindow(x, y, width, 0xeeeeee, true, {"EmptyLine"}, {"CenterText", 0x880000, "Параметры кисти"}, {"Slider", 0x262626, 0x880000, 1, 10, currentBrushSize, "Размер: ", " px"}, {"Slider", 0x262626, 0x880000, 0, 255, currentAlpha, "Прозрачность: ", ""}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}})) + drawTopBar() + end end - elseif eventData[1] == "key_down" then - --КЛАВИША SPACE - if eventData[4] == 57 then - consoleText = "Интерфейс программы перерисован" + elseif e[1] == "key_down" then + --Стрелки + if e[4] == 200 then + move("up") + elseif e[4] == 208 then + move("down") + elseif e[4] == 203 then + move("left") + elseif e[4] == 205 then + move("right") + --Пробел + elseif e[4] == 57 then drawAll() - --КЛАВИША ENTER - elseif eventData[4] == 28 then - newFile() - --КЛАВИША BACKSPACE - elseif eventData[4] == 14 then - deleteLayer(currentLayer) - --КЛАВИША N - elseif eventData[4] == 49 then - addElementToLayers(discription) - drawLayers(drawLayersFrom) - --КЛАВИША X - elseif eventData[4] == 45 then + --X + elseif e[4] == 45 then swapColors() - --КЛАВИША D - elseif eventData[4] == 32 then - background = 0x000000 - foreground = 0xffffff - drawLeftToolbar() - --КЛАВИША T - elseif eventData[4] == 20 then - currentInstrument = 4 - drawLeftToolbar() - --КЛАВИША B - elseif eventData[4] == 48 then - currentInstrument = 2 - drawLeftToolbar() - --КЛАВИША E - elseif eventData[4] == 18 then - currentInstrument = 3 - drawLeftToolbar() - --Клавиша ALT - elseif eventData[4] == 56 then + --B + elseif e[4] == 48 then currentInstrument = 1 - drawLeftToolbar() - elseif eventData[4] == 200 then - drawImageFromY = drawImageFromY - 1 - drawFromMassiv(true) - elseif eventData[4] == 208 then - drawImageFromY = drawImageFromY + 1 - drawFromMassiv(true) - elseif eventData[4] == 203 then - drawImageFromX = drawImageFromX - 1 - drawFromMassiv(true) - elseif eventData[4] == 205 then - drawImageFromX = drawImageFromX + 1 - drawFromMassiv(true) - end - - elseif eventData[1] == "scroll" then - - --[[local a1 = gpu.getResolution() - - local a2 = gpu.maxResolution()Ы - local currentScale = a1/a2 - eventData[3] = math.floor(eventData[3]*0.5/currentScale) - eventData[4] = math.floor(eventData[4]*0.5/currentScale)]] - - if ecs.clickedAtArea(eventData[3],eventData[4],obj["layersZone"][1]["x1"],obj["layersZone"][1]["y1"],obj["layersZone"][1]["x2"],obj["layersZone"][1]["y2"]) then - scrollLayers(eventData[5]) - + drawInstruments() + --E + elseif e[4] == 18 then + currentInstrument = 2 + drawInstruments() + --T + elseif e[4] == 20 then + currentInstrument = 4 + drawInstruments() + + --G + elseif e[4] == 34 then + currentInstrument = 3 + drawInstruments() + --D + elseif e[4] == 32 then + currentBackground = 0x000000 + currentForeground = 0xFFFFFF + currentAlpha = 0x00 + drawColors() end + elseif e[1] == "scroll" then end end ---------------------------------------ВЫХОД ИЗ ПРОЖКИ------------------------------------------------------ +------------------------------------------------ Выход из программы -------------------------------------------------------------- + + + -clearScreen(0x000000) -gpu.setBackground(0x000000) -gpu.setForeground(0xffffff) diff --git a/lib/image.lua b/lib/image.lua index b5ea1f83..f08df030 100644 --- a/lib/image.lua +++ b/lib/image.lua @@ -1,69 +1,121 @@ -local fs = require("filesystem") + +-------------------------------------------- OCIF Image Format ----------------------------------------------------------- + +local copyright = [[ + + Автор: Pirnogion + VK: https://vk.com/id88323331 + Соавтор: IT + VK: https://vk.com/id7799889 + +]] + +--------------------------------------- Подгрузка библиотек -------------------------------------------------------------- + +local component = require("component") local unicode = require("unicode") -local gpu = require("component").gpu +local fs = require("filesystem") +local gpu = component.gpu local image = {} -local transparentSymbol = "#" +-------------------------------------------- Переменные ------------------------------------------------------------------- ---------------------Все, что касается сжатого формата изображений (у нас он назван "JPG")---------------------------------------------------------------------------------- - ---OC image format .ocif by Pirnogion +--Cигнатура OCIF-файла 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 constants = { + elementCount = 4, + byteSize = 8, + nullChar = 0, + rawImageLoadStep = 19, + fileOpenError = "Can't open file", + compressedFileFormat = ".pic", + rawFileFormat = ".rawpic", +} -local imageAPI = {} +---------------------------------------- Локальные функции ------------------------------------------------------------------- ---Выделить бит-терминатор в первом байте utf8 символа: 1100 0010 --> 0010 0000 -local function selectTerminateBit( byte ) - local x = bit32.band( bit32.bnot(byte), 0x000000FF ) +--Выделить бит-терминатор в первом байте UTF-8 символа: 1100 0010 --> 0010 0000 +local function selectTerminateBit_l() + local prevByte = nil + local prevTerminateBit = nil - 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) ) + return function( byte ) + local x, terminateBit = nil + if ( prevByte == byte ) then + return prevTerminateBit + end - return x - bit32.rshift(x, 1) + 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, 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) + readedByte = string.byte( file:read(1) or constants.nullChar ) + readedNumber = readedNumber + bit32.lshift(readedByte, i * constants.byteSize - constants.byteSize) end return readedNumber end ---Преобразует цвет в hex записи в rgb запись -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 +--Преобразует цвет из HEX-записи в RGB-запись +local function HEXtoRGB(color) + return bit32.rshift( color, 16 ), bit32.rshift( bit32.band(color, 0x00ff00), 8 ), bit32.band(color, 0x0000ff) 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 ) +--Аналогично, но из RGB в HEX +local function RGBtoHEX(rr, gg, bb) + return bit32.lshift(rr, 16) + bit32.lshift(gg, 8) + bb +end + +--Смешивание двух цветов на основе альфа-канала второго +local function alphaBlend(back_color, front_color, alpha_channel) + local INVERTED_ALPHA_CHANNEL = 255 - alpha_channel + + local back_color_rr, back_color_gg, back_color_bb = HEXtoRGB(back_color) + local front_color_rr, front_color_gg, front_color_bb = HEXtoRGB(front_color) + + local blended_rr = front_color_rr * INVERTED_ALPHA_CHANNEL / 255 + back_color_rr * alpha_channel / 255 + local blended_gg = front_color_gg * INVERTED_ALPHA_CHANNEL / 255 + back_color_gg * alpha_channel / 255 + local blended_bb = front_color_bb * INVERTED_ALPHA_CHANNEL / 255 + back_color_bb * alpha_channel / 255 + + return RGBtoHEX( blended_rr, blended_gg, blended_bb ) +end + +--Подготавливает цвета и символ для записи в файл сжатого формата +local function encodePixel(background, foreground, alpha, char) + --Расхерачиваем жирные цвета в компактные цвета + local ascii_background1, ascii_background2, ascii_background3 = HEXtoRGB(background) + local ascii_foreground1, ascii_foreground2, ascii_foreground3 = 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 NULL_CHAR - - return rr_fg, gg_fg, bb_fg, rr_bg, gg_bg, bb_bg, ascii_char1, ascii_char2, ascii_char3, ascii_char4, ascii_char5, ascii_char6 + 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 ---Декодирование utf8 символа +--Декодирование UTF-8 символа local function decodeChar(file) local first_byte = readBytes(file, 1) local charcode_array = {first_byte} @@ -89,310 +141,466 @@ local function decodeChar(file) return string.char( table.unpack( charcode_array ) ) end ---Чтение из файла, возвращет массив изображения -local function loadJPG(path) - local kartinka = {} - local file = io.open(path, "rb") +--Правильное конвертирование 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 + +------------------------------ Все, что касается сжатого формата ------------------------------------------------------------ + +-- Запись в файл сжатого OCIF-формата изображения +function image.saveCompressed(path, picture) + local encodedPixel + local file = assert( io.open(path, "w"), constants.fileOpenError ) + + file:write( table.unpack( ocif_signature_expand ) ) + 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 (подробнее о типах см. конец файла) +function image.loadCompressed(path) + local picture = {} + local file = assert( io.open(path, "rb"), constants.fileOpenError ) + + --Проверка файла на соответствие сигнатуры 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) + --Читаем ширину и высоту файла + picture.width = readBytes(file, 1) + picture.height = 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( file ) - end + 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 kartinka + return picture end ---Рисование сжатого формата -function image.drawJPG(x, y, image1) - x = x - 1 - y = y - 1 +------------------------------ Все, что касается сырого формата ------------------------------------------------------------ - local image2 = image.convertImageToGroupedImage(image1) +--Сохранение в файл сырого формата изображения типа 2 (подробнее о типах см. конец файла) +function image.saveRaw(path, picture) + local file = assert( io.open(path, "w"), constants.fileOpenError ) - --Перебираем массив с фонами - 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 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], " ") - 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, 1 do - for x = 1, kartinka.width, 1 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 + xPos = xPos + 1 + if xPos > picture.width then + xPos = 1 + yPos = yPos + 1 + file:write("\n") end end file:close() end ----------------------------Все, что касается несжатого формата (у нас он назван "PNG")------------------------------------------------------- +--Загрузка из файла сырого формата изображения типа 2 (подробнее о типах см. конец файла) +function image.loadRaw(path) + local file = assert( io.open(path, "r"), constants.fileOpenError ) + local picture = {} --- Перевод 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 + local background, foreground, alpha, symbol, sLine + local lineCounter = 0 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 + 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[background] = optimizedPicture[background] or {} + optimizedPicture[background][foreground] = optimizedPicture[background][foreground] or {} + table.insert(optimizedPicture[background][foreground], xPos) + table.insert(optimizedPicture[background][foreground], yPos) + table.insert(optimizedPicture[background][foreground], alpha) + table.insert(optimizedPicture[background][foreground], symbol) + --Если 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 + +--Нарисовать по указанным координатам картинку указанной ширины и высоты для теста +function image.drawRandomImage(x, y, width, height) + local picture = {} + local symbolArray = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "А", "Б", "В", "Г", "Д", "Е", "Ж", "З", "И", "Й", "К", "Л", "И", "Н", "О", "П", "Р", "С", "Т", "У", "Ф", "Х", "Ц", "Ч", "Ш", "Щ", "Ъ", "Ы", "Ь", "Э", "Ю", "Я"} + picture.width = width + picture.height = height + local background, foreground, symbol + for j = 1, height do + for i = 1, width do + background = math.random(0x000000, 0xffffff) + foreground = math.random(0x000000, 0xffffff) + symbol = symbolArray[math.random(1, #symbolArray)] + + table.insert(picture, background) + table.insert(picture, foreground) + table.insert(picture, 0x00) + table.insert(picture, symbol) + end + end + image.draw(x, y, picture) + return picture +end + +----------------------------------------- Основные функции программы ------------------------------------------------------------------- + +--Сохранить изображение любого поддерживаемого формата +function image.save(path, picture) + --Создать папку под файл, если ее нет + fs.makeDirectory(fs.path(path)) + --Получаем формат указанного файла + local fileFormat = getFileFormat(path) + --Проверяем соответствие формата файла + if fileFormat == constants.compressedFileFormat then + image.saveCompressed(path, picture) + elseif fileFormat == constants.rawFileFormat then + image.saveRaw(path, picture) + else + error("Unsupported file format.\n") + end +end + +--Загрузить изображение любого поддерживаемого формата +function image.load(path) + --Кинуть ошибку, если такого файла не существует + if not fs.exists(path) then error("File \""..path.."\" does not exists.\n") end + --Получаем формат указанного файла + local fileFormat = getFileFormat(path) + --Проверяем соответствие формата файла + if fileFormat == constants.compressedFileFormat then + return image.loadCompressed(path) + elseif fileFormat == constants.rawFileFormat then + return image.loadRaw(path) + else + error("Unsupported file format.\n") + end +end + +--Отрисовка изображения типа 3 (подробнее о типах см. конец файла) +function image.draw(x, y, rawPicture) + --Конвертируем в групповое изображение + local picture = image.convertToGroupedImage(rawPicture) + --Все как обычно + x, y = x - 1, y - 1 + --Переменные, чтобы в цикле эту парашу не создавать + local currentBackground, xPos, yPos, alpha, symbol + local _, _ + --Перебираем все цвета фона + for background in pairs(picture) do + --Заранее ставим корректный цвет фона + gpu.setBackground(background) + --Перебираем все цвета текста + for foreground in pairs(picture[background]) do + --Ставим сразу и корректный цвет текста + gpu.setForeground(foreground) + --Перебираем все пиксели + for i = 1, #picture[background][foreground], 4 do + --Получаем временную репрезентацию + xPos, yPos, alpha, symbol = picture[background][foreground][i], picture[background][foreground][i + 1], picture[background][foreground][i + 2], picture[background][foreground][i + 3] + --Если альфа имеется, но она не совсем прозрачна + if alpha > 0x00 and alpha < 0xFF then + _, _, currentBackground = gpu.get(x + xPos, y + yPos) + currentBackground = alphaBlend(currentBackground, background, alpha) + gpu.setBackground(currentBackground) + --Рисуем символ на экране + gpu.set(x + xPos, y + yPos, symbol) + --Если альфа отсутствует + elseif alpha == 0x00 then + if currentBackground ~= background then + currentBackground = background + gpu.setBackground(currentBackground) + end + --Рисуем символ на экране + gpu.set(x + xPos, y + yPos, symbol) + end + + --Выгружаем сгруппированное изображение из памяти + picture[background][foreground][i], picture[background][foreground][i + 1], picture[background][foreground][i + 2], picture[background][foreground][i + 3] = nil, nil, nil, nil + end + --Выгружаем данные о текущем цвете текста из памяти + picture[background][foreground] = nil + end + --Выгружаем данные о текущем фоне из памяти + picture[background] = nil + end +end + +local function loadOldPng(path) + local massiv = {} + local file = io.open(path, "r") + local lineCounter = 0 + local sLine = 0 + for line in file:lines() do + sLine = unicode.len(line) + + for i = 1, sLine, 16 do + table.insert(massiv, tonumber("0x" .. unicode.sub(line, i, i + 5))) + table.insert(massiv, tonumber("0x" .. unicode.sub(line, i + 7, i + 12))) + table.insert(massiv, 0x00) + table.insert(massiv, tonumber("0x" .. unicode.sub(line, i + 14, i + 14))) end lineCounter = lineCounter + 1 end - --Закрываем файл - file:close() - --Очищаем оперативку - pixelCounter, lineCounter, dlinaStroki = nil, nil, nil + massiv.width = sLine / 16 + massiv.height = lineCounter - return newPNGMassiv + return massiv 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], #PNGMassiv - - 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.error("Создаю и рисую картинку!") +-- local generatedPic = image.drawRandomImage(1, 1, 160, 50) +-- ecs.error("Сохраняю созданную картинку в формате .pic!") -- ecs.prepareToExit() --- ecs.centerText("xy", 0, "Сохранил скрин. Ща загружу фотку и нарисую его. Внимание!") --- os.sleep(2) +-- image.save("1.pic", generatedPic) +-- ecs.error("Сохраняю созданную картинку в формате .rawpic!") -- ecs.prepareToExit() --- image.draw(2, 2, image.load(pathToScreen)) +-- image.save("1.rawpic", generatedPic) +-- ecs.error("Загружаю сохраненную картинку в формате .pic!") +-- ecs.prepareToExit() +-- local loadedPic = image.load("1.pic") +-- image.draw(1, 1, loadedPic) +-- ecs.error("Загружаю сохраненную картинку в формате .rawpic!") +-- ecs.prepareToExit() +-- local loadedPic = image.load("1.rawpic") +-- image.draw(1, 1, loadedPic) + +------------------------------------------ Типы массивов изображений --------------------------------------------------- + + +--[[ + + Тип 1: + + Основной формат изображения, линейная запись данных о пикселях, + сжатие двух цветов и альфа-канала в одно число. Минимальный расход + оперативной памяти, однако для изменения цвета требует декомпрессию. + + Структура: + + local picture = { + width = ширина изображения, + height = высота изображения, + Сжатые цвета и альфа-канал, + Символ, + Сжатые цвета и альфа-канал, + Символ, + ... + } + + Пример: + + local picture = { + width = 2, + height = 2, + 0xff00aa, + "Q", + 0x88aacc, + "W", + 0xff00aa, + "E", + 0x88aacc, + "R" + } + + Тип 2 конвертируется только в тип 3 и только для отрисовки на экране: + Изображение типа 3 = image.convertToGroupedImage( Сюда кидаем массив изображения типа 2 ) + + Тип 2 (сгруппированный по цветам формат, ипользуется только для отрисовки изображения): + + Структура: + local picture = { + Цвет фона 1 = { + Цвет текста 1 = { + Координата по X, + Координата по Y, + Альфа-канал, + Символ, + Координата по X, + Координата по Y, + Альфа-канал, + Символ, + ... + }, + Цвет текста 2 = { + Координата по X, + Координата по Y, + Альфа-канал, + Символ, + Координата по X, + Координата по Y, + Альфа-канал, + Символ, + ... + }, + ... + }, + Цвет фона 2 = { + Цвет текста 1 = { + Координата по X, + Координата по Y, + Альфа-канал, + Символ, + Координата по X, + Координата по Y, + Альфа-канал, + Символ, + ... + }, + Цвет текста 2 = { + Координата по X, + Координата по Y, + Альфа-канал, + Символ, + Координата по X, + Координата по Y, + Альфа-канал, + Символ, + ... + }, + ... + }, + } + + Пример: + local picture = { + 0xffffff = { + 0xaabbcc = { + 1, + 1, + 0x00, + "Q", + 12, + 12, + 0xaa, + "W" + }, + 0x88aa44 = { + 5, + 5, + 0xcc, + "E", + 12, + 12, + 0x00, + "R" + } + }, + 0x444444 = { + 0x112233 = { + 40, + 20, + 0x00, + "T", + 12, + 12, + 0xaa, + "Y" + }, + 0x88aa44 = { + 5, + 5, + 0xcc, + "U", + 12, + 12, + 0x00, + "I" + } + } + } + +]] + +------------------------------------------------------------------------------------------------------------------------ return image @@ -404,7 +612,3 @@ return image - - - -