houmain / spright

An advanced sprite sheet packer and sprite annotator.
GNU General Public License v3.0
52 stars 10 forks source link
bin-packing spright sprite spritesheet-packer template-engine texture-atlas texture-packing texturepacker

spright

Build Issues Downloads | Command line arguments | Installation | Building | Changelog

spright is more than a simple sprite sheet packer, it can be used as one though. Its key ideas are:

While any text editor can be used for writing the input definition, the Visual Studio Code Extension can greatly simplify the process.

Also have a look at the spright test suite to get an impression of the functionality.

Introduction:

Reference:


Simple sheet packing

To pack your sprites into one or more sheets, create an input definition file spright.conf like the following and run spright:

max-width 1024                 # set some constraints for the sheet
max-height 1024
power-of-two true

output "spright{0-}.png"
description "spright.json"
  template "phaser.inja"       # select the template for your game engine

id "{{ source.filenameBase }}"
glob "characters/**/*.png"     # set the location of the images you want to pack
glob "scenery/**/*.png"

The sample creates a sprite sheet spright0.png along the output description spright.json, consumable by the Phaser game engine. Templates for some other game engines are provided and additional templates can be easily written.

See the input definition section for a description of the available options.

Advanced usage example

Say you got some nice sprites you would like to use in your next game:

"Decorations (32x32).png" "Old enemies 2.png" "misc_scenery.png" "OrcAttack/Frame01.png" to
"OrcAttack/Frame04.png"

And your game engine supports efficiently packed sprite sheets like this:

Then you may want to use spright to define the sprites within their original sources and directly pack the sheet from there.

To do so, start by creating a spright.conf containing the essential information:

sheet "sprites"
  padding 1
  output "sheet{0-}.png"

colorkey
crop
crop-pivot

input "Decorations (32x32).png"
  grid 32 32

input "Old enemies 2.png"
  grid 16 16

input "Orc Attack/Frame{01-04}.png"

input "misc_scenery.png"
  atlas

When spright is called without command line arguments, the input definition is read from spright.conf and it writes a spright.json file containing the output description.

Passing the argument -m complete activates the auto-completion, which extends spright.conf with automatically deduced information:

input "Decorations (32x32).png"
  grid 32 32
    sprite
    sprite
    sprite
    sprite
    sprite
  row 1
    sprite
    sprite
    sprite
    sprite
    sprite
...

It detected that Decorations (32x32).png contains five sprites per row and added them below...\ For sheets without grids it adds the sprites' rectangles:

...
input "misc_scenery.png"
  atlas
  sprite
    rect 78 12 6 27
  sprite
    rect 88 12 4 24
  sprite
    rect 97 9 6 34
  sprite
    rect 107 8 7 40
  sprite
    rect 6 17 17 52
...

Now you can complete the definition manually by e.g. giving the sprites IDs and customizing their pivot points:

...
input "Decorations (32x32).png"
  grid 32 32
  pivot left top
    sprite "banner_top"
    sprite "platform_1_left"
    sprite "platform_1_middle"
    sprite "platform_1_right"
    sprite "platform_1_sole"
  row 2
    sprite "banner_middle"
    sprite "platform_2_left"
    sprite "platform_2_middle"
    sprite "platform_2_right"
    sprite "platform_2_sole"
  row 3
    sprite "banner_bottom_1"
...

You can also tag sprites to define animations or other game specific properties and leave sprites you do not want to directly address without ID:

...
input "Old enemies 2.png"
  grid 16 16
    tag anim "blue_enemy_idle"
    sprite
    sprite
    tag anim "blue_enemy_dead"
    sprite
    tag anim "blue_enemy_jump"
    sprite
  row 1
    tag anim "blue_enemy_run"
    sprite
    sprite
    sprite
    sprite
    sprite
    sprite
  row 2
...

See the output template engine section on how to generate a file consumable by your game engine.

Terminology

Since there are quite some terms, an explanation of what they represent in spright might be useful:

Input definition

Indentation is relevant in the hierarchical input definition file. There are two scoping rules:

  1. A definition affects succeeding items on the same or a sub-level.
  2. A definition affects its parent item.

The comments in the following sample should exemplify these:

