1
0
Fork 0
Tool to convert a TTF file to binary SDF format.
Find a file
2026-01-06 15:40:56 -06:00
cmd add readme, improve sdftool massively 2026-01-06 15:38:43 -06:00
docs reorganize repo, bring in improvements from msdfgen 2026-01-06 13:53:52 -06:00
examples/api-usage add readme, improve sdftool massively 2026-01-06 15:38:43 -06:00
pkg add readme, improve sdftool massively 2026-01-06 15:38:43 -06:00
.gitignore reorganize repo, bring in improvements from msdfgen 2026-01-06 13:53:52 -06:00
go.mod sdftool version 1 2026-01-05 12:42:27 -06:00
go.sum sdftool version 1 2026-01-05 12:42:27 -06:00
README.md fix readme 2026-01-06 15:40:56 -06:00

sdftool

A high-performance tool for converting TrueType fonts to Signed Distance Field (SDF) atlases in the Iris image format. Optimized for game engines and real-time rendering applications.

Features

  • Fast SDF Generation: Convert TrueType fonts to high-quality signed distance fields
  • Dual Algorithms: Choose between fast rasterization or high-quality perpendicular distance
  • Compact Binary Format: Efficient glyph and kerning data storage (52% smaller than JSON)
  • Iris Image Format: Custom format with LZ4 compression (2+ GB/s decompression)
  • Parallel Processing: Multi-threaded SDF generation for maximum performance
  • Full Font Metrics: Preserves kerning, advance widths, and font metrics
  • Easy Integration: Both CLI tool and Go library API

Quick Start

Installation

# Clone the repository
git clone https://git.sharkk.net/everquest/sdftool
cd sdftool

# Build the tool
go build -o sdftool ./cmd/sdftool

# Optional: Build inspection tool
go build -o iris-inspect ./cmd/iris-inspect

Basic Usage

# Convert a font to SDF atlas (default: 32px glyphs, 4px range)
sdftool font.ttf

# Custom glyph size and SDF range
sdftool font.ttf -s 64 -r 8.0

# Specify output location
sdftool font.ttf -o output.iris

# Generate preview PNG for debugging
sdftool font.ttf -p

# Use high-quality perpendicular distance algorithm
sdftool font.ttf --vector

# Process multiple fonts
sdftool *.ttf

Command-Line Options

Option Description Default
-o, --output Output directory or file Same as input
-s, --size Glyph size in pixels 32
-r, --range SDF spread/range in pixels 4.0
-a, --atlas Atlas dimensions (e.g., 512x512) Auto-calculated
-p, --preview Generate preview PNG false
--chars Character set (currently only 'ascii') ascii
--vector Use perpendicular distance (better quality, slower) false
-v, --verbose Show detailed metadata false
--verify Verify metadata after encoding false

Using as a Go Library

Installation

go get git.sharkk.net/everquest/sdftool

Example: Generate SDF Atlas

package main

import (
    "fmt"
    "log"
    "sdftool/pkg/sdftool"
)

func main() {
    // Create options
    opts := &sdftool.Options{
        GlyphSize: 32,
        SDFRange:  4.0,
        CharSet:   nil, // Use default ASCII (32-126)
        Verify:    true,
    }

    // Create processor
    processor := sdftool.NewProcessor(opts)

    // Process font to file
    if err := processor.ProcessFontToFile("font.ttf", "output.iris"); err != nil {
        log.Fatal(err)
    }

    fmt.Println("Success!")
}

See examples/api-usage/api-usage.go for more details.

Decoding Iris Files

Reading Metadata

package main

import (
    "fmt"
    "os"
    "git.sharkk.net/go/iris"
)

func main() {
    // Open the iris file
    file, _ := os.Open("font.iris")
    defer file.Close()

    // Decode
    imgInterface, _ := iris.Decode(file)
    img := imgInterface.(*iris.Image)

    // Access metadata
    metadata := img.Metadata()
    fmt.Println("SDF Range:", metadata["sdf_range"])
    fmt.Println("Glyph Size:", metadata["glyph_size"])
    fmt.Println("Font Name:", metadata["font_name"])
    fmt.Println("Format:", metadata["format"])
}

Decoding Glyph Data

The glyph data is stored in a compact binary format for optimal performance:

package main

import (
    "fmt"
    "os"
    "git.sharkk.net/go/iris"
    "sdftool/pkg/output"
)

func main() {
    // Open and decode iris file
    file, _ := os.Open("font.iris")
    defer file.Close()

    imgInterface, _ := iris.Decode(file)
    img := imgInterface.(*iris.Image)
    metadata := img.Metadata()

    // Decode binary glyph data
    glyphs, err := output.DecodeGlyphsBinary(metadata["glyphs"])
    if err != nil {
        panic(err)
    }

    // Use glyph data
    for r, g := range glyphs {
        fmt.Printf("'%c': pos=(%d,%d) size=(%dx%d) advance=%d\n",
            r, g.X, g.Y, g.Width, g.Height, g.AdvanceX)
    }

    // Access the atlas texture
    atlasPixels := img.Pix() // []byte - grayscale SDF data
    width, height := img.Bounds().Dx(), img.Bounds().Dy()

    // Upload to GPU...
}

See examples/decode-metadata/decode-metadata.go for a complete example.

Binary Glyph Format (GLYF)

Each glyph entry contains (32 bytes total):

Field Type Description
Rune uint32 Unicode codepoint
X, Y int32 Position in atlas texture
Width, Height int32 Glyph dimensions
OffsetX, OffsetY int32 Rendering offsets
AdvanceX int32 Horizontal advance

All values are in pixels (scaled to the glyph_size). The binary data is base64-encoded and stored in the glyphs metadata field. See docs/BINARY_FORMAT.md for details.

The Iris Image Format

Iris is a minimal, high-performance image format optimized for game engines:

  • Fast: LZ4 decompression at 2+ GB/s
  • Simple: 20-byte header, predictable layout
  • Compact: Excellent compression for SDF textures
  • Flexible: Supports L, LA, RGB, RGBA in 8-bit or 16-bit

Quick Example

import "git.sharkk.net/go/iris"

// Decode (standard library compatible)
file, _ := os.Open("texture.iris")
img, _, _ := image.Decode(file) // Returns image.Image

// Fast decode (zero-copy)
pixels, width, height, format, _ := iris.DecodeFast(file)

// Upload directly to GPU
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, width, height,
    0, gl.ALPHA, gl.UNSIGNED_BYTE, pixels)

See docs/iris.md for complete documentation.

Performance

Binary Format Benefits

For a typical font with 95 ASCII glyphs:

Metric JSON Format Binary Format Improvement
Metadata Size 8,468 bytes 4,072 bytes 52% smaller
Parse Time ~35-40 µs ~20 µs 2× faster
Throughput ~28,000/sec ~48,500/sec 73% faster

SDF Generation Modes

Mode Quality Speed Use Case
Rasterization (default) Good Fast Most fonts, production builds
Perpendicular (--vector) Excellent Slower High-quality fonts, hero text

Metadata Structure

Every .iris file generated by sdftool contains:

Key Type Description
sdf_range float Distance field range (e.g., "4.0")
glyph_size int Glyph size in pixels (e.g., "32")
font_name string Source font filename
format string "binary" or "json"
glyphs base64 Binary glyph positioning data
kerning base64 Binary kerning pairs
metrics JSON Font metrics (ascent, descent, etc.)
atlas_width int Atlas texture width
atlas_height int Atlas texture height
atlas_utilization string Percentage of atlas used

Inspecting Generated Files

Use the included inspection tool to verify files:

# Basic inspection
./iris-inspect font.iris

# Verbose output (shows decoded glyphs)
./iris-inspect -verbose font.iris

# JSON output
./iris-inspect -json font.iris

Output example:

File: SF-Pro.iris
Format: L (Grayscale), 8-bit, LZ4 compressed
Dimensions: 512x192
Metadata entries: 10
  sdf_range: 4.0
  glyph_size: 32
  font_name: SF-Pro.ttf
  format: binary
  atlas_utilization: 99.0%

Examples

The repository includes several examples:

Documentation

Detailed documentation is available in the docs/ directory:

Use Cases

Game Engines

// Load atlas texture
IrisImage atlas = IrisLoader::Load("font.iris");
GLuint textureId = UploadToGPU(atlas.pixels, atlas.width, atlas.height);

// Decode glyph data
auto glyphs = DecodeGlyphsBinary(atlas.metadata["glyphs"]);

// Render text
for (char c : "Hello") {
    GlyphData& g = glyphs[c];
    // Use g.X, g.Y, g.Width, g.Height for UV coords
    // Use g.AdvanceX for cursor advancement
}

Real-Time Applications

  • UI Systems: Fast text rendering with crisp edges at any scale
  • Mobile Games: Compact file size, fast load times
  • 3D Text: Scale-independent text rendering
  • VR/AR: High-quality text without resolution loss

Technical Details

What is SDF?

Signed Distance Field (SDF) stores the distance to the nearest edge at each pixel:

  • Inside the glyph: Positive values (> 128)
  • Outside the glyph: Negative values (< 128)
  • On the edge: Value of 128

This allows:

  • Sharp rendering at any scale
  • Outline effects
  • Drop shadows and glows
  • Smooth anti-aliasing

SDF Range

The sdf_range parameter controls quality vs. file size:

  • 4.0 pixels (default): Good quality, compact
  • 8.0 pixels: Excellent quality, larger effects
  • 2.0 pixels: Small size, limited effects

Character Sets

Currently supports ASCII (codepoints 32-126). Future versions will support:

  • Custom character ranges
  • Unicode blocks
  • Font subsetting

Building from Source

# Install dependencies
go mod download

# Build CLI tool
go build -o sdftool ./cmd/sdftool

# Build inspection tool
go build -o iris-inspect ./cmd/iris-inspect

# Run tests
go test ./...

Contributing

Contributions are welcome! Please ensure:

  • Code follows existing style
  • Tests pass
  • Documentation is updated

License

This project follows the Sharkk Medium License (see Iris specification). Key points:

  • Give credit to original authors
  • Share-Alike with same license
  • Non-commercial use only
  • Source code must be shared

Credits

  • SDF generation techniques inspired by msdfgen
  • Iris image format: Custom high-performance format
  • Font parsing: Go's golang.org/x/image/font/sfnt

Support

For questions, issues, or feature requests, please open an issue on the repository.

See Also