FE8 palette tool: PNG -> 16-colour JASC .pal extraction & apply (fireemblem8u; inspired by Loxed's Porypal)
Project description
porypal-fe8
A small, FE8-oriented palette CLI for the
fireemblem8u decomp
graphics pipeline. It does two things:
extract— quantize a PNG down to ≤16 colours and write a GBA-style JASC.palpalette.apply— remap every pixel of a PNG to its nearest colour in a given.paland save an indexed PNG, ready forgbagfx.
It is a focused, clean reimplementation of the reusable core of
Loxed's Porypal — k-means colour
quantization in a perceptual colour space (Oklab) plus JASC .pal I/O — with
all the Pokémon-specific and UI parts dropped. See
Credits & license.
Why this exists (honest note)
For day-to-day FE8 graphics work you usually do not need this tool:
- Usenti handles palette editing, reduction, and indexed-PNG export interactively.
- The decomp's own
gbagfxalready converts between.png,.pal,.gbapal, and.4bpp.
porypal-fe8 is for batch / automated quantization — e.g. scripting the
reduction of many full-colour PNGs to 16-colour palettes in a build or asset
pipeline, where a headless CLI is more convenient than a GUI.
Install
Requires Python 3.9+.
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
# optional: install the `porypal-fe8` console command
pip install .
Without installing, you can run the module directly:
python3 palette_tool.py extract IN.png -o OUT.pal
Install / Releases
Once published to PyPI:
pip install porypal-fe8
Each v* tag also produces a GitHub Release with the built wheel and sdist
attached (under Releases),
so you can pip install a downloaded artifact even without PyPI.
Note: PyPI publishing uses OIDC trusted publishing (no API token). It requires a trusted publisher to be configured on PyPI for project
porypal-fe8, ownerlaqieer, workflowrelease.yml, environmentpypi. The GitHub Release job is independent and succeeds even before that is set up.
Usage
Extract a palette
porypal-fe8 extract IN.png -o OUT.pal [-n 16]
Quantizes IN.png to at most -n colours (default 16, the GBA 4bpp limit) and
writes a JASC .pal:
JASC-PAL
0100
16
115 131 164
255 255 255
...
Apply a palette
porypal-fe8 apply IN.png PALETTE.pal -o OUT.png
Remaps every pixel of IN.png to the nearest colour in PALETTE.pal (nearest
in Oklab) and saves an indexed (P-mode) PNG whose colours are exactly the
palette.
How it fits the FE8 pipeline
The decomp stores graphics as PNGs and JASC .pal palettes, and its Makefile
drives gbagfx to turn those into the GBA's native formats:
porypal-fe8 extract gbagfx (pal2gbapal)
IN.png ───────────────────────▶ OUT.pal ───────────────────▶ .gbapal (32 bytes for 16 colours)
porypal-fe8 apply gbagfx (png2gbagfx)
IN.png ───────────────────────▶ OUT.png ───────────────────▶ .4bpp
(+ .pal) (indexed)
Relevant Makefile rules in the decomp:
%.gbapal: %.pal ; $(PAL2GBAPAL) $< $@
%.gbapal: %.png ; $(GBAGFX) $< $@
A 16-colour JASC .pal converts to exactly 32 bytes of .gbapal
(16 colours × 2 bytes, the GBA's 15-bit BGR555 format). Palettes are written
with CRLF line endings to match the decomp's .pal files — gbagfx
rejects LF-only palettes. See the
fireemblem8u repo and
gbagfx for the
full graphics flow.
How it works
- Quantization clusters the image's pixels with k-means++ in Oklab, a perceptually uniform colour space, so the chosen colours match how the eye groups them. Each cluster centre is then snapped to the nearest actual colour in the source image, so every palette entry really occurs in the PNG. If the image already has ≤ N colours, they are kept verbatim.
- Apply assigns each pixel to the nearest palette colour, again measured in Oklab.
Credits & license
The colour-quantization approach and JASC .pal round-trip are inspired by
Loxed's Porypal. Porypal is licensed under
the GPL-3.0, which is incompatible with this project's MIT license, so
no Porypal source code was copied — this is an independent reimplementation
of the high-level idea only. Credit and thanks to Loxed.
porypal-fe8 itself is released under the MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file porypal_fe8-0.1.2.tar.gz.
File metadata
- Download URL: porypal_fe8-0.1.2.tar.gz
- Upload date:
- Size: 11.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c0e575bbbd6c14e9c17777773c6ab31a803f3f02ff03cd3000b1c82e5989b0b9
|
|
| MD5 |
e93622fcdd12720b64c2063a960f6382
|
|
| BLAKE2b-256 |
8670b9d6a473eaeed3f0a7414cd4dee6421e7875853bd8a6997d97c71f569db4
|
Provenance
The following attestation bundles were made for porypal_fe8-0.1.2.tar.gz:
Publisher:
release.yml on laqieer/porypal-fe8
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
porypal_fe8-0.1.2.tar.gz -
Subject digest:
c0e575bbbd6c14e9c17777773c6ab31a803f3f02ff03cd3000b1c82e5989b0b9 - Sigstore transparency entry: 1800024010
- Sigstore integration time:
-
Permalink:
laqieer/porypal-fe8@53dcaa430fd5b84caa1a3d562b46e08299a262a0 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/laqieer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@53dcaa430fd5b84caa1a3d562b46e08299a262a0 -
Trigger Event:
push
-
Statement type:
File details
Details for the file porypal_fe8-0.1.2-py3-none-any.whl.
File metadata
- Download URL: porypal_fe8-0.1.2-py3-none-any.whl
- Upload date:
- Size: 10.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3bb2b20d66984cf28e682aba7ea1d85495a25a3d61c2627262dd148c3fffdd80
|
|
| MD5 |
9db8203f14272597fcb9607e4e8b8239
|
|
| BLAKE2b-256 |
4425cbb596507e44ebee99dcc5207cd69972c63fdf28cdc5f6151757e9b4b527
|
Provenance
The following attestation bundles were made for porypal_fe8-0.1.2-py3-none-any.whl:
Publisher:
release.yml on laqieer/porypal-fe8
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
porypal_fe8-0.1.2-py3-none-any.whl -
Subject digest:
3bb2b20d66984cf28e682aba7ea1d85495a25a3d61c2627262dd148c3fffdd80 - Sigstore transparency entry: 1800024100
- Sigstore integration time:
-
Permalink:
laqieer/porypal-fe8@53dcaa430fd5b84caa1a3d562b46e08299a262a0 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/laqieer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@53dcaa430fd5b84caa1a3d562b46e08299a262a0 -
Trigger Event:
push
-
Statement type: