Big update for Image API and Picture Edit

This commit is contained in:
Bs0Dd 2021-07-16 15:18:19 +06:00
parent 274cb57ea7
commit 71dfb3c857
4 changed files with 378 additions and 46 deletions

View File

@ -6,6 +6,7 @@
path = "Path: ",
save = "Save",
saveAs = "Save as",
ok = "Ok",
cancel = "Cancel",
fileName = "File name",
newPicture = "New picture",
@ -35,7 +36,7 @@
"F - braille tool",
" ",
"X - switch colors",
"D - make colors B/W",
"D - reset drawing colors",
},
tool1 = "Selection tool allows you to select preferred area on image and to perform some operations on it. Green dots mean start and end points (for example, it needs to line rasterization)",
fill = "Fill",
@ -65,5 +66,24 @@
eraseSym = "Erase symbol:",
tool7 = "Text tool allows you to type some text data with selected primary color right on your image! It's time to say \"ur mom gay\" to everyone <3",
tool8 = "Fill tool allows you to automatically fill areas with selected primary color just like in Paint. Oh God, where is my RAM...?",
tool9 = "Braille tool allows you to draw pixels with Braille symbols on your image. Select preferred mini-pixels via menu above, configure transparency affecting and \"Let's go fellas!\""
}
tool9 = "Braille tool allows you to draw pixels with Braille symbols on your image. Select preferred mini-pixels via menu above, configure transparency affecting and \"Let's go fellas!\"",
image = "Image",
edit = "Edit",
rotate90 = "Rotate by 90 degrees",
rotate180 = "Rotate by 180 degrees",
rotate270 = "Rotate by 270 degrees",
flipVertical = "Flip vertical",
flipHorizontal = "Flip horizontal",
hueSaturation = "Hue/Saturation",
colorBalance = "Color balance",
photoFilter = "Photo filter",
invertColors = "Invert colors",
blackWhite = "Black and white",
gaussianBlur = "Gaussian blur",
hue = "Hue: ",
saturation = "Saturation: ",
brightness = "Brightness: ",
filterColor = "Filter color",
transparency = "Transparency: ",
force = "Force"
}

View File

@ -6,6 +6,7 @@
path = "Путь: ",
save = "Сохранить",
saveAs = "Сохранить как",
ok = "Ok",
cancel = "Отмена",
fileName = "Имя файла",
newPicture = "Новое изображение",
@ -35,7 +36,7 @@
"F - символы брайля",
" ",
"X - поменять цвета",
"D - убрать цвет в изображении",
"D - сбросить цвета рисованияы",
},
tool1 = "Инструмент выделения способен выделять отдельные участки изображения и делать с ними определенные действия. Зелеными точками обозначается начало и конец выделения (полезно для растеризации линии)",
fill = "Залить",
@ -50,7 +51,7 @@
tool4 = "Инструмент пипетка позволяет получать первичный и вторичный цвет выбранного пикселя. Вы можете выбрать, какие цвета захватывать.",
pickBack = "Захватывать фон:",
pickFor = "Захватывать перед:",
tool5 = "Классический инструмент кисть прозволяет рисовать на изображении с указанным радиусом и прозрачностью. Вы можете выбрать, что будет рисоваться. Также можно выбрать символ, которым будет рисовать кисть, иначе она будет рисовать пустым символом.",
tool5 = "Классический инструмент кисть позволяет рисовать на изображении с указанным радиусом и прозрачностью. Вы можете выбрать, что будет рисоваться. Также можно выбрать символ, которым будет рисовать кисть, иначе она будет рисовать пустым символом.",
drawBack = "Рисовать на фоне:",
drawFor = "Рисовать на пер.:",
drawAlpha = "Изменять прозр.:",
@ -65,5 +66,24 @@
eraseSym = "Стирать символ:",
tool7 = "Инструмент текста позволяет писать текст с указанным первичным и вторичным цветом прямо на изображении! Время сказать всем \"твоя мама гей\" <3",
tool8 = "Инструмент заливка позволяет автоматически заполнять зоны с указанным первичным и вторичным цветом, как это делает Paint. О боже, а где вся оперативка...?",
tool9 = "Утилита Брайля позволяет рисовать пиксели с помощью шрифта Брайля. Выберите мини-пиксели в свойствах, настройте прозрачность и поехали!"
}
tool9 = "Инструмент Брайля позволяет рисовать пиксели с помощью шрифта Брайля. Выберите мини-пиксели в свойствах, настройте прозрачность и поехали!",
image = "Изображение",
edit = "Редактировать",
rotate90 = "Повернуть на 90 градусов",
rotate180 = "Повернуть на 180 градусов",
rotate270 = "Повернуть на 270 градусов",
flipVertical = "Отразить по вертикали",
flipHorizontal = "Отразить по горизонтали",
hueSaturation = "Тон/Насыщенность",
colorBalance = "Цветовой баланс",
photoFilter = "Фотофильтр",
invertColors = "Инвертировать цвета",
blackWhite = "Черно-белый фильтр",
gaussianBlur = "Размытие по Гауссу",
hue = "Тон: ",
saturation = "Насыщенность: ",
brightness = "Яркость: ",
filterColor = "Цвет фильтра",
transparency = "Прозрачность: ",
force = "Сила: "
}