pivot center middle      # sets default pivot point

input "furniture.png"
  pivot center bottom    # sets pivot point within "furniture.png"
  sprite "chair"
  sprite "lamp"
    pivot center top     # sets pivot point of "lamp"
  sprite "bed"

input "characters.png"
  sprite "player"
  sprite "bird"
    pivot center bottom  # sets pivot point of "bird"
  sprite "wolf"          # "wolf" still gets the default pivot point

Definition reference

The following table contains a list of all definitions, with the item each affects, the expected arguments and a description. Optional arguments are enclosed in square brackets. Unless specified otherwise, optional booleans default to true, numbers default to 1, and an additional value defaults to the first value.

Definition Affects Arguments Description
sheet sprite id Sets the sheet on which the sprites should be packed (default: "spright").
pack sheet pack-method Sets the method, which is used for placing the sprites on the sheet:
- binpack : Tries to reduce the sheet size, while keeping the sprites' trimmed rectangle apart (default).
- compact : Tries to reduce the sheet size, while keeping the sprites' convex outlines apart.
- rows : Layout sprites in simple rows.
- columns : Layout sprites in simple columns.
- single : Put each sprite on its own slice.
- origin : Place all sprites in the top-left corner (use align to position).
- layers : Like origin but also activates layered output of .gif files.
- keep : Keep sprite at same position as in source.
width sheet width Sets a fixed sheet width.
height sheet height Sets a fixed sheet height.
max-width sheet width Sets a maximum sheet width.
max-height sheet height Sets a maximum sheet height.
power-of-two sheet [boolean] Restricts the sheet's size to be a power of two.
square sheet [boolean] Restricts the sheet's size to be square.
divisible-width sheet pixels Restricts the sheet's width to be divisible by a certain number of pixels.
allow-rotate sheet [boolean] Allows to rotate sprites clockwise by 90 degrees for improved packing efficiency.
padding sheet [pixels], [pixels] Sets the space between two sprites / the space between a sprite and the sheets's border.
duplicates sheet dedupe-mode Sets how identical sprites should be processed:
- keep : Disable duplicate detection (default).
- share : Identical sprites should share pixels on the sheet.
- drop : Duplicates should be dropped.
output sheet path Adds a new output file at path to a sheet. It can define a single file or a sequence of files (e.g. "sheet{0-}.png"). See a list of available variables. The file format is deduced from the extension (supported are PNG, GIF, TGA, BMP).
debug output [boolean] Draw sprite boundaries and pivot points on output.
scale output scale,
[scale-filter]
Sets a factor the output should be scaled by, with an optional explicit scale-filter:
- box : A trapezoid with 1-pixel wide ramps.
- triangle : A triangle function (same as bilinear texture filtering).
- cubicspline : A cubic b-spline (gaussian-esque).
- catmullrom : An interpolating cubic spline.
- mitchell : Mitchell-Netrevalli filter with B=1/3, C=1/3.
maps output/input suffix+ Specifies the number of maps and their filename suffixes (e.g. "-diffuse", "-normals", ...). Only the first map is considered when packing, others get identical rects.
alpha output alpha-mode,
[color]
Sets an operation depending on the pixels' alpha values:
- keep : Keep source color and alpha.
- opaque : Makes all pixels opaque.
- clear : Replace fully transparent pixels with the specified color (defaults to black).
- bleed : Set color of fully transparent pixels to their nearest non-fully transparent pixel's color.
- premultiply : Premultiply colors with alpha values.
- colorkey : Replace fully transparent pixels with the specified color and make all others opaque.
glob - pattern Adds all files matching the pattern as inputs (e.g. "sprites/**/*.png").
input - path Adds a new input file at path. It can define a single file or an un-/bounded sequence of files (e.g. "frames{0-}.png", "frames{0001-0013}.png").
path input path A path which should be prepended to the input's path.
colorkey input [color] Specifies that the input has a color, which should be considered transparent (in hex notation e.g. FF00FF).
grid input x, [y] Specifies that the input contains multiple sprites, arranged in a grid of a certain cell size. In this mode the rect of each sprite is deduced from the grid. Each sprite automatically advances the current cell horizontally. One dimension is allowed to be 0, so it is automatically set to the source width/height.
grid-cells input x, y As grid, but specifies the number of cells instead of their size. When the cells are squares, one dimension is allowed to be 0, so it is automatically deduced.
grid-offset input x, [y],
[r-x], [b-y]
Offsets the grid from the top-left corner by a number of pixels. Optionally the grid's offset from the bottom-right corner can also be specified, which is used when deducing the cell-size using grid-cells.
grid-spacing input x, [y] Sets a spacing between the grid cells.
row input row Sets a sprite's row within a grid (starting with 0).
skip input [count] Skips one or more horizontal grid cells or indices in file sequences (a non-numeric parameter is treated as 1, to allow simple substitution of sprite "id").
atlas input [pixels] Specifies that the input contains multiple unaligned sprites, separated by more than a specific number of transparent pixel rows.
max-sprites input count Limits the number of sprites to prevent editors from becoming unresponsive on invalid input (default is 1000).
sprite - [id] Adds a new sprite to an input and optionally sets its id.
id sprite id Sets the sprite's id (defaults to "sprite_{{index}}"). See list of available variables.
span sprite columns, rows Sets the number of grid cells a sprite spans.
rect sprite x, y, width, height Sets a sprite's rectangle in the input sheet.
pivot sprite pivot-x, pivot-y Sets the coordinates of the sprite's pivot point. Optionally the horizontal (left, center, right) and vertical (top, middle, bottom) origin of the coordinates can be set (e.g. 10, 20 / right - 5, top + 3 / bottom left).
trim sprite trim-mode Sets a mode for trimming, which reduces the sprite to the non-transparent region:
- none : Do not trim.
- rect : Trim to rectangular region (default).
- convex : Trim to convex region (vertices are set in output description).
trim-channel sprite channel Sets the channel which should be considered during trimming:
- alpha : The alpha channel of a pixel (default).
- gray : The gray level of the pixel.
trim-threshold sprite value Sets the value which should be considered non-transparent during trimming (1 - 255).
trim-margin sprite [pixels] Sets a number of transparent pixel rows around the sprite, which should not be removed by trimming.
crop sprite [boolean] Sets whether the sprite's rectangle should be reduced to the trimmed bounds.
crop-pivot sprite [boolean] Sets whether the sprite's pivot point should be relative to the trimmed bounds.
extrude sprite [pixels],
[wrap-mode]
Adds a padding around the sprite and fills it depending on the wrap-mode :
- clamp : Clamp to border pixels (default).
- mirror : Mirror border pixels.
- repeat : Repeat border pixels.
min-bounds sprite x, [y] Sets a sprite's minimum bounds, which is the total space it allocates on the sheet. Smaller sprites are surrounded with transparency.
divisible-bounds sprite x, [y] Restricts the sprite's bounds to be divisible by a certain number of pixels.
common-bounds sprite [key] Makes all sprites with identical keys expand to common bounds.
align sprite align-x, align-y Sets the alignment of the sprite within its bounds. The parametrization works as for pivot points.
align-pivot sprite [key] Aligns all sprites with identical keys, so their pivot points have identical offsets within the sprites' bounds.
tag sprite key, [value] Adds a tag to a sprite (value defaults to an empty string). See list of available variables.
data sprite key, value Adds a user defined data entry to a sprite.
description - filename Adds an additional location where the output description should be written. When the filename is a sequence e.g. "slice{0-}.plist" then each slice is output to a separate file.
template description filename Sets the template which should be used for generating the output description.
set - key, value Sets a variable value, which can be accessed in strings and templates using {{key}}. See a list of existing variables.
group - - Can be used for opening a new scope, to limit for example the effect of a tag.

