Skip to content

itsmeow/iconforge-rs

Repository files navigation

iconforge

A library for "forging" spritesheets using BYOND's DMI format at the speed of light.

This project was originally part of rust-g, and uses some code from contributors of that project, however, IconForge is my original work. See LICENSE_RUSTG.

Building

Install Rust via rustup.

Create a release build:

cargo build --features spritesheet,gags,ffi --release --target i686-pc-windows-msvc
cargo build --features spritesheet,gags,ffi --release --target i686-unknown-linux-gnu

Build Optimization

IconForge can run up to 10-20% faster when compiled with optimizations for your system, at the cost of compatibility with other/older systems:

set RUSTFLAGS="-C target-cpu=native"
cargo build --release --target i686-pc-windows-msvc
RUSTFLAGS="-C target-cpu=native"
cargo build --release --target i686-unknown-linux-gnu

Public release binaries of IconForge do not use native CPU optimizations, so building it yourself (on your server) is a great way to bump server performance.

Installation

To install IconForge to a BYOND repository, copy iconforge.dm into an early stage of your .dme, so that it can create #defines for later files. Place the remaining files wherever makes sense for your repository.

iconforge.dm function calls will search for a corresponding native library (.dll or .so) in the following locations:

  • In the repository / game root of your BYOND application.
  • In $HOME/.byond/bin/ (only on Linux)

It expects the filename of the native library to be any of the following:

  • Windows: iconforge.dll (or iconforge64.dll, for 64-bit BYOND implementations such as OpenDream)
  • Linux: libiconforge.so (or libiconforge64.so)
  • Linux (Legacy, do not use): iconforge (or iconforge64)

Features

DM API

IconForge includes a DM API, which offers easy functions for direct interaction with the DLL. This includes the Universal Icon system, which is a managed DM library for imitating native icon procs. Simply replace /icon with /datum/universal_icon and change operations to match the alternate syntax (e.g. MapColors to map_colors), then you can use it with any IconForge generator, or even use the native generation viato_icon()

Inline Example

// Define an icon and its transformations without generating it
var/datum/universal_icon/my_universal_icon = uni_icon('icons/my_icon.dmi', "my_icon_state")
my_universal_icon.scale(64, 64)

// Generate & assign icon to an atom
my_atom.icon = my_universal_icon.to_icon_headless("tmp/my_generated_icon.dmi", "my_spritesheet_icon_state")
my_atom.icon_state = "my_spritesheet_icon_state"

Headless Example

The inline function to_icon_headless performs this procedure, with additional error handling:

// Create a mapping of icon_state names -> list-encoded universal icons
var/list/my_spritesheet_icons = list()

// Define an icon without generating it
var/datum/universal_icon/my_universal_icon = uni_icon('icons/my_icon.dmi', "my_icon_state")
my_universal_icon.scale(64, 64)

// Convert it to a list and map it to an output icon state name
my_spritesheet_icons["my_spritesheet_icon_state"] = my_universal_icon.to_list()

// Generate the mapped icons, encoding the mapping as JSON
iconforge_generate_headless("tmp/my_generated_icon.dmi", json_encode(my_spritesheet_icons), FALSE)

// Given that the previous generation was a success (some error checking would be a good idea, see to_icon_headless),
// have BYOND load it as an icon and assign it to the atom
my_atom.icon = icon(file("tmp/my_generated_icon.dmi"))
my_atom.icon_state = "my_spritesheet_icon_state"

Batched Multi-size or Async Generation

If you have a large sheet with multiple sizes of icons and want to efficiently handle them, iconforge_generate and iconforge_generate_async provide the tools to generate them with efficient caching.

The output from hash_icons can be used for iconforge_cache_valid to validate if a given sprites list will create the same result as a previous one by using the hash of all of the input files and the hash of the sprites transformations input.

Take a look at the DM functions for documentation on how to use them properly.

Comparison to native operations

When compared to native BYOND icon manipulation procs, IconForge offers better speed and error handling. BYOND has a habit of silently eating weird operations, and is exceedingly slow - single icon operations can take up to 50ms each. This is unacceptable for any server-generated icons, where maintaining a playable tick rate is paramount.

IconForge speeds these operations up by a factor of 1000, some icon operations are in the microsecond range, with I/O being a primary limiter on speed. This is why IconForge takes a massively parallel, batched approach. Instead of generating one-off icons, several hundred are batched into one operation which can then be executed on multiple threads, collected, and cached efficiently, using IconForge's built-in cache validator.

IconForge also offers near-full parity on most native BYOND operations, ensuring that icons look the same or similar to those generated by the engine.

Operation Parity Table

