Updated Aseprite Tools (#39358)

Modified aesprite tools
This commit is contained in:
Southbridge 2025-08-28 17:44:50 -04:00 committed by Vanessa
parent 8aa6bdc284
commit d3e912f41a
3 changed files with 190 additions and 88 deletions

View File

@ -30,8 +30,8 @@ diag:button{
local selection = sprite.selection local selection = sprite.selection
local image = cel.image:clone() local image = cel.image:clone()
for x = 0, selection.bounds.width do for x = 0, selection.bounds.width - 1 do
for y = 0, selection.bounds.height do for y = 0, selection.bounds.height - 1 do
local xSel = x + selection.origin.x local xSel = x + selection.origin.x
local ySel = y + selection.origin.y local ySel = y + selection.origin.y

View File

@ -0,0 +1,63 @@
local sprite = app.editor.sprite
local cel = app.cel
function Shift(dx, dy)
if sprite.selection.isEmpty then
sprite.selection:selectAll()
end
local selection = sprite.selection
local image = cel.image:clone()
for it in image:pixels(selection) do
local color = Color(it())
local position = Point(it.x, it.y) -- gets the position
if not selection:contains(position.x + cel.position.x, position.y + cel.position.y) then
goto continue
end
color.red = math.min(255, math.max(0, color.red + dx))
color.green = math.min(255, math.max(0, color.green + dy))
it(color.rgbaPixel)
::continue::
end
cel.image = image
app.refresh()
end
local diag = Dialog{
title = "Shift Displacement Map"
}
diag
:button{
text="",
onclick=function()
Shift(0,1)
end
}
:newrow()
:button{
text="",
onclick=function()
Shift(1,0)
end
}
:button{
text="",
onclick=function()
Shift(-1,0)
end
}
:newrow()
:button{
text="",
onclick=function()
Shift(0,-1)
end
}
diag:show{wait=false}

View File

@ -5,113 +5,133 @@
-- TODO: Handling of sizes != 127 doesn't work properly and rounds differently from the real shader. Ah well. -- TODO: Handling of sizes != 127 doesn't work properly and rounds differently from the real shader. Ah well.
local scale = 4 local scale = 4
local hasOobPixels = false
-- This script requires UI -- This script requires UI
if not app.isUIAvailable then if not app.isUIAvailable then
return return
end end
local getOffsetPixel = function(x, y, image, rect) local sprite = app.editor.sprite
local posX = x - rect.x
local posY = y - rect.y
if posX < 0 or posX >= image.width or posY < 0 or posY >= image.height then local spriteChanged = sprite.events:on("change",
return image.spec.transparentColor function()
dialog:repaint()
end
)
dialog = Dialog{
title = "Displacement map preview",
onclose = function(ev)
sprite.events:off(spriteChanged)
end
}
function isOutOfBounds(x,y, dx, dy)
local size = dialog.data["frame-size"]
-- I messed around in Desmos for 2 hours trying to find a function that could do all of this at once
-- but I am sadly not a math major
-- This works by checking to see if we've wrapped around from say 31 to 01 which indicates that we've gone over
-- the edges of a sprite's bounds.
if dx > 0 and math.fmod(x+dx, size) < math.fmod(x, size) then
return true
end
-- gotta add size here in case we go from 0 -> -1, since mod -1 is just -1 not 31
if dx < 0 and math.fmod(x+size+dx, size) > math.fmod(x, size) then
return true
end
if dy > 0 and math.fmod(y+dy, size) < math.fmod(y, size) then
return true
end
if dy < 0 and math.fmod(y+size+dy, size) > math.fmod(y, size) then
return true
end end
return image:getPixel(posX, posY) return false
end end
local pixelValueToColor = function(sprite, value) function getOobColor(x,y)
return Color(value) if dialog.data["mark-oob-checkerboard"] then -- requested by Emogarbage :3
local size = dialog.data["frame-size"]
if (math.sin(math.pi*x*8.0/size) > 0) == (math.cos(math.pi*y*8.0/size) > 0) then
return Color{r=0, g=0, b=0, a=255}
end
end
return dialog.data["mark-oob-color"]
end end
local applyDisplacementMap = function(width, height, size, displacement, displacementRect, target, targetRect) function getOffsetPixel(x, y, dx, dy, image, bounds)
-- print(Color(displacement:getPixel(17, 15)).red) if isOutOfBounds(x,y,dx,dy,image) then
local image = target:clone() hasOobPixels = true
if dialog.data["mark-oob"] then
return getOobColor(x,y)
end
end
local adj_x = x - bounds.x
local adj_y = y - bounds.y
if (image.bounds:contains(Rectangle{adj_x+dx, adj_y+dy, 1, 1})) then
return image:getPixel(adj_x+dx, adj_y+dy)
end
return image.spec.transparentColor
end
function applyDisplacementMap(width, height, displacement, target)
local image = target.image:clone()
image:resize(width, height) image:resize(width, height)
image:clear() image:clear()
local displacement_size = dialog.data["displacement_size"]
for x = 0, width - 1 do for x = 0, width - 1 do
for y = 0, height - 1 do for y = 0, height - 1 do
local value = getOffsetPixel(x, y, displacement, displacementRect) if not displacement.bounds:contains(Rectangle{x,y,1,1}) then
local color = pixelValueToColor(sprite, value) goto continue
if color.alpha ~= 0 then
local offset_x = (color.red - 128) / 127 * size
local offset_y = (color.green - 128) / 127 * size
local colorValue = getOffsetPixel(x + offset_x, y + offset_y, target, targetRect)
image:drawPixel(x, y, colorValue)
end end
local color = Color(displacement.image:getPixel(x - displacement.bounds.x,y - displacement.bounds.y))
if color.alpha == 0 then
goto continue
end
local dx = (color.red - 128) / 127 * displacement_size
local dy = (color.green - 128) / 127 * displacement_size
local colorValue = getOffsetPixel(x, y, dx, dy, target.image, target.bounds)
image:drawPixel(x, y, colorValue)
::continue::
end end
end end
return image return image
end end
local dialog = nil
local sprite = app.editor.sprite
local spriteChanged = sprite.events:on("change",
function(ev)
dialog:repaint()
end)
local layers = {} local layers = {}
for i,layer in ipairs(sprite.layers) do for i,layer in ipairs(sprite.layers) do
table.insert(layers, 1, layer.name) table.insert(layers, 1, layer.name)
end end
local findLayer = function(sprite, name) function findLayer(_sprite, name)
for i, layer in ipairs(sprite.layers) do for i,layer in ipairs(_sprite.layers) do
if layer.name == name then if layer.name == name then
return layer return layer
end end
end end
return nil return nil
end end
local applyOffset = function(dx, dy)
local cel = app.cel
local image = cel.image:clone()
local sprite = app.editor.sprite
local selection = sprite.selection
for x = selection.bounds.x, selection.bounds.x + selection.bounds.width - 1 do
for y = selection.bounds.y, selection.bounds.y + selection.bounds.height - 1 do
local xImg = x - cel.position.x
local yImg = y - cel.position.y
if xImg >= 0 and xImg < image.width and yImg >= 0 and yImg < image.height then
local pixelValue = image:getPixel(xImg, yImg)
local color = Color(pixelValue)
-- Offset R and G channel
color.red = math.min(255, math.max(0, color.red + dx))
color.green = math.min(255, math.max(0, color.green + dy))
image:drawPixel(xImg, yImg, app.pixelColor.rgba(color.red, color.green, color.blue, color.alpha))
end
end
end
cel.image = image
dialog:repaint()
end
dialog = Dialog{
title = "Displacement map preview",
onclose = function(ev)
sprite.events:off(spriteChanged)
end}
dialog:canvas{ dialog:canvas{
id = "canvas", id = "canvas",
width = sprite.width * scale, width = sprite.width * scale,
height = sprite.height * scale, height = sprite.height * scale,
onpaint = function(ev) onpaint = function(ev)
local context = ev.context local context = ev.context
hasOobPixels = false
local layerDisplacement = findLayer(sprite, dialog.data["displacement-select"]) local layerDisplacement = findLayer(sprite, dialog.data["displacement-select"])
local layerTarget = findLayer(sprite, dialog.data["reference-select"]) local layerTarget = findLayer(sprite, dialog.data["reference-select"])
@ -139,9 +159,8 @@ dialog:canvas{
-- Apply displacement map and draw -- Apply displacement map and draw
local image = applyDisplacementMap( local image = applyDisplacementMap(
sprite.width, sprite.height, sprite.width, sprite.height,
dialog.data["size"], celDisplacement,
celDisplacement.image, celDisplacement.bounds, celTarget)
celTarget.image, celTarget.bounds)
context:drawImage( context:drawImage(
-- srcImage -- srcImage
@ -154,6 +173,10 @@ dialog:canvas{
0, 0, 0, 0,
-- dstSize -- dstSize
image.width * scale, image.height * scale) image.width * scale, image.height * scale)
dialog:modify{
id = "oob-pixels-warn",
visible = hasOobPixels
}
end end
} }
@ -185,7 +208,7 @@ dialog:combobox{
} }
dialog:slider{ dialog:slider{
id = "size", id = "displacement_size",
label = "displacement size", label = "displacement size",
min = 127, --We dont support non 127 atm min = 127, --We dont support non 127 atm
max = 127, max = 127,
@ -195,35 +218,51 @@ dialog:slider{
end end
} }
dialog:button{ -- Out of Bounds marking
id = "moveDown", dialog:separator()
text = "Down",
dialog:label{
id = "oob-pixels-warn",
text = "Warning: Out-of-bounds displacements detected!",
visible = false
}
dialog:check{
id = "mark-oob",
label = "Mark Out-of-Bounds Displacements",
selected = false,
hexpand = false,
onclick = function(ev) onclick = function(ev)
applyOffset(0, -1) dialog:repaint()
end end
} }
dialog:button{ dialog:check{
id = "moveUp", id = "mark-oob-checkerboard",
text = "Up", label = "Checkerboard Pattern",
selected = false,
hexpand = false,
onclick = function(ev) onclick = function(ev)
applyOffset(0, 1) dialog:repaint()
end end
} }
dialog:button{ dialog:number{
id = "moveLeft", id = "frame-size",
text = "Left", label = "Frame Size",
onclick = function(ev) text = "32",
applyOffset(1, 0) hexpand = false,
onchange = function(ev)
dialog:repaint()
end end
} }
dialog:button{ dialog:color{
id = "moveRight", id = "mark-oob-color",
text = "Right", label = "Out-of-Bounds Pixels Color",
onclick = function(ev) color = Color{r = 255, g = 0, b = 0},
applyOffset(-1, 0) onchange = function(ev)
dialog:repaint()
end end
} }