This is a PNM import-export dialog for use with the Aseprite scripting API. The PNM family of image formats supported by this dialog are pbm
, pgm
and ppm
.
ppm
files support up to 256-bit per red, green and blue color channels. Alpha is not included. The maximum per each channel can be adjusted.pgm
files support up to 256-bit per gray channel. Alpha is not included. The maximum per channel can be adjusted.pbm
files support either1
or0
per pixel. The format specifies that1
is black while0
is white. This dialog inverts this to match image conventions.
These three files can be formatted either as human-readable ASCII or as binary.
To download this script, click on the green Code button above, then select Download Zip. You can also click on the netpbmio.lua
file. Beware that some browsers will append a .txt
file format extension to script files on download. Aseprite will not recognize the script until this is removed and the original .lua
extension is used. There can also be issues with copying and pasting. Be sure to click on the Raw file button; do not copy the formatted code.
To use this script, open Aseprite. In the menu bar, go to File > Scripts > Open Scripts Folder
. Move the Lua script into the folder that opens. Return to Aseprite; go to File > Scripts > Rescan Scripts Folder
(the default hotkey is F5
). The script should now be listed under File > Scripts
. Select netpbmio.lua
to launch the dialog.
If an error message in Aseprite's console appears, check if the script folder is on a file path that includes characters beyond ASCII, such as 'é' (e acute) or 'ö' (o umlaut).
A hot key can be assigned to the script by going to Edit > Keyboard Shortcuts
. The search input box in the top left of the shortcuts dialog can be used to locate the script by its file name. The dialog can be closed with Alt+C
. The import button can be activated with Alt+I
; export, with Alt+E
.
Import and export ignore alpha completely. For example transparent red, 0x000000ff
, will not be corrected to opaque black, 0xff000000
. I recommend that you set an opaque background layer if you want to avoid issues.
PNM file formats support neither layers nor frames. For that reason, a flattened copy of the sprite is made at the active frame (unless the script is called from the CLI, see below).
When the color maximum is reduced, the script performs no dithering, unlike GIMP or Krita. Channel contraction uses the formula floor(val * (mx / 255) + 0.5)
; expansion, floor(val * (255 / mx) + 0.5)
. This is equivalent to a signed, rather than an unsigned, quantization. The difference can be illustrated with this Desmos graph or a comparison of gradients.
Unsigned quantization is in the middle row; signed is on the bottom. If you prefer unsigned quantization, or require a dithering tool for one-bit or quantized color, see AsepriteAddons.
Aseprite's definition of "luma" to convert to grayscale is used by both pbm
and pgm
exports. For more info, I wrote a guide comparing grayscale conversion methods here. pbm
grayscale values are then thresholded against a pivot, 128
by default.
This script's parser expects the header, image dimensions, max channel and pixel data to be separated by line breaks. In other words, don't expect one liner files to parse correctly.
Below is an example output for a 9 by 16 ppm file, upscaled to 90 by 160:
In a text editor, the ASCII version looks like this:
P3
9 16
255
220 058 058 182 037 079 151 028 090 137 036 105 132 038 109 137 036 105 151 028 090 182 037 079 220 058 058
142 033 100 104 047 120 081 064 143 057 078 153 045 082 155 057 078 153 081 064 143 104 047 120 142 033 100
081 064 143 000 094 156 000 109 156 000 118 156 000 122 155 000 118 156 000 109 156 000 094 156 081 064 143
000 103 156 000 126 155 000 143 149 000 152 133 000 154 126 000 152 133 000 143 149 000 126 155 000 103 156
000 129 154 000 152 133 042 171 096 115 190 069 138 196 059 115 190 069 042 171 096 000 152 133 000 129 154
000 149 140 065 178 089 166 207 048 213 219 027 227 220 025 213 219 027 166 207 048 065 178 089 000 149 140
000 157 107 151 201 054 227 220 025 255 199 017 255 179 018 255 199 017 227 220 025 151 201 054 000 157 107
042 171 096 181 212 041 254 217 023 252 151 028 238 103 044 252 151 028 254 217 023 181 212 041 042 171 096
042 171 096 181 212 041 254 217 023 252 151 028 238 103 044 252 151 028 254 217 023 181 212 041 042 171 096
000 157 107 151 201 054 227 220 025 255 199 017 255 179 018 255 199 017 227 220 025 151 201 054 000 157 107
000 149 140 065 178 089 166 207 048 213 219 027 227 220 025 213 219 027 166 207 048 065 178 089 000 149 140
000 129 154 000 152 133 042 171 096 115 190 069 138 196 059 115 190 069 042 171 096 000 152 133 000 129 154
000 103 156 000 126 155 000 143 149 000 152 133 000 154 126 000 152 133 000 143 149 000 126 155 000 103 156
081 064 143 000 094 156 000 109 156 000 118 156 000 122 155 000 118 156 000 109 156 000 094 156 081 064 143
142 033 100 104 047 120 081 064 143 057 078 153 045 082 155 057 078 153 081 064 143 104 047 120 142 033 100
220 058 058 182 037 079 151 028 090 137 036 105 132 038 109 137 036 105 151 028 090 182 037 079 220 058 058
In a hex editor, the binary version looks like this:
50 36 0A 39 20 31 36 0A 32 35 35 0A
DC 3A 3A B6 25 4F 97 1C 5A 89 24 69
84 26 6D 89 24 69 97 1C 5A B6 25 4F
DC 3A 3A 8E 21 64 68 2F 78 51 40 8F
39 4E 99 2D 52 9B 39 4E 99 51 40 8F
68 2F 78 8E 21 64 51 40 8F 00 5E 9C
00 6D 9C 00 76 9C 00 7A 9B 00 76 9C
00 6D 9C 00 5E 9C 51 40 8F 00 67 9C
00 7E 9B 00 8F 95 00 98 85 00 9A 7E
00 98 85 00 8F 95 00 7E 9B 00 67 9C
00 81 9A 00 98 85 2A AB 60 73 BE 45
8A C4 3B 73 BE 45 2A AB 60 00 98 85
00 81 9A 00 95 8C 41 B2 59 A6 CF 30
D5 DB 1B E3 DC 19 D5 DB 1B A6 CF 30
41 B2 59 00 95 8C 00 9D 6B 97 C9 36
E3 DC 19 FF C7 11 FF B3 12 FF C7 11
E3 DC 19 97 C9 36 00 9D 6B 2A AB 60
B5 D4 29 FE D9 17 FC 97 1C EE 67 2C
FC 97 1C FE D9 17 B5 D4 29 2A AB 60
2A AB 60 B5 D4 29 FE D9 17 FC 97 1C
EE 67 2C FC 97 1C FE D9 17 B5 D4 29
2A AB 60 00 9D 6B 97 C9 36 E3 DC 19
FF C7 11 FF B3 12 FF C7 11 E3 DC 19
97 C9 36 00 9D 6B 00 95 8C 41 B2 59
A6 CF 30 D5 DB 1B E3 DC 19 D5 DB 1B
A6 CF 30 41 B2 59 00 95 8C 00 81 9A
00 98 85 2A AB 60 73 BE 45 8A C4 3B
73 BE 45 2A AB 60 00 98 85 00 81 9A
00 67 9C 00 7E 9B 00 8F 95 00 98 85
00 9A 7E 00 98 85 00 8F 95 00 7E 9B
00 67 9C 51 40 8F 00 5E 9C 00 6D 9C
00 76 9C 00 7A 9B 00 76 9C 00 6D 9C
00 5E 9C 51 40 8F 8E 21 64 68 2F 78
51 40 8F 39 4E 99 2D 52 9B 39 4E 99
51 40 8F 68 2F 78 8E 21 64 DC 3A 3A
B6 25 4F 97 1C 5A 89 24 69 84 26 6D
89 24 69 97 1C 5A B6 25 4F DC 3A 3A
The pixel in the top left corner can be seen as 220
for the red channel, 58
for the green and 58
for the blue. Or, in hexadecimal: 0xDC
, 0x3A
and 0x3A
. The file header is still in human readable form: 0x50
0x36
is P3
, 0x39
0x20
0x31
0x36
is 9 16
and 0x32
0x35
0x35
is 255
.
Below is from the same source image as above, but exported as a pbm:
The ASCII version looks like this:
P1
9 16
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 0 0 0 0 0 1 1
1 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 1
0 0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0 0
1 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 1
1 1 0 0 0 0 0 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
The binary version looks like this:
50 34 0A 39 20 31 36 0A FF FF FF FF
FF FF FF FF C1 FF 80 FF 80 FF 08 7F
08 7F 80 FF 80 FF C1 FF FF FF FF FF
FF FF FF FF
Binary pbms pack 8 pixels of binary data into one byte, with extra padding depending on the image width.
Due to the relative slowness of Lua scripts, I recommend using another graphics package to convert files to or from PNM in bulk. However, Aseprite does support calling scripts from the command line (CLI). This script has been updated to utilize that feature.
The primary -script-param
to call is action
, which may be either IMPORT
-- to convert from PNM -- or EXPORT
-- to convert to PNM. The next important parameter is readFile
, which should be assigned a file path. A separate writeFile
path can be specified optionally. If omitted, the writeFile
path will be given the readFile
path with the extension changed. The extension will be aseprite
for IMPORT
or ppm
for EXPORT
.
For example,
aseprite -b -script-param readFile="path\\to\\pnm" -script-param action=IMPORT -script-param colorMode=INDEXED -script "path\\to\\lua"
and
aseprite -b -script-param readFile="path\\to\\img" -script-param writeFile="path\\to\\pnm" -script-param action=EXPORT -script-param writeMode=BINARY -script-param frames=ALL -script-param channelMax=7 -script-param scale=2 -script "path\\to\\lua"
One of the benefits of exporting via the CLI is that multiple frames can be exported. As seen above, the argument all
is used. Alternatively, the string 1:3,7:9
uses a colon :
to specify two ranges from 1 to 3 and from 7 to 9, separated by a comma, ,
.
To modify these scripts, see Aseprite's API Reference. There is also a type definition for use with VS Code and the Lua Language Server extension.
This script was tested in Aseprite version 1.3.2 on Windows 10. Its user interface elements were tested with 100% screen scaling and 200% UI scaling. Please report issues in the issues section on Github. The script was compared with the import-export capabilities of GIMP version 2.10.36 and Krita version 5.2.2.