Apply outline of selected pattern and colors to all layers or frames [SOLVED]

Make the Aseprite API outline command recieve a LUA array(3) of arrays(3) (3x3 Matrix) of booleans for the outline pattern, instead of relying on the 4 basic options (square, circle, vertical, horizontal). I want to, in a script, be able to automate calling the outline dialog once and taking its opt object to iterate the command across all layers or frames with the same parameter. Alternatively, adding an option to apply outline to all layers or frames, or adding an “Outline Mode” for layers, so that it shows as an outline of the visible layers below.

The outline command’s matrix parameter also accepts an integer code, or composite thereof, where each neighbor pixel is encoded like this:

256, 128,  64,
 32,  16,   8,
  4,   2,   1

So the code below would activate the top-center, bottom-left and bottom-center neighbors:

app.command.Outline {
    color = Color { r = 255, g = 0, b = 0, a = 255 },
    matrix = 128 | 4 | 2,
    ui = false,
}

To convert dialog check widget arguments to a composited integer:

local dlg <const> = Dialog { title = "Outline Test" }
dlg:check { id = "m00", label = "Matrix:", selected = true, }
dlg:check { id = "m01", selected = true, }
dlg:check { id = "m02", selected = true, }
dlg:newrow { always = false }
dlg:check { id = "m10", selected = true, }
dlg:check { id = "m11", selected = false, }
dlg:check { id = "m12", selected = true, }
dlg:newrow { always = false }
dlg:check { id = "m20", selected = true, }
dlg:check { id = "m21", selected = true, }
dlg:check { id = "m22", selected = true, }
dlg:newrow { always = false }
dlg:button { id = "confirmButton", text = "&OK", onclick = function()
    local args <const> = dlg.data
    ---@type boolean[]
    local activeMatrix <const> = {
        args.m00 --[[@as boolean]],
        args.m01 --[[@as boolean]],
        args.m02 --[[@as boolean]],

        args.m10 --[[@as boolean]],
        args.m11 --[[@as boolean]],
        args.m12 --[[@as boolean]],

        args.m20 --[[@as boolean]],
        args.m21 --[[@as boolean]],
        args.m22 --[[@as boolean]]
    }

    local composite = 0
    local i = 0
    while i < 9 do
        i = i + 1
        local isActive <const> = activeMatrix[i]
        local code <const> = isActive and 1 << (9 - i) or 0
        composite = composite | code
    end

    app.command.Outline {
        ui = true,
        matrix = composite
    }
end }
dlg:show { wait = false }

I couldn’t find a simple way to target selected vs. all cels, though. When ui is false, the command is not applied to all layers, even though I changed preferences in advance:

app.preferences.filters.cels_target = 1

I’m assuming that all is 1 based on this in the source code.

It can be a hassle to target selected cels for a command through script, since a range’s cels cannot be set. Doubly so if you want to restore the user’s timeline range when the script is done.

For targeting all, I tried assigning sprite frame numbers to a range’s frames before calling the command:

local lenFrames = #app.sprite.frames
local rangeFrames = {}
local j = 0
while j < lenFrames do
    j = j + 1
    rangeFrames[j] = j
end
app.range.frames = rangeFrames

It’s simpler than assigning to range layers, which would require navigating nested groups.

The problem with this is that if the active cel is empty, Aseprite will throw an error, even if there are other frames in the range that have cels.

1 Like