Variables

Beside variables defined by set the following variables can be accessed in string parameters using {{variable}}:

In sprite definitions:

In output definitions:

The variables are substituted before the description is output. Custom transformations can be applied afterwards using the template engine functions.

Output description

By default a JSON file containing all the information about the sprites is written to spright.json. It has the following structure:

{
  "sprites": [
    {
      "id": "sprite_0",
      "index": 0,
      "inputIndex": 0,
      "inputSpriteIndex": 0,
      "pivot": { "x": 8.0, "y": 8.0 },
      "rect": { "x": 0, "y": 0, "w": 16, "h": 16 },
      "trimmedRect": { "x": 0, "y": 0, "w": 16, "h": 16 },
      "rotated": false,
      "sourceIndex": 0,
      "sourceRect": { "x": 0, "y": 0, "w": 16, "h": 16 },
      "trimmedSourceRect": { "x": 0, "y": 0, "w": 16, "h": 16 },
      "sliceIndex": 0,
      "sliceSpriteIndex": 0,
      "data": { "key": "value" },
      "tags": { "key": "value" },
      "vertices": [ 0.0, 0.0,  16.0, 0.0,  16.0, 16.0,  0.0, 16.0 ]
    }
  ],
  "slices": [
    {
      "spriteIndices": [0]
    }
  ],
  "sources": [
    {
      "filename": "dir/source.png",
      "path": "root",
      "width": 256,
      "height": 256,
    }
  ],
  "inputs": [
    {
      "filename": "dir/source.png",
      "sources": [
        {
          "index": 0,
          "spriteIndices": [0]
        }
      ]
    }
  ],
  "tags": {
    "key": {
      "value": [0]
    }
  },
  "textures": [
    {
      "sliceIndex": 0,
      "spriteIndices": [0],
      "path": "root",
      "filename": "dir/spright-0.png",
      "width": 256,
      "height": 256,
      "scale": 1.0,
      "map": ""
    }
  ]
}

