- Go 100%
| cmd | ||
| docs | ||
| examples/api-usage | ||
| pkg | ||
| .gitignore | ||
| go.mod | ||
| go.sum | ||
| README.md | ||
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:
- examples/api-usage/api-usage.go - Using sdftool as a library
- examples/decode-metadata/decode-metadata.go - Decoding and reading Iris files
Documentation
Detailed documentation is available in the docs/ directory:
- docs/iris.md - Iris image format specification and API
- docs/METADATA.md - Metadata structure and decoding guide
- docs/BINARY_FORMAT.md - Binary glyph format specification
- docs/CPP_DECODER_GUIDE.md - C++ decoder implementation guide
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
- Iris Image Format - The underlying image format
- golang.org/x/image - Go image libraries
- TrueType Font Specification