Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local color = require("grapes.Color")
- local Filesystem = require("grapes.Filesystem")
- local unicode = require("unicode")
- local bit32 = require("bit32")
- -- Grapes filesystem initialization
- local component = require("component")
- local bootFilesystemProxy = component.proxy(component.invoke(component.list("eeprom")(), "getData"))
- Filesystem.setProxy(bootFilesystemProxy)
- --------------------------------------------------------------------------------
- local image = {}
- local OCIFSignature = "OCIF"
- local encodingMethodsRead = {}
- local encodingMethodsReadMetadata = {}
- local encodingMethodsWrite = {}
- --------------------------------------------------------------------------------
- local function group(picture, compressColors)
- local groupedPicture, x, y, background, foreground = {}, 1, 1
- for i = 3, #picture, 4 do
- if compressColors then
- background, foreground = color.to8Bit(picture[i]), color.to8Bit(picture[i + 1])
- if i % 603 == 0 then
- computer.pullSignal(0)
- end
- else
- background, foreground = picture[i], picture[i + 1]
- end
- groupedPicture[picture[i + 2]] = groupedPicture[picture[i + 2]] or {}
- groupedPicture[picture[i + 2]][picture[i + 3]] = groupedPicture[picture[i + 2]][picture[i + 3]] or {}
- groupedPicture[picture[i + 2]][picture[i + 3]][background] = groupedPicture[picture[i + 2]][picture[i + 3]][background] or {}
- groupedPicture[picture[i + 2]][picture[i + 3]][background][foreground] = groupedPicture[picture[i + 2]][picture[i + 3]][background][foreground] or {}
- groupedPicture[picture[i + 2]][picture[i + 3]][background][foreground][y] = groupedPicture[picture[i + 2]][picture[i + 3]][background][foreground][y] or {}
- table.insert(groupedPicture[picture[i + 2]][picture[i + 3]][background][foreground][y], x)
- x = x + 1
- if x > picture[1] then
- x, y = 1, y + 1
- end
- end
- return groupedPicture
- end
- -- 5
- encodingMethodsWrite[5] = function(file, picture)
- file:writeBytes(
- bit32.rshift(picture[1], 8),
- bit32.band(picture[1], 0xFF)
- )
- file:writeBytes(
- bit32.rshift(picture[2], 8),
- bit32.band(picture[2], 0xFF)
- )
- for i = 3, #picture, 4 do
- file:writeBytes(
- color.to8Bit(picture[i]),
- color.to8Bit(picture[i + 1]),
- math.floor(picture[i + 2] * 255)
- )
- file:write(picture[i + 3])
- end
- end
- encodingMethodsReadMetadata[5] = function(file)
- return
- file:readBytes(2),
- file:readBytes(2)
- end
- encodingMethodsRead[5] = function(file, picture, width, height)
- picture[1] = width
- picture[2] = height
- for i = 1, width * height do
- table.insert(picture, color.to24Bit(file:readBytes(1)))
- table.insert(picture, color.to24Bit(file:readBytes(1)))
- table.insert(picture, file:readBytes(1) / 255)
- table.insert(picture, file:readUnicodeChar())
- end
- end
- local function readMetadata678(file, is7, is8)
- return
- file:readBytes(1) + is8,
- file:readBytes(1) + is8
- end
- local function read678(file, picture, width, height, is7, is8)
- picture[1] = width
- picture[2] = height
- local currentAlpha, currentSymbol, currentBackground, currentForeground, currentY
- for alpha = 1, file:readBytes(1) + is7 do
- currentAlpha = file:readBytes(1) / 255
- for symbol = 1, file:readBytes(2) + is7 do
- currentSymbol = file:readUnicodeChar()
- for background = 1, file:readBytes(1) + is7 do
- currentBackground = color.to24Bit(file:readBytes(1))
- for foreground = 1, file:readBytes(1) + is7 do
- currentForeground = color.to24Bit(file:readBytes(1))
- for y = 1, file:readBytes(1) + is7 do
- currentY = file:readBytes(1)
- for x = 1, file:readBytes(1) + is7 do
- image.set(
- picture,
- file:readBytes(1) + is8,
- currentY + is8,
- currentBackground,
- currentForeground,
- currentAlpha,
- currentSymbol
- )
- end
- end
- end
- end
- end
- end
- end
- local function saveOCIF678(file, picture, is7, is8)
- local function getGroupSize(t)
- local size = -is7
- for key in pairs(t) do
- size = size + 1
- end
- return size
- end
- -- Grouping picture by it's alphas, symbols and colors
- local groupedPicture = group(picture, true)
- -- Writing 1 byte per image width and height
- file:writeBytes(
- picture[1] - is8,
- picture[2] - is8
- )
- -- Writing 1 byte for alphas array size
- file:writeBytes(getGroupSize(groupedPicture))
- local symbolsSize
- for alpha in pairs(groupedPicture) do
- symbolsSize = getGroupSize(groupedPicture[alpha])
- file:writeBytes(
- -- Writing 1 byte for current alpha value
- math.floor(alpha * 255),
- -- Writing 2 bytes for symbols array size
- bit32.rshift(symbolsSize, 8),
- bit32.band(symbolsSize, 0xFF)
- )
- for symbol in pairs(groupedPicture[alpha]) do
- -- Writing current unicode symbol value
- file:write(symbol)
- -- Writing 1 byte for backgrounds array size
- file:writeBytes(getGroupSize(groupedPicture[alpha][symbol]))
- for background in pairs(groupedPicture[alpha][symbol]) do
- file:writeBytes(
- -- Writing 1 byte for background color value (compressed by color)
- background,
- -- Writing 1 byte for foregrounds array size
- getGroupSize(groupedPicture[alpha][symbol][background])
- )
- for foreground in pairs(groupedPicture[alpha][symbol][background]) do
- file:writeBytes(
- -- Writing 1 byte for foreground color value (compressed by color)
- foreground,
- -- Writing 1 byte for y array size
- getGroupSize(groupedPicture[alpha][symbol][background][foreground])
- )
- for y in pairs(groupedPicture[alpha][symbol][background][foreground]) do
- file:writeBytes(
- -- Writing 1 byte for current y value
- y - is8,
- -- Writing 1 byte for x array size
- #groupedPicture[alpha][symbol][background][foreground][y] - is7
- )
- for x = 1, #groupedPicture[alpha][symbol][background][foreground][y] do
- -- Wrting 1 byte for current x value
- file:writeBytes(groupedPicture[alpha][symbol][background][foreground][y][x] - is8)
- end
- end
- end
- end
- end
- end
- end
- -- 6
- encodingMethodsReadMetadata[6] = function(file)
- return readMetadata678(file, 0, 0)
- end
- encodingMethodsRead[6] = function(file, picture, width, height)
- read678(file, picture, width, height, 0, 0)
- end
- encodingMethodsWrite[6] = function(file, picture)
- saveOCIF678(file, picture, 0, 0)
- end
- -- 7
- encodingMethodsReadMetadata[7] = function(file)
- return readMetadata678(file, 1, 0)
- end
- encodingMethodsRead[7] = function(file, picture, width, height)
- read678(file, picture, width, height, 1, 0)
- end
- encodingMethodsWrite[7] = function(file, picture)
- saveOCIF678(file, picture, 1, 0)
- end
- -- 8
- encodingMethodsReadMetadata[8] = function(file)
- return readMetadata678(file, 1, 1)
- end
- encodingMethodsRead[8] = function(file, picture, width, height)
- read678(file, picture, width, height, 1, 1)
- end
- encodingMethodsWrite[8] = function(file, picture)
- saveOCIF678(file, picture, 1, 1)
- end
- --------------------------------------------------------------------------------
- function image.getIndex(x, y, width)
- return 4 * (width * (y - 1) + x) - 1
- end
- function image.create(width, height, background, foreground, alpha, symbol, random)
- local picture = {width, height}
- for i = 1, width * height do
- table.insert(picture, random and math.random(0x0, 0xFFFFFF) or (background or 0x0))
- table.insert(picture, random and math.random(0x0, 0xFFFFFF) or (foreground or 0x0))
- table.insert(picture, alpha or 0x0)
- table.insert(picture, random and string.char(math.random(65, 90)) or (symbol or " "))
- end
- return picture
- end
- function image.copy(picture)
- local newPicture = {}
- for i = 1, #picture do
- newPicture[i] = picture[i]
- end
- return newPicture
- end
- function image.save(path, picture, encodingMethod)
- encodingMethod = encodingMethod or 6
- local file, reason = Filesystem.open(path, "wb")
- if file then
- if encodingMethodsWrite[encodingMethod] then
- file:write(OCIFSignature, string.char(encodingMethod))
- local result, reason = xpcall(encodingMethodsWrite[encodingMethod], debug.traceback, file, picture)
- file:close()
- if result then
- return true
- else
- return false, "Failed to save OCIF image: " .. tostring(reason)
- end
- else
- file:close()
- return false, "Failed to save OCIF image: encoding method \"" .. tostring(encodingMethod) .. "\" is not supported"
- end
- else
- return false, "Failed to open file for writing: " .. tostring(reason)
- end
- end
- function image.readMetadata(file)
- local signature = file:readString(#OCIFSignature)
- if signature == OCIFSignature then
- local encodingMethod = file:readBytes(1)
- if encodingMethodsReadMetadata[encodingMethod] then
- local result, widthOrReason, height = xpcall(encodingMethodsReadMetadata[encodingMethod], debug.traceback, file, picture)
- if result then
- return signature, encodingMethod, widthOrReason, height
- else
- file:close()
- return false, "Failed to read OCIF metadata: " .. tostring(widthOrReason)
- end
- else
- file:close()
- return false, "Failed to read OCIF metadata: encoding method \"" .. tostring(encodingMethod) .. "\" is not supported"
- end
- else
- file:close()
- return false, "Failed to read OCIF metadata: binary signature \"" .. tostring(signature) .. "\" is not valid"
- end
- end
- function image.readPixelData(file, encodingMethod, width, height)
- local picture = {}
- local result, reason = xpcall(encodingMethodsRead[encodingMethod], debug.traceback, file, picture, width, height)
- file:close()
- if result then
- return picture
- else
- return false, "Failed to read OCIF pixel data: " .. tostring(reason)
- end
- end
- function image.load(path)
- local file, reason = Filesystem.open(path, "rb")
- if file then
- local signature, encodingMethod, width, height = image.readMetadata(file)
- if signature then
- return image.readPixelData(file, encodingMethod, width, height)
- else
- return false, encodingMethod
- end
- else
- return false, "Failed to open file \"" .. tostring(path) .. "\" for reading: " .. tostring(reason)
- end
- end
- -------------------------------------------------------------------------------
- function image.toString(picture)
- local charArray = {
- string.format("%02X", picture[1]),
- string.format("%02X", picture[2])
- }
- for i = 3, #picture, 4 do
- table.insert(charArray, string.format("%02X", color.to8Bit(picture[i])))
- table.insert(charArray, string.format("%02X", color.to8Bit(picture[i + 1])))
- table.insert(charArray, string.format("%02X", math.floor(picture[i + 2] * 255)))
- table.insert(charArray, picture[i + 3])
- if i % 603 == 0 then
- computer.pullSignal(0)
- end
- end
- return table.concat(charArray)
- end
- function image.fromString(pictureString)
- local picture = {
- tonumber("0x" .. unicode.sub(pictureString, 1, 2)),
- tonumber("0x" .. unicode.sub(pictureString, 3, 4)),
- }
- for i = 5, unicode.len(pictureString), 7 do
- table.insert(picture, color.to24Bit(tonumber("0x" .. unicode.sub(pictureString, i, i + 1))))
- table.insert(picture, color.to24Bit(tonumber("0x" .. unicode.sub(pictureString, i + 2, i + 3))))
- table.insert(picture, tonumber("0x" .. unicode.sub(pictureString, i + 4, i + 5)) / 255)
- table.insert(picture, unicode.sub(pictureString, i + 6, i + 6))
- end
- return picture
- end
- --------------------------------------------------------------------------------
- function image.set(picture, x, y, background, foreground, alpha, symbol)
- local index = image.getIndex(x, y, picture[1])
- picture[index], picture[index + 1], picture[index + 2], picture[index + 3] = background, foreground, alpha, symbol
- return picture
- end
- function image.get(picture, x, y)
- local index = image.getIndex(x, y, picture[1])
- return picture[index], picture[index + 1], picture[index + 2], picture[index + 3]
- end
- function image.getSize(picture)
- return picture[1], picture[2]
- end
- function image.getWidth(picture)
- return picture[1]
- end
- function image.getHeight(picture)
- return picture[2]
- end
- function image.transform(picture, newWidth, newHeight)
- local newPicture, stepWidth, stepHeight, background, foreground, alpha, symbol = {newWidth, newHeight}, picture[1] / newWidth, picture[2] / newHeight
- local x, y = 1, 1
- for j = 1, newHeight do
- for i = 1, newWidth do
- background, foreground, alpha, symbol = image.get(picture, math.floor(x), math.floor(y))
- table.insert(newPicture, background)
- table.insert(newPicture, foreground)
- table.insert(newPicture, alpha)
- table.insert(newPicture, symbol)
- x = x + stepWidth
- end
- x, y = 1, y + stepHeight
- end
- return newPicture
- end
- function image.crop(picture, fromX, fromY, width, height)
- if fromX >= 1 and fromY >= 1 and fromX + width - 1 <= picture[1] and fromY + height - 1 <= picture[2] then
- local newPicture, background, foreground, alpha, symbol = {width, height}
- for y = fromY, fromY + height - 1 do
- for x = fromX, fromX + width - 1 do
- background, foreground, alpha, symbol = image.get(picture, x, y)
- table.insert(newPicture, background)
- table.insert(newPicture, foreground)
- table.insert(newPicture, alpha)
- table.insert(newPicture, symbol)
- end
- end
- return newPicture
- else
- return false, "Failed to crop image: target coordinates are out of source range"
- end
- end
- function image.flipHorizontally(picture)
- local newPicture, background, foreground, alpha, symbol = {picture[1], picture[2]}
- for y = 1, picture[2] do
- for x = picture[1], 1, -1 do
- background, foreground, alpha, symbol = image.get(picture, x, y)
- table.insert(newPicture, background)
- table.insert(newPicture, foreground)
- table.insert(newPicture, alpha)
- table.insert(newPicture, symbol)
- end
- end
- return newPicture
- end
- function image.flipVertically(picture)
- local newPicture, background, foreground, alpha, symbol = {picture[1], picture[2]}
- for y = picture[2], 1, -1 do
- for x = 1, picture[1] do
- background, foreground, alpha, symbol = image.get(picture, x, y)
- table.insert(newPicture, background)
- table.insert(newPicture, foreground)
- table.insert(newPicture, alpha)
- table.insert(newPicture, symbol)
- end
- end
- return newPicture
- end
- function image.expand(picture, fromTop, fromBottom, fromLeft, fromRight, background, foreground, alpha, symbol)
- local newPicture = image.create(picture[1] + fromRight + fromLeft, picture[2] + fromTop + fromBottom, background, foreground, alpha, symbol)
- for y = 1, picture[2] do
- for x = 1, picture[1] do
- image.set(newPicture, x + fromLeft, y + fromTop, image.get(picture, x, y))
- end
- end
- return newPicture
- end
- function image.blend(picture, blendColor, transparency)
- local newPicture = {picture[1], picture[2]}
- for i = 3, #picture, 4 do
- table.insert(newPicture, color.blend(picture[i], blendColor, transparency))
- table.insert(newPicture, color.blend(picture[i + 1], blendColor, transparency))
- table.insert(newPicture, picture[i + 2])
- table.insert(newPicture, picture[i + 3])
- end
- 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
- copyPixel(newPicture, picture, image.getIndex(i, j, picture[2]))
- 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
- copyPixel(newPicture, picture, image.getIndex(i, j, picture[2]))
- 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
- copyPixel(newPicture, picture, image.getIndex(i, j, picture[2]))
- end
- end
- return newPicture
- else
- error("Can't rotate image: angle must be 90, 180 or 270 degrees.")
- end
- end
- function image.hueSaturationBrightness(picture, hue, saturation, brightness)
- local function calculate(c)
- local h, s, b = color.integerToHSB(c)
- 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] = calculate(picture[i])
- picture[i + 1] = calculate(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 calculate(c)
- local rr, gg, bb = color.integerToRGB(c)
- 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] = calculate(picture[i])
- picture[i + 1] = calculate(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.getGaussianBlurKernel(radius, weight)
- local size, index, sum, weightSquared2, value =
- radius * 2 + 1,
- 2,
- 0,
- 2 * weight * weight
- local kernel, constant =
- {size},
- 1 / (math.pi * weightSquared2)
- -- Filling convolution matrix
- for y = -radius, radius do
- for x = -radius, radius do
- value = constant * math.exp(-((y * y) + (x * x)) / weightSquared2);
- kernel[index] = value
- sum = sum + value;
- index = index + 1
- end
- end
- index = 2
- for y = 1, size do
- for x = 1, size do
- kernel[index] = kernel[index] * 1 / sum;
- index = index + 1
- end
- end
- return kernel;
- end
- function image.convolve(picture, kernel)
- -- Processing
- local
- pictureWidth,
- pictureHeight,
- kernelSize,
- pictureIndex,
- kernelIndex,
- kernelValue,
- rAcc,
- gAcc,
- bAcc,
- r,
- g,
- b,
- x,
- y = picture[1], picture[2], kernel[1], 3
- local newPicture, kernelRadius =
- { pictureWidth, pictureHeight },
- math.floor(kernelSize / 2)
- for pictureY = 1, pictureHeight do
- for pictureX = 1, pictureWidth do
- rAcc, gAcc, bAcc, kernelIndex = 0, 0, 0, 2
- -- Summing
- for kernelY = -kernelRadius, kernelRadius do
- y = pictureY + kernelY
- if y >= 1 and y <= pictureHeight then
- for kernelX = -kernelRadius, kernelRadius do
- x = pictureX + kernelX
- if x >= 1 and x <= pictureWidth then
- kernelValue = kernel[kernelIndex]
- r, g, b = color.integerToRGB(picture[4 * (pictureWidth * (y - 1) + x) - 1])
- rAcc, gAcc, bAcc = rAcc + r * kernelValue, gAcc + g * kernelValue, bAcc + b * kernelValue
- end
- kernelIndex = kernelIndex + 1
- end
- else
- kernelIndex = kernelIndex + kernelSize
- end
- end
- -- Setting pixel values on new picture
- if rAcc > 255 then
- rAcc = 255
- elseif rAcc < 0 then
- rAcc = 0
- else
- rAcc = rAcc - rAcc % 1
- end
- if gAcc > 255 then
- gAcc = 255
- elseif gAcc < 0 then
- gAcc = 0
- else
- gAcc = gAcc - gAcc % 1
- end
- if bAcc > 255 then
- bAcc = 255
- elseif bAcc < 0 then
- bAcc = 0
- else
- bAcc = bAcc - bAcc % 1
- end
- newPicture[pictureIndex] = color.RGBToInteger(rAcc, gAcc, bAcc)
- pictureIndex = pictureIndex + 1
- newPicture[pictureIndex] = 0x0
- pictureIndex = pictureIndex + 1
- newPicture[pictureIndex] = picture[pictureIndex]
- pictureIndex = pictureIndex + 1
- newPicture[pictureIndex] = " "
- pictureIndex = pictureIndex + 1
- end
- end
- return newPicture
- end
- --------------------------------------------------------------------------------
- return image
Add Comment
Please, Sign In to add comment