View File

@ -134,7 +134,6 @@ window.sidebarLayout.eventHandler = function(workspace, object, e1, e2, e3, e4,
end
window.sidebarLayout:setMargin(1, 1, h, v)
workspace:draw()
end
end
@ -146,7 +145,6 @@ for i = 1, #config.recentColors do
local button = recentColorsContainer:addChild(GUI.button(x, y, 2, 1, 0x0, 0x0, 0x0, 0x0, " "))
button.onTouch = function()
window.primaryColorSelector.color = config.recentColors[i]
workspace:draw()
end
x = x + 2
@ -199,7 +197,6 @@ local function onToolTouch(index)
aboutToolTextBox.height = #aboutToolTextBox.lines
end
workspace:draw()
end
local tools = filesystem.list(toolsPath)
@ -277,7 +274,6 @@ end
local function swapColors()
window.primaryColorSelector.color, window.secondaryColorSelector.color = window.secondaryColorSelector.color, window.primaryColorSelector.color
workspace:draw()
end
local function colorSelectorDraw(object)
@ -292,7 +288,7 @@ window.secondaryColorSelector = window:addChild(GUI.colorSelector(3, 1, 5, 2, 0x
window.primaryColorSelector = window:addChild(GUI.colorSelector(2, 1, 5, 2, 0x000000, " "))
window.secondaryColorSelector.draw, window.primaryColorSelector.draw = colorSelectorDraw, colorSelectorDraw
window.swapColorsButton = window:addChild(GUI.button(1, 1, window.toolsList.width, 1, nil, 0x696969, nil, 0xA5A5A5, ">"))
window.swapColorsButton = window:addChild(GUI.button(1, 1, window.toolsList.width, 1, nil, 0x696969, nil, 0xA5A5A5, " ←→"))
window.swapColorsButton.onTouch = swapColors
local function setSavePath(path)
@ -346,7 +342,6 @@ local function saveAs()
filesystemDialog.onSubmit = function(path)
save(path)
workspace:draw()
end
end
@ -365,6 +360,7 @@ end
local function new()
local container = GUI.addBackgroundContainer(workspace, true, true, locale.newPicture)
container.panel.eventHandler = nil
local layout = container.layout:addChild(GUI.layout(1, 1, 36, 3, 1, 1))
layout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL)
@ -378,23 +374,23 @@ local function new()
layout:addChild(GUI.text(1, 1, 0x696969, " x "))
local heightInput = addInput("", locale.height)
widthInput.width, heightInput.width = 16, 17
container.panel.eventHandler = function(workspace, panel, e1)
if e1 == "touch" then
if
widthInput.text:match("%d+") and
heightInput.text:match("%d+")
then
newNoGUI(tonumber(widthInput.text), tonumber(heightInput.text), nil)
window.image.reposition()
end
container:remove()
workspace:draw()
container.layout:addChild(GUI.button(1, 1, 30, 3, 0xFFFFFF, 0x555555, 0x880000, 0xFFFFFF, locale.ok)).onTouch = function()
if
widthInput.text:match("%d+") and
heightInput.text:match("%d+")
then
newNoGUI(tonumber(widthInput.text), tonumber(heightInput.text), nil)
window.image.reposition()
end
container:remove()
end
container.layout:addChild(GUI.button(1, 1, 30, 3, 0xFFFFFF, 0x555555, 0x880000, 0xFFFFFF, locale.cancel)).onTouch = function()
container:remove()
end
workspace:draw()
end
local function open()
@ -409,7 +405,6 @@ local function open()
loadImage(path)
window.image.reposition()
workspace:draw()
end
end
@ -484,9 +479,7 @@ fileItem:addItem(locale.new, false, "^N").onTouch = new
fileItem:addSeparator()
fileItem:addItem(locale.open, false, "^O").onTouch = function()
open()
end
fileItem:addItem(locale.open, false, "^O").onTouch = open
local fileItemSubMenu = fileItem:addSubMenuItem(locale.openRecent, #config.recentFiles == 0)
for i = 1, #config.recentFiles do
@ -494,17 +487,23 @@ for i = 1, #config.recentFiles do
loadImage(config.recentFiles[i])
window.image.reposition()
workspace:draw()
end
end
fileItem:addItem(locale.openFromURL).onTouch = function()
local container = GUI.addBackgroundContainer(workspace, true, true, locale.openFromURL)
container.panel.eventHandler = nil
local input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x969696, 0xE1E1E1, 0x2D2D2D, "", "http://example.com/test.pic"))
input.onInputFinished = function()
local okBut = container.layout:addChild(GUI.button(1, 1, 30, 3, 0xFFFFFF, 0x555555, 0x880000, 0xFFFFFF, locale.ok))
local cancelBut = container.layout:addChild(GUI.button(1, 1, 30, 3, 0xFFFFFF, 0x555555, 0x880000, 0xFFFFFF, locale.cancel))
okBut.onTouch = function()
if #input.text > 0 then
input:remove()
okBut:remove()
cancelBut:remove()
container.layout:addChild(GUI.label(1, 1, container.width, 1, 0x969696, locale.downloading):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP))
workspace:draw()
@ -523,11 +522,14 @@ fileItem:addItem(locale.openFromURL).onTouch = function()
GUI.alert(reason)
end
workspace:draw()
else
container:remove()
end
end
workspace:draw()
cancelBut.onTouch = function()
container:remove()
end
end
fileItem:addSeparator()
@ -541,28 +543,163 @@ fileItem:addItem(locale.saveAs, false, "^⇧S").onTouch = saveAs
menu:addItem(locale.view).onTouch = function()
local container = GUI.addBackgroundContainer(workspace, true, true, locale.view)
container.panel.eventHandler = nil
local colorSelector1 = container.layout:addChild(GUI.colorSelector(1, 1, 36, 3, config.transparencyBackground, locale.transBack))
local colorSelector2 = container.layout:addChild(GUI.colorSelector(1, 1, 36, 3, config.transparencyForeground, locale.transFor))
container.panel.eventHandler = function(workspace, object, e1)
if e1 == "touch" then
config.transparencyBackground, config.transparencyForeground = colorSelector1.color, colorSelector2.color
container.layout:addChild(GUI.button(1, 1, 30, 3, 0xFFFFFF, 0x555555, 0x880000, 0xFFFFFF, locale.ok)).onTouch = function()
config.transparencyBackground, config.transparencyForeground = colorSelector1.color, colorSelector2.color
container:remove()
workspace:draw()
saveConfig()
end
container:remove()
saveConfig()
end
container.layout:addChild(GUI.button(1, 1, 30, 3, 0xFFFFFF, 0x555555, 0x880000, 0xFFFFFF, locale.cancel)).onTouch = function()
container:remove()
end
end
workspace:draw()
local imageItem = menu:addContextMenuItem(locale.image)
imageItem:addItem(locale.flipVertical).onTouch = function()
window.image.data = image.flipVertically(window.image.data)
end
imageItem:addItem(locale.flipHorizontal).onTouch = function()
window.image.data = image.flipHorizontally(window.image.data)
end
imageItem:addSeparator()
imageItem:addItem(locale.rotate90).onTouch = function()
window.image.data = image.rotate(window.image.data, 90)
window.image.width = window.image.data[1]
window.image.height = window.image.data[2]
window.image.reposition()
end
imageItem:addItem(locale.rotate180).onTouch = function()
window.image.data = image.rotate(window.image.data, 180)
window.image.width = window.image.data[1]
window.image.height = window.image.data[2]
window.image.reposition()
end
imageItem:addItem(locale.rotate270).onTouch = function()
window.image.data = image.rotate(window.image.data, 270)
window.image.width = window.image.data[1]
window.image.height = window.image.data[2]
window.image.reposition()
end
local editItem = menu:addContextMenuItem(locale.edit)
editItem:addItem(locale.hueSaturation).onTouch = function()
local container = GUI.addBackgroundContainer(workspace, true, true, locale.hueSaturation)
container.layout:setSpacing(1, 1, 2)
container.panel.eventHandler = nil
local hue = container.layout:addChild(GUI.slider(1, 1, 50, 0x66DB80, 0x0, 0xFFFFFF, 0xAAAAAA, -360, 360, 0, true, locale.hue))
local satur = container.layout:addChild(GUI.slider(1, 1, 50, 0x66DB80, 0x0, 0xFFFFFF, 0xAAAAAA, -100, 100, 0, true, locale.saturation))
local bright = container.layout:addChild(GUI.slider(1, 1, 50, 0x66DB80, 0x0, 0xFFFFFF, 0xAAAAAA, -100, 100, 0, true, locale.brightness))
hue.roundValues = true
satur.roundValues = true
bright.roundValues = true
local buttonsLay = container.layout:addChild(GUI.layout(1, 1, 30, 7, 1, 1))
buttonsLay:addChild(GUI.button(1, 1, 30, 3, 0xFFFFFF, 0x555555, 0x880000, 0xFFFFFF, locale.ok)).onTouch = function()
window.image.data = image.hueSaturationBrightness(window.image.data, hue.value, satur.value/100, bright.value/100)
container:remove()
end
buttonsLay:addChild(GUI.button(1, 1, 30, 3, 0xFFFFFF, 0x555555, 0x880000, 0xFFFFFF, locale.cancel)).onTouch = function()
container:remove()
end
end
editItem:addItem(locale.colorBalance).onTouch = function()
local container = GUI.addBackgroundContainer(workspace, true, true, locale.colorBalance)
container.layout:setSpacing(1, 1, 2)
container.panel.eventHandler = nil
local r = container.layout:addChild(GUI.slider(1, 1, 50, 0x66DB80, 0x0, 0xFF0000, 0xAAAAAA, -255, 255, 0, true, "R: "))
local g = container.layout:addChild(GUI.slider(1, 1, 50, 0x66DB80, 0x0, 0x00FF00, 0xAAAAAA, -255, 255, 0, true, "G: "))
local b = container.layout:addChild(GUI.slider(1, 1, 50, 0x66DB80, 0x0, 0x0000FF, 0xAAAAAA, -255, 255, 0, true, "B: "))
r.roundValues = true
g.roundValues = true
b.roundValues = true
local buttonsLay = container.layout:addChild(GUI.layout(1, 1, 30, 7, 1, 1))
buttonsLay:addChild(GUI.button(1, 1, 30, 3, 0xFFFFFF, 0x555555, 0x880000, 0xFFFFFF, locale.ok)).onTouch = function()
window.image.data = image.colorBalance(window.image.data, math.floor(r.value), math.floor(g.value), math.floor(b.value))
container:remove()
end
buttonsLay:addChild(GUI.button(1, 1, 30, 3, 0xFFFFFF, 0x555555, 0x880000, 0xFFFFFF, locale.cancel)).onTouch = function()
container:remove()
end
end
editItem:addItem(locale.photoFilter).onTouch = function()
local container = GUI.addBackgroundContainer(workspace, true, true, locale.photoFilter)
container.layout:setSpacing(1, 1, 2)
container.panel.eventHandler = nil
local filterColor = container.layout:addChild(GUI.colorSelector(1, 1, 30, 3, 0x333333, locale.filterColor))
local transparency = container.layout:addChild(GUI.slider(1, 1, 50, 0x66DB80, 0x0, 0xFFFFFF, 0xAAAAAA, 0, 1, 0.5, true, locale.transparency))
local buttonsLay = container.layout:addChild(GUI.layout(1, 1, 30, 7, 1, 1))
buttonsLay:addChild(GUI.button(1, 1, 30, 3, 0xFFFFFF, 0x555555, 0x880000, 0xFFFFFF, locale.ok)).onTouch = function()
window.image.data = image.photoFilter(window.image.data, filterColor.color, transparency.value)
container:remove()
end
buttonsLay:addChild(GUI.button(1, 1, 30, 3, 0xFFFFFF, 0x555555, 0x880000, 0xFFFFFF, locale.cancel)).onTouch = function()
container:remove()
end
end
editItem:addSeparator()
editItem:addItem(locale.invertColors).onTouch = function()
window.image.data = image.invert(window.image.data)
end
editItem:addItem(locale.blackWhite).onTouch = function()
window.image.data = image.blackAndWhite(window.image.data)
end
editItem:addSeparator()
editItem:addItem(locale.gaussianBlur).onTouch = function()
local container = GUI.addBackgroundContainer(workspace, true, true, locale.gaussianBlur)
container.layout:setSpacing(1, 1, 2)
container.panel.eventHandler = nil
local radius = container.layout:addChild(GUI.slider(1, 1, 50, 0x66DB80, 0x0, 0xFFFFFF, 0xAAAAAA, 0, 5, 2.5, true, locale.radius))
local force = container.layout:addChild(GUI.slider(1, 1, 50, 0x66DB80, 0x0, 0xFFFFFF, 0xAAAAAA, 0, 1, 0.5, true, locale.force))
radius.roundValues = true
local buttonsLay = container.layout:addChild(GUI.layout(1, 1, 30, 7, 1, 1))
buttonsLay:addChild(GUI.button(1, 1, 30, 3, 0xFFFFFF, 0x555555, 0x880000, 0xFFFFFF, locale.ok)).onTouch = function()
window.image.data = image.gaussianBlur(window.image.data, math.floor(radius.value), force.value)
container:remove()
end
buttonsLay:addChild(GUI.button(1, 1, 30, 3, 0xFFFFFF, 0x555555, 0x880000, 0xFFFFFF, locale.cancel)).onTouch = function()
container:remove()
end
end
menu:addItem(locale.hotkeys).onTouch = function()
local container = GUI.addBackgroundContainer(workspace, true, true, locale.hotkeys)
container.layout:addChild(GUI.textBox(1, 1, 36, 1, nil, 0x969696, locale.hotkeysText, 1, 0, 0, true, true)).eventHandler = nil
workspace:draw()
end
window.currentToolOverlay = window:addChild(GUI.container(1, 1, 1, 1))