This spright.json was generated from the sample above. As you can see, it is very verbose and only intended as an intermediate file, which should be transformed using the template engine.

Output template engine

With the power of the inja template engine it should be possible to transform the output description to a text file consumable by your game engine.\ A template is selected with the -t or --template and the output filename with the -o or --output parameter:

spright -t cpp.inja -o spright.h

This is what a simple template, that transforms the IDs of the output description's sprites into a JavaScript array initialization, looks like:

let sprite_ids = [
{% for sprite in sprites %}
"{{ sprite.id }}",
{% endfor %}
];

For information about the functionality of the template engine, please see the inja reference and the provided templates:

Additional functions

Beside the functions provided by inja the following functions are available:

Templates

The following templates are already available:

Target Template
C++ cpp.inja
Cocos2d-x cocos2dx.inja
Phaser 3 phaser.inja
Phaser 2 phaser2.inja
PixiJS pixijs.inja

Please share templates you come up with in the Discussions section.

The C++ template is just an example for how the sprite description can be directly embedded in code. This spright.h was generated from the sample, using the cpp.inja template.

Command line arguments

Usage: spright [-options]
  -m, --mode <mode>       sets the run mode:
     'update'             the default run mode.
     'rebuild'            regenerate output even when input did not change.
     'describe'           only output description, no texture files.
     'describe-input'     only output description of input, do not pack.
     'complete' [pattern] autocomplete inputs (matching optional pattern).
  -i, --input <file>      input definition file (default: spright.conf).
  -o, --output <file>     output file containing either the output
                     description (default: spright.json) or the
                     autocompleted input definition (defaults to --input).
  -t, --template <file>   template for the output description.
  -p, --path <path>       path to prepend to all output files.
  -v, --verbose           enable verbose messages.
  -h, --help              print this help.

The special identifiers stdin and stdout can be passed to --input and --output to enable console redirection.


Installation

Linux / MacOS

Pre-built packages can be downloaded from the latest release page.

A Homebrew formula is provided for building and installing spright:

brew tap houmain/tap
brew install --HEAD spright

Arch Linux users can install an up to date build from the AUR.

Windows

Pre-built packages can be downloaded from the latest release page.

Most conveniently but possibly not always the very latest version can be installed using a package manager:

# install using winget
winget install spright

# install using Chocolatey
choco install spright

Building

A C++17 conforming compiler is required. A script for the CMake build system is provided.

Installing dependencies on Debian Linux and derivatives:

sudo apt install build-essential git cmake

Checking out the source:

git clone --recurse-submodules https://github.com/houmain/spright

Building:

cd spright
cmake -B build
cmake --build build

License

spright is released under the GNU GPLv3. It comes with absolutely no warranty. Please see LICENSE for license details.

All image files are derived from files released as CC0 on OpenGameArt.