Skip to content

Instantly share code, notes, and snippets.

@EgoMoose
Created January 16, 2025 21:26
Show Gist options
  • Save EgoMoose/d82214f96f974eee7314d99ab7e4a4fd to your computer and use it in GitHub Desktop.
Save EgoMoose/d82214f96f974eee7314d99ab7e4a4fd to your computer and use it in GitHub Desktop.
--!strict
local AssetService = game:GetService("AssetService")
local THICKNESS = 5
local RGBA = { 255, 255, 255, 255 }
local NEIGHBOR_OFFSETS: { { number } } = {
{ -1, -1 },
{ 0, -1 },
{ 1, -1 },
{ -1, 0 },
{ 1, 0 },
{ -1, 1 },
{ 0, 1 },
{ 1, 1 },
}
local function outlineBuffer(imgBuffer: buffer, width: number, height: number, thickness: number, rgba: {number})
local result = buffer.create(width * height * 4)
buffer.copy(result, 0, imgBuffer)
local function readRGBA(index: number)
local r = buffer.readu8(result, index + 0)
local g = buffer.readu8(result, index + 1)
local b = buffer.readu8(result, index + 2)
local a = buffer.readu8(result, index + 3)
return r, g, b, a
end
local function writeRGBA(index: number, r: number, g: number, b: number, a: number)
buffer.writeu8(result, index + 0, r)
buffer.writeu8(result, index + 1, g)
buffer.writeu8(result, index + 2, b)
buffer.writeu8(result, index + 3, a)
end
local queue = {}
local queueFront = 1
local queueLength = 0
local function pushBack(value: number)
queue[queueFront + queueLength] = value
queueLength = queueLength + 1
end
local function popFront()
local value = queue[queueFront]
queue[queueFront] = nil
queueFront = queueFront + 1
queueLength = queueLength - 1
return value
end
local function getNeighbors(index: number)
local j = index / 4
local y = math.floor(j / width)
local x = j - y * width
local neighbors = {}
for _, offset in NEIGHBOR_OFFSETS do
local nx = x + offset[1] + 1
local ny = y + offset[2] + 1
if nx > 0 and nx <= width and ny > 0 and ny <= height then
local k = ((nx - 1) + (ny - 1) * width) * 4
table.insert(neighbors, k)
end
end
return neighbors
end
local visited = {}
for x = 1, width do
for y = 1, height do
local index = ((x - 1) + (y - 1) * width) * 4
local _r, _g, _b, a = readRGBA(index)
if a == 255 then
visited[index] = true
continue
end
local hasFullyOpaqueNeighbor = false
for _, neighborIndex in getNeighbors(index) do
local _nr, _ng, _nb, na = readRGBA(neighborIndex)
if na == 255 then
hasFullyOpaqueNeighbor = true
break
end
end
if hasFullyOpaqueNeighbor then
visited[index] = true
pushBack(index)
end
end
end
for _ = 1, thickness do
for _ = 1, queueLength do
local poppedIndex = popFront()
writeRGBA(poppedIndex, rgba[1], rgba[2], rgba[3], rgba[4])
for _, neighborIndex in getNeighbors(poppedIndex) do
if not visited[neighborIndex] then
pushBack(neighborIndex)
visited[neighborIndex] = true
end
end
end
end
return result
end
local function outlineEditImage(editImage: EditableImage)
local imgSize = editImage.Size
local imgBuffer = editImage:ReadPixelsBuffer(Vector2.zero, imgSize)
local outlinedBuffer = outlineBuffer(imgBuffer, imgSize.X, imgSize.Y, THICKNESS, RGBA)
local outlinedEditImage = AssetService:CreateEditableImage({ Size = imgSize })
outlinedEditImage:WritePixelsBuffer(Vector2.zero, imgSize, outlinedBuffer)
return outlinedEditImage
end
@EgoMoose
Copy link
Author

A few examples:
RobloxStudioBeta_EgNoVpQNy7
RobloxStudioBeta_ZZyLlvliEd
RobloxStudioBeta_jRjwQVDJIQ

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment