How or where can I get a GBA color

I’m making Sprites as a hobby and my favorite designs are the Game Boy Advance ones, mostly because of their bright and colorful palettes. The problem is that I can’t find a palette that contains the colors used in this console and I would like to use them to make that contrast that that console showed (the Wikipedia entry is of no use, only to show you the combination operation and that’s it)

The Wikipedia entry mentioned that Game Boy Advance has 15 bit RGB, which is 32768 colors.

That’s so many colors I can’t even copy and paste the palette into my reply. So here’s a link to a Github gist.

The soft cap on palette length in Aseprite (and GIMP) for indexed color mode is 256 colors. So palettes for a specific game on the GBA might be more practical if you can find one of them.

Another option is to work in the default 24 bit RGB (plus another 8 for alpha), then quantize to 15 bit.

This is a Lua script that generated the palette:

local sprite <const> = app.sprite
if not sprite then return end

local floor <const> = math.floor
local n <const> = 2 << (5 - 1)

local palette <const> = Palette(1 + n * n * n)
palette:setColor(0, Color { r = 0, g = 0, b = 0, a = 0 })

local m = 0
local i = 0
while i < n do
    local blue <const> = floor(0.5 + 255.0 * i / (n - 1.0))
    local j = 0
    while j < n do
        local green <const> = floor(0.5 + 255.0 * j / (n - 1.0))
        local k = 0
        while k < n do
            local red <const> = floor(0.5 + 255.0 * k / (n - 1.0))
            local aseColor <const> = Color { r = red, g = green, b = blue, a = 255 }
            m = m + 1
            palette:setColor(m, aseColor)
            k = k + 1
        end
        j = j + 1
    end
    i = i + 1
end

sprite:setPalette(palette)

in fact, the “32768 colors” refer to the number of colors that the hardware can create, but the GBA does not show this number, but a reduced number of 512, which is what I need, but thank you very much for your help : )

In that case, the next place I would look are emulator tools. For example, mGBA has a palette tool, a forum where you can ask questions and its source code is on Github.

Unfortunately, the export pal button writes binary pal files. As far as I know Aseprite imports plain text ASCII files. [Edit: If you don’t mind exporting act files, an Aseprite act importer can be found here.]

Is it possible to translate the Binary export into ASCII text?

Aseprite is extensible with a Lua scripting API, so an importer can be made. Scripts with the lua extension are placed in the directory opened from File > Scripts > Open Scripts Folder. After the folder is rescanned, the script can then be run from File > Scripts.

Below is a rough script that is not robust, but does ok parsing the binary riff pals I’ve exported from mGba. If you search, I’m sure you can find alternatives that convert palettes from riff pal to gpl or whatever, as well as explainers on how they work, like this one.

I found out a few minutes after I posted that mGba also supports act file format export. I already wrote an importer for that format, see above.

local dlg = Dialog { title = "RIFF PAL Import" }

dlg:file {
    id = "importFilepath",
    label = "Open:",
    filetypes = { "pal" },
    open = true,
    focus = true
}

dlg:check {
    id = "preserveIndices",
    label = "Keep",
    text = "&Order",
    selected = false
}

dlg:button {
    id = "importButton",
    text = "&IMPORT",
    focus = false,
    onclick = function()
        local sprite <const> = app.sprite
        if not sprite then
            app.alert {
                title = "Error",
                text = "There is no active sprite."
            }
            return
        end

        local args <const> = dlg.data
        local importFilepath <const> = args.importFilepath --[[@as string]]

        if (not importFilepath) or (#importFilepath < 1)
            or (not app.fs.isFile(importFilepath)) then
            app.alert {
                title = "Error",
                text = "Invalid file path."
            }
            return
        end

        local fileExtLc <const> = string.lower(app.fs.fileExtension(importFilepath))
        if fileExtLc ~= "pal" then
            app.alert {
                title = "Error",
                text = "File format must be pal."
            }
            return
        end

        local binFile <const>, err <const> = io.open(importFilepath, "rb")
        if err ~= nil then
            if binFile then binFile:close() end
            app.alert { title = "Error", text = err }
            return
        end
        if binFile == nil then return end

        local fileData <const> = binFile:read("a")
        binFile:close()

        local strbyte <const> = string.byte
        local strsub <const> = string.sub
        local strunpack <const> = string.unpack

        local lenFileData <const> = #fileData
        -- print(string.format("lenFileData: %d", lenFileData))

        local magicCheckRiff <const> = strunpack("I4", "RIFF")
        local magicWordRiff <const> = strunpack("I4", strsub(fileData, 1, 4))
        -- print(string.format("magicWordRiff: %d, magicCheckRiff: %d %s",
        --     magicWordRiff, magicCheckRiff,
        --     magicWordRiff == magicCheckRiff and "MATCH" or "MISMATCH"))
        if magicWordRiff ~= magicCheckRiff then
            app.alert { title = "Error", text = "File is not a RIFF." }
            return
        end

        -- local lenRiff <const> = strunpack("I4", strsub(fileData, 5, 8))
        -- print(string.format("lenRiff: %d", lenRiff))

        local magicCheckPal <const> = strunpack("I4", "PAL ")
        local magicWordPal <const> = strunpack("I4", strsub(fileData, 9, 12))
        -- print(string.format("magicWordPal: %d, magicCheckPal: %d %s",
        --     magicWordPal, magicCheckPal,
        --     magicWordPal == magicCheckPal and "MATCH" or "MISMATCH"))
        if magicWordPal ~= magicCheckPal then
            app.alert { title = "Error", text = "File is not a palette." }
            return
        end

        local magicCheckData <const> = strunpack("I4", "data")
        local magicWordData = 0
        local i = 12
        while i < lenFileData and magicWordData ~= magicCheckData do
            i = i + 1
            magicWordData = strunpack("I4", strsub(fileData, i, i + 3))
        end

        -- print(string.format("i: %d, magicWordData: %d, magicCheckData: %d %s",
        --     i, magicWordData, magicCheckData,
        --     magicWordData == magicCheckData and "MATCH" or "MISMATCH"))

        local palIdx = 0

        ---@type table<integer, integer[]>
        local hexDict <const> = {}
        local lenUniques = 0

        local j = i + 12
        while j < lenFileData do
            local r8 <const> = strbyte(fileData, j, j)
            local g8 <const> = strbyte(fileData, j + 1, j + 1)
            local b8 <const> = strbyte(fileData, j + 2, j + 2)
            local abgr32 <const> = 0xff000000
                | (b8 << 0x10)
                | (g8 << 0x08)
                | r8

            if hexDict[abgr32] then
                local arr <const> = hexDict[abgr32]
                arr[#arr + 1] = palIdx
            else
                hexDict[abgr32] = { palIdx }
                lenUniques = lenUniques + 1
            end

            palIdx = palIdx + 1
            j = j + 4
        end

        local preserveIndices = args.preserveIndices --[[@as boolean]]
        if preserveIndices then
            local pal <const> = Palette(256)
            for abgr32, indices in pairs(hexDict) do
                local r8 <const> = abgr32 & 0xff
                local g8 <const> = (abgr32 >> 0x08) & 0xff
                local b8 <const> = (abgr32 >> 0x10) & 0xff
                local aseColor <const> = Color { r = r8, g = g8, b = b8, a = 255 }

                local lenIdcs <const> = #indices
                local k = 0
                while k < lenIdcs do
                    k = k + 1
                    local idx <const> = indices[k]
                    pal:setColor(idx, aseColor)
                end
            end
            sprite:setPalette(pal)
        else
            ---@type integer[]
            local sortedUniquesArr <const> = {}
            for abgr32, _ in pairs(hexDict) do
                sortedUniquesArr[#sortedUniquesArr + 1] = abgr32
            end

            table.sort(sortedUniquesArr, function(a, b)
                return hexDict[a][1] < hexDict[b][1]
            end)

            local pal <const> = Palette(lenUniques)
            local k = 0
            while k < lenUniques do
                local abgr32 <const> = sortedUniquesArr[1 + k]
                local r8 <const> = abgr32 & 0xff
                local g8 <const> = (abgr32 >> 0x08) & 0xff
                local b8 <const> = (abgr32 >> 0x10) & 0xff
                pal:setColor(k, Color { r = r8, g = g8, b = b8, a = 255 })
                k = k + 1
            end
            sprite:setPalette(pal)
        end
    end
}

dlg:button {
    id = "cancel",
    text = "&CANCEL",
    focus = false,
    onclick = function()
        dlg:close()
    end
}

dlg:show { wait = false }

Once imported into Aseprite, you can re-export the palette (as pal, gpl, an image, etc.) by looking in the hamburger menu above the color swatches, to the right of the palette padlock and palette presets buttons.

1 Like

uhhh… I think I can understand that
thank you

also, do you know a game that uses the complete palette in one screen?