View File

@ -456,6 +456,161 @@ function image.blend(picture, blendColor, transparency)
return newPicture
end
function image.rotate(picture, angle)
local function copyPixel(newPic, oldPic, index)
table.insert(newPic, oldPic[index])
table.insert(newPic, oldPic[index + 1])
table.insert(newPic, oldPic[index + 2])
table.insert(newPic, oldPic[index + 3])
end
if angle == 90 then
local newPicture = {picture[2], picture[1]}
for i = 1, picture[2] do
for j = picture[1], 1, -1 do
local index = image.getIndex(i, j, picture[2])
copyPixel(newPicture, picture, index)
end
end
return newPicture
elseif angle == 180 then
local newPicture = {picture[1], picture[2]}
for j = picture[1], 1, -1 do
for i = picture[2], 1, -1 do
local index = image.getIndex(i, j, picture[2])
copyPixel(newPicture, picture, index)
end
end
return newPicture
elseif angle == 270 then
local newPicture = {picture[2], picture[1]}
for i = picture[2], 1, -1 do
for j = 1, picture[1] do
local index = image.getIndex(i, j, picture[2])
copyPixel(newPicture, picture, index)
end
end
return newPicture
else
error("Can't rotate image: angle must be 90, 180 or 270 degrees.")
end
end
function image.gaussianBlur(picture, radius, force)
local function createConvolutionMatrix(maximumValue, matrixSize)
local delta = maximumValue / matrixSize
local matrix = {}
for y = 1, matrixSize do
for x = 1, matrixSize do
local value = ((x - 1) * delta + (y - 1) * delta) / 2
matrix[y] = matrix[y] or {}
matrix[y][x] = value
end
end
return matrix
end
local function spreadPixelToSpecifiedCoordinates(picture, xCoordinate, yCoordinate, matrixValue, startBackground, startForeground, startAlpha, startSymbol)
local matrixBackground, matrixForeground, matrixAlpha, matrixSymbol = image.get(picture, xCoordinate, yCoordinate)
if matrixBackground and matrixForeground then
local newBackground = color.blend(startBackground, matrixBackground, matrixValue)
local newForeground = matrixSymbol == " " and newBackground or color.blend(startForeground, matrixForeground, matrixValue)
image.set(picture, xCoordinate, yCoordinate, newBackground, newForeground, 0x00, matrixSymbol)
end
end
local function spreadColorToOtherPixels(picture, xStart, yStart, matrix)
local startBackground, startForeground, startAlpha, startSymbol = image.get(picture, xStart, yStart)
local xCoordinate, yCoordinate
for yMatrix = 2, #matrix do
for xMatrix = 2, #matrix[yMatrix] do
xCoordinate, yCoordinate = xStart - xMatrix + 1, yStart - yMatrix + 1
spreadPixelToSpecifiedCoordinates(picture, xCoordinate, yCoordinate, matrix[yMatrix][xMatrix], startBackground, startForeground, startAlpha, startSymbol)
xCoordinate, yCoordinate = xStart + xMatrix - 1, yStart + yMatrix - 1
spreadPixelToSpecifiedCoordinates(picture, xCoordinate, yCoordinate, matrix[yMatrix][xMatrix], startBackground, startForeground, startAlpha, startSymbol)
end
end
end
local matrix = createConvolutionMatrix(force or 0x55, radius)
for y = 1, picture[2] do
for x = 1, picture[1] do
spreadColorToOtherPixels(picture, x, y, matrix)
end
end
return picture
end
function image.hueSaturationBrightness(picture, hue, saturation, brightness)
local function calculateBrightnessChanges(colr)
local h, s, b = color.integerToHSB(colr)
b = b + brightness; if b < 0 then b = 0 elseif b > 1 then b = 1 end
s = s + saturation; if s < 0 then s = 0 elseif s > 1 then s = 1 end
h = h + hue; if h < 0 then h = 0 elseif h > 360 then h = 360 end
return color.HSBToInteger(h, s, b)
end
for i = 3, #picture, 4 do
picture[i] = calculateBrightnessChanges(picture[i])
picture[i + 1] = calculateBrightnessChanges(picture[i + 1])
end
return picture
end
function image.hue(picture, hue)
return image.hueSaturationBrightness(picture, hue, 0, 0)
end
function image.saturation(picture, saturation)
return image.hueSaturationBrightness(picture, 0, saturation, 0)
end
function image.brightness(picture, brightness)
return image.hueSaturationBrightness(picture, 0, 0, brightness)
end
function image.blackAndWhite(picture)
return image.hueSaturationBrightness(picture, 0, -1, 0)
end
function image.colorBalance(picture, r, g, b)
local function calculateRGBChanges(colr)
local rr, gg, bb = color.integerToRGB(colr)
rr = rr + r; gg = gg + g; bb = bb + b
if rr < 0 then rr = 0 elseif rr > 255 then rr = 255 end
if gg < 0 then gg = 0 elseif gg > 255 then gg = 255 end
if bb < 0 then bb = 0 elseif bb > 255 then bb = 255 end
return color.RGBToInteger(rr, gg, bb)
end
for i = 3, #picture, 4 do
picture[i] = calculateRGBChanges(picture[i])
picture[i + 1] = calculateRGBChanges(picture[i + 1])
end
return picture
end
function image.invert(picture)
for i = 3, #picture, 4 do
picture[i] = 0xffffff - picture[i]
picture[i + 1] = 0xffffff - picture[i + 1]
end
return picture
end
function image.photoFilter(picture, colr, transparency)
if transparency < 0 then transparency = 0 elseif transparency > 1 then transparency = 1 end
for i = 3, #picture, 4 do
picture[i] = color.blend(picture[i], colr, transparency)
picture[i + 1] = color.blend(picture[i + 1], colr, transparency)
end
return picture
end
--------------------------------------------------------------------------------
return image