------------------------------------------------ Информешйн, епта -------------------------------------------------------------- local photoshopVersion = "Photoshop v6.6" local copyright = [[ Photoshop v6.6 для OpenComputers Автор: ECS Контактый адрес: https://vk.com/id7799889 Соавтор: Pornogion Контактый адрес: https://vk.com/id88323331 Что нового в версии 6.6: - Программа адаптирована под работу с нумерически индексированным форматом изображения - Добавлена поддержка кодирования формата OCIF6 Что нового в версии 6.5: - Палитра заменена на более быструю и стильную, работающую на тройном буфере - Добавлена возможность загрузки изображения из строки, созданной методом сохранения StringImage Что нового в версии 6.4: - Добавлена возможность выбора цвета сетки прозрачности во вкладке "Вид" Что нового в версии 6.3: - Добавлена поддержка языковых пакетов Что нового в версии 6.2: - Добавлен суб-инструмент "Полигон" - Улучшен инструмент "Выделение", теперь можно выделять области с шириной или высотой, равными 1 Что нового в версии 6.1: - Добавлен суб-инструмент "Эллипс" Что нового в версии 6.0: - Добавлен иструмент "Фигура", включающий в себя линию, прямоугольник и рамку - Добавлен фильтр размытия по Гауссу - Переработана концепция работы с выделениями Что нового в версии 5.1: - Цветовая гамма программы изменена на более детальную - Добавлена информационная мини-панель к инструменту "выделение" - Добавлена информация о размере изображения, отображаемая под самим изображением - Ускорен алгоритм рисования кистью и ластиком Что нового в версии 5.0: - Добавлен инструмент "выделение" и несколько функций для работы с ним - Добавлено меню "Горячие клавиши", подсказывающее, как можно удобнее работать с программой Что нового в версии 4.0: - Программа переведена на библиотеку тройного буфера, скорость работы увеличена в десятки раз - Добавлены функции обрезки, расширения, поворота и отражения картинки - Добавлены функции тона/насыщенности, цветового баланса и наложения фотофильтра ]] copyright = nil ------------------------------------------------ Библиотеки -------------------------------------------------------------- local ecs = require("ECSAPI") local MineOSCore = require("MineOSCore") local GUI = require("GUI") local fs = require("filesystem") local unicode = require("unicode") local image = require("image") local component = require("component") local keyboard = require("keyboard") local buffer = require("doubleBuffering") local color = require("color") local palette = require("palette") local event = require("event") ------------------------------------------------ Переменные -------------------------------------------------------------- --Инициализируем библиотеку двойного буфера buffer.start() --Массив локалиации local localization = MineOSCore.getCurrentApplicationLocalization() --Массив инфы о выделении local selection --Получаем аргументы программы local args = {...} --Массив главного изображения local masterPixels = { 0, 0, } --Базовая цветовая схема программы local colors = { leftToolbar = 0x3c3c3c, leftToolbarButton = 0x2d2d2d, leftToolbarButtonText = 0xeeeeee, topToolbar = 0x4b4b4b, drawingArea = 0x1e1e1e, console = 0x2d2d2d, consoleText = 0x999999, transparencyWhite = 0xffffff, transparencyGray = 0xcccccc, transparencyVariable = 0xffffff, oldBackground = 0x0, oldForeground = 0x0, topMenu = 0xeeeeee, topMenuText = 0x262626, } --Различные константы и размеры тулбаров и кликабельных зон local sizes = { widthOfLeftBar = 6, } sizes.xStartOfDrawingArea = sizes.widthOfLeftBar + 1 sizes.xEndOfDrawingArea = buffer.screen.width sizes.yStartOfDrawingArea = 2 sizes.yEndOfDrawingArea = buffer.screen.height - 1 sizes.widthOfDrawingArea = sizes.xEndOfDrawingArea - sizes.xStartOfDrawingArea + 1 sizes.heightOfDrawingArea = sizes.yEndOfDrawingArea - sizes.yStartOfDrawingArea + 1 sizes.heightOfLeftBar = buffer.screen.height - 1 sizes.sizeOfPixelData = 4 --Для изображения local function reCalculateImageSizes(x, y) sizes.xStartOfImage = x or 9 sizes.yStartOfImage = y or 6 sizes.xEndOfImage = sizes.xStartOfImage + image.getWidth(masterPixels) - 1 sizes.yEndOfImage = sizes.yStartOfImage + image.getHeight(masterPixels) - 1 end reCalculateImageSizes() --Инструменты local instruments = { "M", "B", "E", "F", "T", "S", } sizes.heightOfInstrument = 3 sizes.yStartOfInstruments = 2 local currentInstrument = 2 local currentBackground = 0x000000 local currentForeground = 0xFFFFFF local currentAlpha = 0x00 local currentSymbol = " " local currentBrushSize = 1 local savePath local currentShape local currentPolygonCountOfEdges local showTransparencyGrid = true ------------------------------------------------ Функции отрисовки -------------------------------------------------------------- --Объекты для тача local obj = {} local function newObj(class, name, ...) obj[class] = obj[class] or {} obj[class][name] = {...} end --Функция-сваппер переменных, пока что юзается только в выделении --А, не, наебал! --Вон, ниже тоже юзается! Ха, удобненько local function swap(a, b) return b, a end --Адекватное округление, ибо в луашке нет такого, ХАХ local function round(num) if num >= 0 then return math.floor(num + 0.5) else return math.ceil(num - 0.5) end end --Отрисовка "прозрачной зоны", этакая сеточка чередующаяся local function drawTransparentZone(x, y) if showTransparencyGrid then y = y - 1 local stro4ka1 = "" local stro4ka2 = "" if image.getWidth(masterPixels) % 2 == 0 then stro4ka1 = string.rep("▒ ", math.floor(image.getWidth(masterPixels) / 2)) stro4ka2 = stro4ka1 else stro4ka1 = string.rep("▒ ", math.floor(image.getWidth(masterPixels) / 2)) stro4ka2 = stro4ka1 .. "▒" end for i = 1, image.getHeight(masterPixels) do if i % 2 == 0 then buffer.square(x, y + i, image.getWidth(masterPixels), 1, colors.transparencyWhite, colors.transparencyGray, " ") buffer.text(x + 1, y + i, colors.transparencyGray, stro4ka1) else buffer.square(x, y + i, image.getWidth(masterPixels), 1, colors.transparencyWhite, colors.transparencyGray) buffer.text(x, y + i, colors.transparencyGray, stro4ka2) end end else buffer.square(x, y, image.getWidth(masterPixels), image.getHeight(masterPixels), colors.transparencyWhite, 0x000000, " ") end end --Банальная заливка фона local function drawBackground() buffer.square(sizes.xStartOfDrawingArea, sizes.yStartOfDrawingArea, sizes.widthOfDrawingArea, sizes.heightOfDrawingArea + 1, colors.drawingArea, 0xFFFFFF, " ") end --Отрисовка цветов local function drawColors() local xPos, yPos = 2, buffer.screen.height - 4 buffer.square(xPos, yPos, 3, 2, currentBackground, 0xFFFFFF, " ") buffer.square(xPos + 3, yPos + 1, 1, 2, currentForeground, 0xFFFFFF, " ") buffer.square(xPos + 1, yPos + 2, 2, 1, currentForeground, 0xFFFFFF, " ") buffer.text(xPos + 1, yPos + 3, 0xaaaaaa, "←→") 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() --Рисуем подложечку buffer.square(1, 5, sizes.widthOfLeftBar, sizes.heightOfLeftBar, colors.leftToolbar, 0xFFFFFF, " ") --Рисуем инструменты local yPos = sizes.yStartOfInstruments for i = 1, #instruments do if currentInstrument == i then buffer.square(1, yPos, sizes.widthOfLeftBar, sizes.heightOfInstrument, colors.leftToolbarButton, 0xFFFFFF, " ") else buffer.square(1, yPos, sizes.widthOfLeftBar, sizes.heightOfInstrument, colors.leftToolbar, 0xFFFFFF, " ") end buffer.text(3, yPos + 1, colors.leftToolbarButtonText, instruments[i]) newObj("Instruments", i, 1, yPos, sizes.widthOfLeftBar, yPos + sizes.heightOfInstrument - 1) yPos = yPos + sizes.heightOfInstrument end --И цвета drawColors() end --Отрисовка верхнего меню local function drawTopMenu() obj.menu = GUI.menu(1, 1, buffer.screen.width, colors.topMenu, colors.topMenuText, 0x3366CC, 0xFFFFFF, 0) obj.menu:addItem("PS", ecs.colors.blue) obj.menu:addItem(localization.file) obj.menu:addItem(localization.image) obj.menu:addItem(localization.view) obj.menu:addItem(localization.hotkeys) obj.menu:addItem(localization.about) obj.menu:draw() end --Функция, создающая пустой массив изображения на основе указанных ранее длины и ширины local function createEmptyMasterPixels() --Создаем пустой мастерпиксельс for j = 1, image.getHeight(masterPixels) * image.getWidth(masterPixels) do table.insert(masterPixels, 0x010101) table.insert(masterPixels, 0x010101) table.insert(masterPixels, 0xFF) table.insert(masterPixels, " ") end end --Мини-консолька для отладки, сообщающая снизу, че происходит ваще local function console(text) buffer.square(sizes.xStartOfDrawingArea, buffer.screen.height, sizes.widthOfDrawingArea, 1, colors.console, colors.consoleText, " ") local _, total, used = ecs.getInfoAboutRAM() local RAMText = used .. "/" .. total .. " KB RAM" buffer.text(sizes.xEndOfDrawingArea - unicode.len(RAMText), buffer.screen.height, colors.consoleText, RAMText) buffer.text(sizes.xStartOfDrawingArea + 1, buffer.screen.height, colors.consoleText, text) end --Функция, берущая указанный пиксель из массива изображения и рисующая его в буфере корректно, --т.е. с учетом прозрачности и т.п. local function drawPixel(x, y, xPixel, yPixel, iterator) --Получаем тукущие данные о пикселе local background, foreground, alpha, symbol = masterPixels[iterator], masterPixels[iterator + 1], masterPixels[iterator + 2], masterPixels[iterator + 3] --Если пиксель не прозрачный if alpha == 0x00 then buffer.set(x, y, background, foreground, symbol) --Если пиксель прозрачнее непрозрачного elseif alpha > 0x00 then local blendColor if xPixel % 2 == 0 then if yPixel % 2 == 0 then blendColor = colors.transparencyGray else blendColor = colors.transparencyWhite end else if yPixel % 2 == 0 then blendColor = colors.transparencyWhite else blendColor = colors.transparencyGray end end buffer.set(x, y, color.blend(blendColor, background, alpha / 0xFF), foreground, symbol) end background, foreground, alpha, symbol = nil, nil, nil, nil end --Пиздюлинка, показывающая размер текста и тыпы local function drawTooltip(x, y, ...) local texts = {...} --Рассчитываем ширину пиздюлинки local maxWidth = 0; for i = 1, #texts do maxWidth = math.max(maxWidth, unicode.len(texts[i])) end --Рисуем пиздюлинку buffer.square(x, y, maxWidth + 2, #texts, 0x000000, 0xFFFFFF, " ", 69); x = x + 1 for i = 1, #texts do buffer.text(x, y, 0xFFFFFF, texts[i]); y = y + 1 end end --Функция для отрисовки выделения соотв. инструментом local function drawSelection() local color = 0x000000 local xStart, yStart = sizes.xStartOfImage + selection.x - 1, sizes.yStartOfImage + selection.y - 1 local xEnd, yEnd = xStart + selection.width - 1, yStart + selection.height - 1 local currentBackground local function nextColor() if color == 0x000000 then color = 0xFFFFFF else color = 0x000000 end end --"━" --"┃" local function drawSelectionSquare(x1, y1, x2, y2, xStep, yStep, symbol) for y = y1, y2, yStep do for x = x1, x2, xStep do local background = buffer.get(x, y) buffer.set(x, y, background, color, symbol) nextColor() end end end local function drawCornerPoint(x, y, symbol) local background = buffer.get(x, y) buffer.set(x, y, background, color, symbol) nextColor() end if selection.width > 1 and selection.height > 1 then drawCornerPoint(xStart, yStart, "┏") drawSelectionSquare(xStart + 1, yStart, xEnd - 1, yStart, 1, 1, "━") drawCornerPoint(xEnd, yStart, "┓") drawSelectionSquare(xEnd, yStart + 1, xEnd, yEnd - 1, 1, 1, "┃") drawCornerPoint(xEnd, yEnd, "┛") drawSelectionSquare(xEnd - 1, yEnd, xStart + 1, yEnd, -1, 1, "━") drawCornerPoint(xStart, yEnd, "┗") drawSelectionSquare(xStart, yEnd - 1, xStart, yStart + 1, 1, -1, "┃") else if selection.width == 1 then drawSelectionSquare(xStart, yStart, xStart, yEnd, 1, 1, "┃") elseif selection.height == 1 then drawSelectionSquare(xStart, yStart, xEnd, yStart, 1, 1, "━") end end drawTooltip(xEnd + 2, yEnd - 1, localization.w .. ": " .. selection.width .. " px", localization.h .. ": " .. selection.height .. " px") -- drawTooltip(xEnd + 2, yEnd - 1, "Ш: " .. selection.width .. " px", "В: " .. selection.height .. " px", "S: " .. xStart .. "x" .. yStart, "E: " .. xEnd .. "x" .. yEnd) end local function line(x0, y0, x1, y1, background, applyToMasterPixels) local steep = false; if math.abs(x0 - x1) < math.abs(y0 - y1 ) then x0, y0 = swap(x0, y0) x1, y1 = swap(x1, y1) steep = true; end if (x0 > x1) then x0, x1 = swap(x0, x1) y0, y1 = swap(y0, y1) end local dx = x1 - x0; local dy = y1 - y0; local derror2 = math.abs(dy) * 2 local error2 = 0; local y = y0; for x = x0, x1, 1 do if steep then if applyToMasterPixels then image.set(masterPixels, y, x, background, 0x000000, 0x00, " ") else buffer.set(y, x, background, 0x000000, " ") end else if applyToMasterPixels then image.set(masterPixels, x, y, background, 0x000000, 0x00, " ") else buffer.set(x, y, background, 0x000000, " ") end end error2 = error2 + derror2; if error2 > dx then y = y + (y1 > y0 and 1 or -1); error2 = error2 - dx * 2; end end end local function drawShapeCornerPoints(xStart, yStart, xEnd, yEnd) buffer.set(xStart, yStart, 0x99FF80, 0x000000, " ") buffer.set(xEnd, yEnd, 0x99FF80, 0x000000, " ") end local function drawLineShape() local xStart, yStart = sizes.xStartOfImage + selection.xStart - 1, sizes.yStartOfImage + selection.yStart - 1 local xEnd, yEnd = sizes.xStartOfImage + selection.xEnd - 1, sizes.yStartOfImage + selection.yEnd - 1 line(xStart, yStart, xEnd, yEnd, currentBackground) drawShapeCornerPoints(xStart, yStart, xEnd, yEnd) drawTooltip(xEnd + 2, yEnd - 3, localization.w .. ": " .. selection.width .. " px", localization.h .. ": " .. selection.height .. " px", " ", localization.accept) end --Функция для обводки выделенной зоны local function stroke(x, y, width, height, color, applyToMasterPixels) if applyToMasterPixels then local iterator for i = x, x + width - 1 do iterator = image.getImageIndexByCoordinates(i, y, image.getWidth(masterPixels)) masterPixels[iterator] = color; masterPixels[iterator + 1] = 0x0; masterPixels[iterator + 2] = 0x0; masterPixels[iterator + 3] = " " iterator = image.getImageIndexByCoordinates(i, y + height - 1, image.getWidth(masterPixels)) masterPixels[iterator] = color; masterPixels[iterator + 1] = 0x0; masterPixels[iterator + 2] = 0x0; masterPixels[iterator + 3] = " " end for i = y, y + height - 1 do iterator = image.getImageIndexByCoordinates(x, i, image.getWidth(masterPixels)) masterPixels[iterator] = color; masterPixels[iterator + 1] = 0x0; masterPixels[iterator + 2] = 0x0; masterPixels[iterator + 3] = " " iterator = image.getImageIndexByCoordinates(x + width - 1, i, image.getWidth(masterPixels)) masterPixels[iterator] = color; masterPixels[iterator + 1] = 0x0; masterPixels[iterator + 2] = 0x0; masterPixels[iterator + 3] = " " end else buffer.square(x, y, width, 1, color, 0x000000, " ") buffer.square(x, y + height - 1, width, 1, color, 0x000000, " ") buffer.square(x, y, 1, height, color, 0x000000, " ") buffer.square(x + width - 1, y, 1, height, color, 0x000000, " ") end end local function ellipse(xStart, yStart, width, height, color, applyToMasterPixels) --helper function, draws pixel and mirrors it local function setpixel4(centerX, centerY, deltaX, deltaY, color) if applyToMasterPixels then image.set(masterPixels, centerX + deltaX, centerY + deltaY, color, 0x000000, 0x00, " ") image.set(masterPixels, centerX - deltaX, centerY + deltaY, color, 0x000000, 0x00, " ") image.set(masterPixels, centerX + deltaX, centerY - deltaY, color, 0x000000, 0x00, " ") image.set(masterPixels, centerX - deltaX, centerY - deltaY, color, 0x000000, 0x00, " ") else buffer.set(centerX + deltaX, centerY + deltaY, color, 0x000000, " ") buffer.set(centerX - deltaX, centerY + deltaY, color, 0x000000, " ") buffer.set(centerX + deltaX, centerY - deltaY, color, 0x000000, " ") buffer.set(centerX - deltaX, centerY - deltaY, color, 0x000000, " ") end end --red ellipse, 2*10px border local centerX = math.floor(xStart + width / 2) local centerY = math.floor(yStart + height / 2) local radiusX = math.floor(width / 2) local radiusY = math.floor(height / 2) local radiusX2 = radiusX ^ 2 local radiusY2 = radiusY ^ 2 --upper and lower halves local quarter = math.floor(radiusX2 / math.sqrt(radiusX2 + radiusY2)) for x = 0, quarter do local y = radiusY * math.sqrt(1 - x^2 / radiusX2) setpixel4(centerX, centerY, x, math.floor(y), color); end --right and left halves quarter = math.floor(radiusY2 / math.sqrt(radiusX2 + radiusY2)); for y = 0, quarter do x = radiusX * math.sqrt(1 - y^2 / radiusY2); setpixel4(centerX, centerY, math.floor(x), y, color); end end local function polygon(xCenter, yCenter, xStart, yStart, countOfEdges, color) local degreeStep = 360 / countOfEdges local deltaX, deltaY = xStart - xCenter, yStart - yCenter local radius = math.sqrt(deltaX ^ 2 + deltaY ^ 2) local halfRadius = radius / 2 local startDegree = math.deg(math.asin(deltaX / radius)) local function calculatePosition(degree) local radDegree = math.rad(degree) local deltaX2 = math.sin(radDegree) * radius local deltaY2 = math.cos(radDegree) * radius return round(xCenter + deltaX2), round(yCenter + (deltaY >= 0 and deltaY2 or -deltaY2)) end local xOld, yOld, xNew, yNew = calculatePosition(startDegree) for degree = (startDegree + degreeStep - 1), (startDegree + 360), degreeStep do xNew, yNew = calculatePosition(degree) buffer.line(xOld, yOld, xNew, yNew, color, 0x000000, " ") xOld, yOld = xNew, yNew end end local function drawPolygonShape() local xStart, yStart = sizes.xStartOfImage + selection.xStart - 1, sizes.yStartOfImage + selection.yStart - 1 local xEnd, yEnd = sizes.xStartOfImage + selection.xEnd - 1, sizes.yStartOfImage + selection.yEnd - 1 local radius = math.floor(math.sqrt(selection.width ^ 2 + (selection.height*2) ^ 2)) polygon(xStart, yStart, xEnd, yEnd, currentPolygonCountOfEdges, currentBackground) drawShapeCornerPoints(xStart, yStart, xEnd, yEnd) drawTooltip(xEnd + 2, yEnd - 3, localization.radius .. ": " .. radius .. " px", localization.edges .. ": " .. currentPolygonCountOfEdges, " ", localization.accept) end local function drawSquareShape(type) local xStart, yStart = sizes.xStartOfImage + selection.x - 1, sizes.yStartOfImage + selection.y - 1 local xEnd, yEnd = xStart + selection.width - 1, yStart + selection.height - 1 if type == "filledSquare" then buffer.square(xStart, yStart, selection.width, selection.height, currentBackground, 0x000000, " ") elseif type == "frame" then stroke(xStart, yStart, selection.width, selection.height, currentBackground, false) elseif type == "ellipse" then ellipse(xStart, yStart, selection.width, selection.height, currentBackground, false) end drawShapeCornerPoints(xStart, yStart, xEnd, yEnd) drawTooltip(xEnd + 2, yEnd - 3, localization.w .. ": " .. selection.width .. " px", localization.h .. ": " .. selection.height .. " px", " ", localization.accept) end local function drawMultiPointInstrument() if selection and selection.finished == true then if instruments[currentInstrument] == "M" then drawSelection() elseif instruments[currentInstrument] == "S" then if currentShape == localization.line then drawLineShape() elseif currentShape == localization.ellipse then drawSquareShape("ellipse") elseif currentShape == localization.rectangle then drawSquareShape("filledSquare") elseif currentShape == localization.border then drawSquareShape("frame") elseif currentShape == localization.polygon then drawPolygonShape() end end end end --Отрисовка изображения local function drawImage() --Стартовые нужности local xPixel, yPixel = 1, 1 local xPos, yPos = sizes.xStartOfImage, sizes.yStartOfImage --Устанавливаем ограничение прорисовки, чтобы картинка не съебывала за дозволенную зону buffer.setDrawLimit(sizes.xStartOfDrawingArea, sizes.yStartOfDrawingArea, sizes.widthOfDrawingArea, sizes.heightOfDrawingArea) --Рисуем прозрачную зону drawTransparentZone(xPos, yPos) --Перебираем массив мастерпиксельса for i = 3, #masterPixels, 4 do --Рисуем пиксель, если у него прозрачность не абсолютная, ЛИБО имеется какой-то символ --Т.е. даже если прозрачность и охуела, но символ есть, то рисуем его if masterPixels[i + 2] ~= 0xFF or masterPixels[i + 3] ~= " " then drawPixel(xPos, yPos, xPixel, yPixel, i) end --Всякие расчеты координат xPixel = xPixel + 1 xPos = xPos + 1 if xPixel > image.getWidth(masterPixels) then xPixel = 1; xPos = sizes.xStartOfImage; yPixel = yPixel + 1; yPos = yPos + 1 end end if image.getWidth(masterPixels) > 0 and image.getHeight(masterPixels) > 0 then local text = localization.size .. ": " .. image.getWidth(masterPixels) .. "x" .. image.getHeight(masterPixels) .. " px" xPos = math.floor(sizes.xStartOfImage + image.getWidth(masterPixels) / 2 - unicode.len(text) / 2) buffer.text(xPos, sizes.yEndOfImage + 1, 0xFFFFFF, text) end --Рисуем мультиинструмент drawMultiPointInstrument() --Убираем ограничение отрисовки buffer.resetDrawLimit() end --Просто для удобства local function drawBackgroundAndImage() drawBackground() drawImage() end --Функция, рисующая ВСЕ, абсолютли, епта local function drawAll() drawBackground() drawLeftBar() drawTopMenu() drawBackgroundAndImage() buffer.draw() end ------------------------------------------------ Вспомогательные функции для работы с изображением и прочим -------------------------------------------------------------- --Смена инструмента на указанный номер local function changeInstrumentTo(ID) currentInstrument = ID selection = nil drawAll() end --Перемещалка картинки в указанном направлении, поддерживающая все инструменты local function move(direction) if instruments[currentInstrument] == "M" and selection and selection.finished == true then if direction == "up" then selection.y = selection.y - 1 if selection.y < 1 then selection.y = 1 end elseif direction == "down" then selection.y = selection.y + 1 if selection.y + selection.height - 1 > image.getHeight(masterPixels) then selection.y = selection.y - 1 end elseif direction == "left" then selection.x = selection.x - 1 if selection.x < 1 then selection.x = 1 end elseif direction == "right" then selection.x = selection.x + 1 if selection.x + selection.width - 1 > image.getWidth(masterPixels) then selection.x = selection.x - 1 end end else local howMuchUpDown = 2 local howMuchLeftRight = 4 if direction == "up" then reCalculateImageSizes(sizes.xStartOfImage, sizes.yStartOfImage - howMuchUpDown) elseif direction == "down" then reCalculateImageSizes(sizes.xStartOfImage, sizes.yStartOfImage + howMuchUpDown) elseif direction == "left" then reCalculateImageSizes(sizes.xStartOfImage - howMuchLeftRight, sizes.yStartOfImage) elseif direction == "right" then reCalculateImageSizes(sizes.xStartOfImage + howMuchLeftRight, sizes.yStartOfImage) end end drawBackgroundAndImage() buffer.draw() end --Просто более удобная установка пикселя, а то все эти плюсы, минусы, бррр 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() currentBackground, currentForeground = swap(currentBackground, currentForeground) drawColors() console("Цвета поменяны местами") end --Ух, сука! Функция для работы инструмента текста --Лютая дичь, спиздил со старого фш, но, вроде, пашет нормас --Правда, чет есть предчувствие, что костыльная и багованная она, ну да похуй local function inputText(x, y, limit) local oldPixels = ecs.rememberOldPixels(x,y-1,x+limit-1,y+1) local text = "" local inputPos = 1 local function drawThisShit() for i = 1, inputPos do ecs.invertedText(x + i - 1, y + 1, "─") 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), currentBackground) end drawThisShit() while true do local e = {event.pull()} if e[1] == "key_down" then if e[4] == 14 then if unicode.len(text) >= 1 then text = unicode.sub(text, 1, -2) if unicode.len(text) < (limit - 1) then inputPos = inputPos - 1 end ecs.drawOldPixels(oldPixels) drawThisShit() end elseif e[4] == 28 then break elseif e[4] == 200 then text = text .. "▀" if unicode.len(text) < limit then inputPos = inputPos + 1 end drawThisShit() elseif e[4] == 208 then text = text .. "▄" if unicode.len(text) < limit then inputPos = inputPos + 1 end drawThisShit() else local symbol = ecs.convertCodeToSymbol(e[3]) if symbol ~= nil then text = text .. symbol if unicode.len(text) < limit then inputPos = inputPos + 1 end drawThisShit() end end elseif e[1] == "clipboard" then if e[3] then text = text .. e[3] if unicode.len(text) < limit then inputPos = inputPos + unicode.len(e[3]) end drawThisShit() end end end ecs.drawOldPixels(oldPixels) if text == "" then text = " " end return text end --Функция-применятор текста к массиву изображения local function saveTextToPixels(x, y, text) local sText = unicode.len(text) local iterator x = x - 1 for i = 1, sText do if x + i > image.getWidth(masterPixels) then break end iterator = image.getImageIndexByCoordinates(x + i, y, image.getWidth(masterPixels)) setPixel(iterator, masterPixels[iterator], currentBackground, masterPixels[iterator + 2], unicode.sub(text, i, i)) end end --Функция-центратор картинки по центру моника local function tryToFitImageOnCenterOfScreen() reCalculateImageSizes() local x, y = sizes.xStartOfImage, sizes.yStartOfImage if image.getWidth(masterPixels) < sizes.widthOfDrawingArea then x = math.floor(sizes.xStartOfDrawingArea + sizes.widthOfDrawingArea / 2 - image.getWidth(masterPixels) / 2) - 1 end if image.getHeight(masterPixels) < sizes.heightOfDrawingArea then y = math.floor(sizes.yStartOfDrawingArea + sizes.heightOfDrawingArea / 2 - image.getHeight(masterPixels) / 2) end reCalculateImageSizes(x, y) end --Функция, спрашивающая юзверя, какого размера пикчу он хочет создать - ну, и создает ее local function new() selection = nil local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, localization.newDocument}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, localization.width}, {"Input", 0x262626, 0x880000, localization.height}, {"EmptyLine"}, {"Button", {0x999999, 0xffffff, "OK"}}) data[1] = tonumber(data[1]) or 51 data[2] = tonumber(data[2]) or 19 masterPixels = {} masterPixels[1], masterPixels[2] = data[1], data[2] createEmptyMasterPixels() tryToFitImageOnCenterOfScreen() drawAll() end --Обычная рекурсивная заливка, алгоритм спизжен с вики --Есть инфа, что выжирает стек, но Луа, вроде, не особо ругается, так что заебок все local function fill(x, y, startColor, fillColor) local function doFill(xStart, yStart) local iterator = image.getImageIndexByCoordinates(xStart, yStart, image.getWidth(masterPixels)) --Завершаем функцию, если цвет в массиве не такой, какой мы заливаем 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) x, y = x - position, y - position --Хуевинка для рисования local newIterator --Перебираем кисть по ширине и высоте for cyka = 1, currentBrushSize do for pidor = 1, currentBrushSize do --Если этот кусочек входит в границы рисовабельной зоны, то if x >= 1 and x <= image.getWidth(masterPixels) and y >= 1 and y <= image.getHeight(masterPixels) then --Считаем итератор для кусочка кисти newIterator = image.getImageIndexByCoordinates(x, y, image.getWidth(masterPixels)) --Если прозрачности кисти ВАЩЕ НЕТ, то просто рисуем как обычненько все if alpha == 0x00 then setPixel(newIterator, background, foreground, alpha, symbol) --Если прозрачности кисти есть какая-то, но она не абсолютная elseif alpha < 0xFF and alpha > 0x00 then --Если пиксель в массиве ни хуя не прозрачный, то оставляем его таким же, разве что цвет меняем на сблендированный if masterPixels[newIterator + 2] == 0x00 then local gettedBackground = color.blend(masterPixels[newIterator], background, alpha / 0xFF) setPixel(newIterator, gettedBackground, foreground, 0x00, symbol) --А если прозрачный, то смешиваем прозрачности --Пиздануться вообще, сук else --Если его прозрачность максимальная 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 - currentBrushSize y = y + 1 end end --Функция-обрезчик картинки local function crop() if selection then masterPixels = image.crop(masterPixels, selection.x, selection.y, selection.width, selection.height) selection = nil tryToFitImageOnCenterOfScreen() drawAll() end end --Функция-расширитель картинки local function expand() local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, localization.expand}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, localization.countOfPixels}, {"Selector", 0x262626, 0x880000, localization.fromBottom, localization.fromTop, localization.fromLeft, localization.fromRight}, {"EmptyLine"}, {"Button", {0xaaaaaa, 0xffffff, "OK"}, {0x888888, 0xffffff, localization.cancel}} ) if data[3] == "OK" then local countOfPixels = tonumber(data[1]) if countOfPixels then masterPixels = image.expand(masterPixels, data[2] == localization.fromTop and countOfPixels or 0, data[2] == localization.fromBottom and countOfPixels or 0, data[2] == localization.fromLeft and countOfPixels or 0, data[2] == localization.fromRight and countOfPixels or 0, 0x010101, 0x010101, 0xFF, " " ) reCalculateImageSizes(sizes.xStartOfImage, sizes.yStartOfImage) drawAll() end end end --Функция-загрузчик картинки из файла local function loadImageFromFile(path) if fs.exists(path) then selection = nil masterPixels = image.load(path) savePath = path tryToFitImageOnCenterOfScreen() else ecs.error("Файл \"" .. path .. "\" не существует") end end --Функция-заполнитель выделенной зоны какими-либо данными local function fillSelection(background, foreground, alpha, symbol) for j = selection.y, selection.y + selection.height - 1 do for i = selection.x, selection.x + selection.width - 1 do local iterator = image.getImageIndexByCoordinates(i, j, image.getWidth(masterPixels)) masterPixels[iterator] = background masterPixels[iterator + 1] = foreground masterPixels[iterator + 2] = alpha masterPixels[iterator + 3] = symbol end end drawAll() end local function applyShapeToMasterPixels() if currentShape == localization.line then line(selection.xStart, selection.yStart, selection.xEnd, selection.yEnd, currentBackground, true) elseif currentShape == localization.rectangle then fillSelection(currentBackground, 0x00000, 0x00, " ") elseif currentShape == localization.border then stroke(selection.x, selection.y, selection.width, selection.height, currentBackground, true) elseif currentShape == localization.ellipse then ellipse(selection.x, selection.y, selection.width, selection.height, currentBackground, true) end selection = nil drawBackgroundAndImage() buffer.draw() end ------------------------------------------------ Старт программы -------------------------------------------------------------- --Рисуем весь интерфейс чисто для красоты drawAll() --Открываем файлы по аргументам программы if args[1] == "o" or args[1] == "open" or args[1] == "-o" or args[1] == "load" then loadImageFromFile(args[2]) else new() end --Отрисовываем интерфейс снова, поскольку у нас либо создался новый документ, либо открылся имеющийся файл drawAll() --Анализируем ивенты while true do local e = {event.pull()} if e[1] == "touch" or e[1] == "drag" or e[1] == "drop" then --Левый клик if e[5] == 0 then --Если кликнули на рисовабельную зонку if ecs.clickedAtArea(e[3], e[4], sizes.xStartOfImage, sizes.yStartOfImage, sizes.xEndOfImage, sizes.yEndOfImage) then --Получаем координаты в изображении и итератор local x, y = e[3] - sizes.xStartOfImage + 1, e[4] - sizes.yStartOfImage + 1 local iterator = image.getImageIndexByCoordinates(x, y, image.getWidth(masterPixels)) --Все для инструментов мультиточечного рисования if instruments[currentInstrument] == "M" or instruments[currentInstrument] == "S" then if e[1] == "touch" then selection = {} selection.xStart, selection.yStart = x, y elseif e[1] == "drag" and selection then local x1, y1 = selection.xStart, selection.yStart local x2, y2 = x, y if x1 > x2 then x1, x2 = swap(x1, x2) end if y1 > y2 then y1, y2 = swap(y1, y2) end selection.x, selection.y = x1, y1 selection.x2, selection.y2 = x2, y2 selection.xEnd, selection.yEnd = x, y selection.width = selection.x2 - selection.x + 1 selection.height = selection.y2 - selection.y + 1 selection.finished = true end --Если выделение полностью завершено, то отдаем контроль отрисовочным функциям -- if instruments[currentInstrument] == "M" then drawBackgroundAndImage() buffer.draw() -- end --Кисть elseif instruments[currentInstrument] == "B" then --Если нажата клавиша альт if keyboard.isKeyDown(56) then local _, gettedForeground, gettedBackground = component.gpu.get(e[3], e[4]) currentBackground = gettedBackground currentForeground = gettedForeground drawColors() buffer.draw() --Если обычная кисть, просто кисть, вообще всем кистям кисть else brush(x, y, currentBackground, currentForeground, currentAlpha, currentSymbol) --Пишем что-то в консоли console("Кисть: клик на точку "..e[3].."x"..e[4]..", координаты в изображении: "..x.."x"..y..", индекс массива изображения: "..iterator) buffer.draw() end --Ластик elseif instruments[currentInstrument] == "E" then brush(x, y, currentBackground, currentForeground, 0xFF, currentSymbol) console("Ластик: клик на точку "..e[3].."x"..e[4]..", координаты в изображении: "..x.."x"..y..", индекс массива изображения: "..iterator) buffer.draw() --Текст elseif instruments[currentInstrument] == "T" then local limit = image.getWidth(masterPixels) - x + 1 local text = inputText(e[3], e[4], limit) saveTextToPixels(x, y, text) drawImage() buffer.draw() --Заливка elseif instruments[currentInstrument] == "F" then fill(x, y, masterPixels[iterator], currentBackground) drawImage() buffer.draw() end iterator, x, y = nil, nil, nil end --Цвета 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.show("auto", "auto", currentBackground) or currentBackground drawAll() elseif key == 2 or key == 3 then currentForeground = palette.show("auto", "auto", currentForeground) or currentForeground drawAll() elseif key == 4 then buffer.text(obj["Colors"][key][1], obj["Colors"][key][2], 0xFF0000, "←→") os.sleep(0.2) swapColors() buffer.draw() 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 selection = nil currentInstrument = key drawLeftBar(); buffer.draw() if instruments[currentInstrument] == "S" then local action = GUI.contextMenu(obj["Instruments"][key][3] + 1, obj["Instruments"][key][2], {localization.line}, {localization.ellipse}, {localization.rectangle}, {localization.polygon}, {localization.border}):show() currentShape = action or localization.line if currentShape == localization.polygon then local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, localization.edges}, {"EmptyLine"}, {"Selector", 0x262626, 0x880000, "3", "4", "5", "6", "7", "8", "9", "10"}, {"EmptyLine"}, {"Button", {0xaaaaaa, 0xffffff, "OK"}} ) currentPolygonCountOfEdges = tonumber(data[1]) end drawAll() end break end end --Верхний меню-бар local object = obj.menu:getClickedObject(e[3], e[4]) if object then object:press() buffer.draw() local action if object.text == localization.file then action = GUI.contextMenu(object.x, object.y + 1, {localization.new, false, "^N"}, {localization.open, false, "^O"}, {localization.createFromString}, "-", {localization.save, (savePath == nil), "^S"}, {localization.saveAs}, "-", {localization.exit}):show() elseif object.text == localization.image then action = GUI.contextMenu(object.x, object.y + 1, -- {localization.crop}, {localization.expand}, -- "-", -- {localization.rotateBy90}, -- {localization.rotateBy180}, "-", {localization.flipHorizontal}, {localization.flipVertical} ):show() elseif object.text == localization.view then action = GUI.contextMenu(object.x, object.y + 1, {localization.transparencyPad}):show() elseif object.text == localization.about then ecs.universalWindow("auto", "auto", 36, 0xeeeeee, true, {"EmptyLine"}, {"CenterText", 0x880000, photoshopVersion}, {"EmptyLine"}, {"CenterText", 0x262626, localization.developers}, {"CenterText", 0x555555, "Тимофеев Игорь"}, {"CenterText", 0x656565, "vk.com/id7799889"}, {"CenterText", 0x656565, "Трифонов Глеб"}, {"CenterText", 0x656565, "vk.com/id88323331"}, {"EmptyLine"}, {"CenterText", 0x262626, localization.testers}, {"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"}}) elseif object.text == localization.hotkeys then ecs.universalWindow( "auto", "auto", 42, 0xeeeeee, true, table.unpack(localization.hotkeysLines) ) end if action == localization.exit then ecs.prepareToExit() return elseif action == localization.hueSaturation then local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, localization.hueSaturation}, {"EmptyLine"}, {"Slider", 0x262626, 0x880000, 0, 100, 50, localization.hue .. ": ", ""}, {"Slider", 0x262626, ecs.colors.red, 0, 100, 50, localization.saturation .. ": ", ""}, {"Slider", 0x262626, 0x000000, 0, 100, 50, localization.brightness .. ": ", ""}, {"EmptyLine"}, {"Button", {0xaaaaaa, 0xffffff, "OK"}, {0x888888, 0xffffff, localization.cancel}} ) if data[4] == "OK" then masterPixels = image.hueSaturationBrightness(masterPixels, data[1] - 50, data[2] - 50, data[3] - 50) drawAll() end elseif action == localization.gaussianBlur then local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, localization.gaussianBlur}, {"EmptyLine"}, {"Slider", 0x262626, 0x880000, 1, 5, 2, localization.radius .. ": ", ""}, {"Slider", 0x262626, 0x880000, 1, 255, 0x88, localization.force .. ": ", ""}, {"EmptyLine"}, {"Button", {0xaaaaaa, 0xffffff, "OK"}, {0x888888, 0xffffff, localization.cancel}} ) if data[3] == "OK" then masterPixels = image.gaussianBlur(masterPixels, tonumber(data[1]), tonumber(data[2])) drawAll() end elseif action == localization.colorBalance then local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, localization.colorBalance}, {"EmptyLine"}, {"Slider", 0x262626, 0x880000, 0, 100, 50, "R: ", ""}, {"Slider", 0x262626, ecs.colors.green, 0, 100, 50, "G: ", ""}, {"Slider", 0x262626, ecs.colors.blue, 0, 100, 50, "B: ", ""}, {"EmptyLine"}, {"Button", {0xaaaaaa, 0xffffff, "OK"}, {0x888888, 0xffffff, localization.cancel}} ) if data[4] == "OK" then masterPixels = image.colorBalance(masterPixels, data[1] - 50, data[2] - 50, data[3] - 50) drawAll() end elseif action == localization.photoFilter then local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, localization.photoFilter}, {"EmptyLine"}, {"Color", localization.filterColor, 0x333333}, {"Slider", 0x262626, 0x880000, 0, 255, 100, localization.transparency .. ": ", ""}, {"EmptyLine"}, {"Button", {0xaaaaaa, 0xffffff, "OK"}, {0x888888, 0xffffff, localization.cancel}} ) if data[3] == "OK" then masterPixels = image.photoFilter(masterPixels, data[1], data[2]) drawAll() end elseif action == localization.crop then crop() elseif action == localization.expand then expand() elseif action == localization.flipVertical then masterPixels = image.flipVertically(masterPixels) drawAll() elseif action == localization.flipHorizontal then masterPixels = image.flipHorizontally(masterPixels) drawAll() elseif action == localization.invertColors then masterPixels = image.invert(masterPixels) drawAll() elseif action == localization.blackWhite then masterPixels = image.blackAndWhite(masterPixels) drawAll() elseif action == localization.rotateBy90 then masterPixels = image.rotate(masterPixels, 90) drawAll() elseif action == localization.rotateBy180 then masterPixels = image.rotate(masterPixels, 180) drawAll() elseif action == localization.new then new() drawAll() elseif action == localization.saveAs then local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, localization.saveAs}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, localization.path}, {"Selector", 0x262626, 0x880000, "OCIF6", "OCIF1", "StringImage"}, {"EmptyLine"}, {"Button", {0xaaaaaa, 0xffffff, "OK"}, {0x888888, 0xffffff, localization.cancel}} ) if data[3] == "OK" then data[1] = data[1] or "Untitled.pic" local path = string.gsub(data[1], "%.pic$", "") .. ".pic" if data[2] == "StringImage" then local file = io.open(path, "w") file:write(image.toString(masterPixels)) file:close() else savePath = path image.save(path, masterPixels, data[2] == "OCIF6" and 6 or 1) end end elseif action == localization.save then image.save(savePath, masterPixels) elseif action == localization.open then local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, localization.open}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, localization.path}, {"EmptyLine"}, {"Button", {0xaaaaaa, 0xffffff, "OK"}, {0x888888, 0xffffff, localization.cancel}}) if data[2] == "OK" then 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" and fileFormat ~= ".png" then ecs.error("Формат файла \""..fileFormat.."\" не поддерживается!") else loadImageFromFile(data[1]) drawAll() end end elseif action == localization.createFromString then local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, localization.createFromString}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, localization.string}, {"EmptyLine"}, {"Button", {0xaaaaaa, 0xffffff, "OK"}, {0x888888, 0xffffff, localization.cancel}} ) if data[2] == "OK" then local success, picture = pcall(image.fromString, data[1]) if success then masterPixels, selection, savePath = picture, nil, nil tryToFitImageOnCenterOfScreen() drawAll() else error("Failed to create image from string") end end elseif action == localization.transparencyPad then local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, localization.transparencyPad}, {"EmptyLine"}, {"Color", localization.transparencyColor .. " 1", colors.transparencyWhite}, {"Color", localization.transparencyColor .. " 2", colors.transparencyGray}, {"EmptyLine"}, {"Switch", 0xF2B233, 0xffffff, 0x262626, localization.transparencyGrid, showTransparencyGrid}, {"EmptyLine"}, {"Button", {0xaaaaaa, 0xffffff, "OK"}, {0x888888, 0xffffff, localization.cancel}} ) if data[4] == "OK" then colors.transparencyWhite, colors.transparencyGray, showTransparencyGrid = data[1], data[2], data[3] drawAll() end end drawTopMenu() buffer.draw() end else --Если кликнули на рисовабельную зонку if ecs.clickedAtArea(e[3], e[4], sizes.xStartOfImage, sizes.yStartOfImage, sizes.xEndOfImage, sizes.yEndOfImage) then if instruments[currentInstrument] == "M" and selection then local action = GUI.contextMenu(e[3], e[4], {localization.deselect}, {localization.crop}, "-", {localization.fill}, {localization.border}, "-", {localization.clear} ):show() if action == localization.deselect then selection = nil drawAll() elseif action == localization.crop then crop() elseif action == localization.clear then fillSelection(0x0, 0x0, 0xFF, " ") elseif action == localization.fill then fillSelection(currentBackground, 0x0, 0x0, " ") elseif action == localization.border then stroke(selection.x, selection.y, selection.width, selection.height, currentBackground, true) drawAll() end else local x, y, width, height = e[3], e[4], 30, 12 --А это чтоб за края экрана не лезло if y + height >= buffer.screen.height then y = buffer.screen.height - height end if x + width + 1 >= buffer.screen.width then x = buffer.screen.width - width - 1 end currentBrushSize, currentAlpha = table.unpack(ecs.universalWindow(x, y, width, 0xeeeeee, true, {"EmptyLine"}, {"CenterText", 0x880000, localization.brushParameters}, {"Slider", 0x262626, 0x880000, 1, 10, currentBrushSize, localization.size .. ": ", " px"}, {"Slider", 0x262626, 0x880000, 0, 255, currentAlpha, localization.transparency .. ": ", ""}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}})) buffer.draw() end end end 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 e[4] == 28 then if instruments[currentInstrument] == "S" and selection and selection.finished then applyShapeToMasterPixels() end --BACKSPACE elseif e[4] == 14 then if selection and selection.finished then fillSelection(0x000000, 0x000000, 0x00, " ") drawAll() end --X elseif e[4] == 45 then swapColors() buffer.draw() --B elseif e[4] == 48 then changeInstrumentTo(2) --E elseif e[4] == 18 then changeInstrumentTo(3) --G elseif e[4] == 34 then changeInstrumentTo(4) --T elseif e[4] == 20 then changeInstrumentTo(5) --M elseif e[4] == 50 then changeInstrumentTo(1) --D elseif e[4] == 32 then if keyboard.isControlDown() then selection = nil drawAll() else currentBackground = 0x000000 currentForeground = 0xFFFFFF currentAlpha = 0x00 drawColors() buffer.draw() end end elseif e[1] == "scroll" then if e[5] == 1 then move("up") else move("down") end end end ------------------------------------------------ Выход из программы --------------------------------------------------------------