Parity is covered by tests and checked against BYOND's output as part of CI. This is a table of the standard to which they are tested against.

Feature Transform Define Parity Status Notes
BYOND procs
Scale() ICONFORGE_SCALE ⚠️ Partial Parity Full parity for some types; excluded for odd sizes (e.g., 8x19).
Crop() ICONFORGE_CROP ✅ Full Parity Extensively tested for expansion and shrinking.
Blend() (Icon) ICONFORGE_BLEND_ICON ✅ Full Parity Supports x/y offsets. See Blend Modes below.
Blend() (Color) ICONFORGE_BLEND_COLOR ✅ Full Parity See Blend Modes below.
MapColors() ICONFORGE_MAP_COLORS ✅ Full Parity Supports RGB, RGBA (Hex/Num), and Inversion.
Flip() ICONFORGE_FLIP ✅ Full Parity Tested for all 8 cardinal/ordinal directions.
Turn() ICONFORGE_TURN ⚠️ Partial Parity Parity for 90° increments only.
Shift() ICONFORGE_SHIFT ✅ Full Parity Supports wrapping, no-wrap, and overflows.
SwapColor() ICONFORGE_SWAP_COLOR ✅ Full Parity Handles various alpha/hex combinations.
DrawBox() ICONFORGE_DRAW_BOX ✅ Full Parity Supports coordinate ranges and alpha.
BYOND Icon Blend Modes
ICON_ADD ✅ Full Parity Within +/- 1 bit per pixel.
ICON_SUBTRACT ✅ Full Parity Within +/- 1 bit per pixel.
ICON_MULTIPLY ✅ Full Parity Within +/- 1 bit per pixel.
ICON_OVERLAY ✅ Full Parity Within +/- 1 bit per pixel.
ICON_AND ✅ Full Parity Within +/- 1 bit per pixel.
ICON_OR ✅ Full Parity Within +/- 1 bit per pixel.
ICON_UNDERLAY ✅ Full Parity Within +/- 1 bit per pixel.
Other Features
GAGS API ✅ Full Parity Supported via GAGS ColorMatrix layer type.
Mixed-Dir Blending ⚠️ Partial Parity 4/8 onto single dir or single dir onto 4/8 only.
Mixed-Frame Blending ⚠️ Partial Parity Multi-frame icons onto single frame only.
Movement States ❌ Not Implemented
Greyscale/Non-RGBA ❌ Not Implemented IconForge will only output RGBA icons.

Notable Differences

Missing Icon States

BYOND will generally silently eat nonexistent icon states when blending icons, using the "" (empty string) icon state as a default if it exists. IconForge will instead return an error, providing generally improved developer UX, as this is not typically wanted behavior and there is very little visibility into it.

Dir Mixing

IconForge does not allow specific blend operations between icon states that have a different number of directions. There is special handling for when an icon has only one direction and is blended onto one with multiple - this behavior will match BYOND as it is used occasionally and makes sense to the user.

There is a similar handling for blending a multiple-dir icon onto a single-dir icon, it will always use SOUTH from the upper icon, as this is also generally expected.

However, blending an icon with NORTH/EAST/SOUTH/WEST onto an icon NORTH/EAST/NORTHEAST/etc. is typically unwanted behavior, and IconForge will error in this case. The universal_icon API accepts dir restrictions, which can define a single dir to be blended instead.

Frame Mixing

The same difference exists for icon states with different numbers of frames - there is special handling for blending an icon with multiple frames onto one with only one frame (by adding copies of the first frame), because this is expected (and used) behavior to animate an icon with an overlay. IconForge will add new delays entries with a value of 1, the same as BYOND.

Crop & DrawBox Coordinate Ordering

If a crop or DrawBox operation places a larger bound before a smaller bound, IconForge will return an error. This is just a matter of ordering the coordinates by size (e.g. (x1=64, y1=32, x2=32, y2=64) should be (x1=32, y1=32, x2=64, y2=64)) as it is clearer to the developer and the result is identical.

Diagonal (non-cardinal) Flip() on a non-square icon

Did you know you can Flip(NORTHEAST)? Now you do! In BYOND, performing a flip on an icon that is not square provides unpredictable behavior. IconForge will error. This is because Flip(NORTHEAST) both flips AND rotates the image, and rotating an image of a non-square size will change its dimensions. This will cause the output state to have a different dimension than the rest of the images, which is generally a problem outside of special spritesheet handling.

Invalid colors

Providing a non-hex color to BYOND generally causes internal runtimes or the resulting icon to use white instead. IconForge will return decode errors for hex strings that are invalid.

About

IconForge, a library for forging spritesheets using BYOND's DMI format at the speed of light

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors