No description
Find a file
2026-04-08 11:54:28 -07:00
src Initialize repository. 2026-04-08 11:54:28 -07:00
.gitignore Initialize repository. 2026-04-08 11:54:28 -07:00
Cargo.lock Initialize repository. 2026-04-08 11:54:28 -07:00
Cargo.toml Initialize repository. 2026-04-08 11:54:28 -07:00
README.md Initialize repository. 2026-04-08 11:54:28 -07:00

Vector Graphics Format (VGF) Specification

Version: 0.1 (Draft)
Status: Pre-implementation design specification


Table of Contents

  1. Overview
  2. Design Principles
  3. Primitive Types
  4. File Structure
  5. Feature Flags
  6. Dictionary
  7. Palette Table
  8. Pattern Table
  9. Component Table
  10. Rig Table
  11. Scene Table
  12. Animation
  13. Coordinate Spaces
  14. Rendering Model
  15. Field Encoding
  16. Open Questions

1. Overview

VGF is a structured binary vector graphics format targeting icon-scale assets in web and desktop applications. It is designed around three priorities:

  • Bounded rendering cost. Every feature has a statically computable upper bound on rendering work, determined entirely from the file's declared parameters. No feature may introduce unbounded iteration, recursion, or evaluation cost.
  • Structural intent. The format encodes what an image is (shapes, constraints, animation hints) rather than how to draw it (procedural drawing commands). Renderers have latitude in how they satisfy the structure.
  • Shared assets. Components, rigs, palettes, and patterns are defined in global tables and referenced by index. Multiple scenes within a file share these tables, enabling compact representation of icon sets, glyph libraries, and themed asset collections.

VGF explicitly does not aim to replace general-purpose vector formats (SVG) or animation runtimes (game engines). Scripting, arbitrary filters, and DOM-style programmability are out of scope.


2. Design Principles

2.1 Bounded Cost

The total rendering work for any VGF file is bounded by a function of its declared table sizes, scene instance counts, component shape counts, and pattern bytecode lengths. A renderer may compute this bound before decoding any geometry and reject files that exceed its policy limits.

2.2 No External Dependencies

VGF files are self-contained. Text is represented as shaped components (glyph outlines stored as shapes, characters instanced via the rig system). No font loading, network access, or external resource resolution is required or permitted.

2.3 Declarative Animation

Animation is expressed as parameter tracks: time-varying scalar or vector values that drive rig transforms and material blends. There are no conditionals, callbacks, or general computation in animation data. Application-bound parameters are advisory hints; a renderer that does not support a given hint uses the parameter's declared default.

2.4 Best-Effort Rendering

VGF does not define mandatory feature support levels. A renderer encountering an unknown feature flag or unsupported constraint type should render what it can and skip what it cannot. Files should not rely on any specific renderer behaviour beyond the core 2D path and palette system.

2.5 Separation of Definition and Placement

Components define geometry. Rigs define how components are transformed and constrained. Scenes place rig instances into viewports. These three layers are strictly separated: a component cannot reference a rig, and a rig definition cannot reference a scene.


3. Primitive Types

All multi-byte integers are little-endian. No implicit alignment padding appears in the file format; fields are packed sequentially.

Type Size Description
u8 1 byte Unsigned 8-bit integer
u16 2 bytes Unsigned 16-bit integer
u32 4 bytes Unsigned 32-bit integer
i8 1 byte Signed 8-bit integer
i16 2 bytes Signed 16-bit integer
f16 2 bytes IEEE 754 binary16 half-precision float
f32 4 bytes IEEE 754 binary32 single-precision float
bool 1 byte 0x00 = false, 0x01 = true; other values reserved

3.1 Variable-Width Coordinate

Within component path data, coordinate values are unsigned integers whose width in bytes is determined by the component's declared grid dimensions:

coord_bytes = ceil(ceil(log2(max(grid_w, grid_h) + 1)) / 8)

This yields 1 byte for grids up to 255 units, 2 bytes for grids up to 65535 units. The width is uniform across all path data within a single component and is computed once from the component header.

3.2 Index Encoding with Implicit Default

Table indices throughout the format use a 1-based offset convention: a stored value of 0 means "not present / use default" and stored value N refers to table entry N - 1. This allows optional fields to encode absence as zero without a separate presence flag.

The bit width of an index field is determined by the size of the table it references, rounded up to the nearest byte:

index_bits = ceil(log2(table_size + 1))   // +1 for the implicit default (0)
index_bytes = ceil(index_bits / 8)

A file with 200 palette entries uses 1-byte palette indices (fits in 8 bits with room for 0). A file with 300 palette entries uses 2-byte indices.


4. File Structure

[File Header]
[Table Directory]
  → Component Table offset + size
  → Rig Table offset + size
  → Palette Table offset + size
  → Pattern Table offset + size
  → Dictionary offset + size  (0 if absent)
[Scene Directory]
  → Scene count
  → [Scene offset + size] × scene_count
[Table Data]
  [Palette Table]
  [Pattern Table]
  [Component Table]
  [Rig Table]
[Scene Data]
  [Scene 0]
  [Scene 1]
  ...

Offsets in the table and scene directories are absolute byte offsets from the start of the file. A renderer may seek to any table or scene without parsing preceding data.

4.1 File Header

Field Type Description
magic [4]u8 0x56 0x47 0x46 0x00 ("VGF\0")
version u8 Format version; currently 1
feature_flags u32 See §5
dir_offset u32 Byte offset to Table Directory

4.2 Table Directory

Field Type Description
palette_offset u32 Byte offset to Palette Table
palette_size u32 Byte length of Palette Table
pattern_offset u32 Byte offset to Pattern Table (0 if absent)
pattern_size u32 Byte length of Pattern Table
component_offset u32 Byte offset to Component Table
component_size u32 Byte length of Component Table
rig_offset u32 Byte offset to Rig Table
rig_size u32 Byte length of Rig Table
dict_offset u32 Byte offset to Dictionary (0 if absent)
dict_size u32 Byte length of Dictionary

4.3 Scene Directory

Immediately following the Table Directory:

Field Type Description
scene_count u16 Number of scenes in this file
scenes [scene_count]SceneEntry Array of scene directory entries

SceneEntry:

Field Type Description
offset u32 Byte offset to scene data
size u32 Byte length of scene data

5. Feature Flags

A u32 bitfield in the file header. Renderers should attempt best-effort rendering when encountering set bits they do not support.

Bit Name Description
0 ANIMATION File contains animation tracks (§12)
1 APP_PARAMS Rigs declare application-bound parameters
2 PATTERNS Pattern Table is present (§8)
3 F16_PALETTE Palette contains F16×4 entries
4 GRADIENTS Palette contains gradient entries
5 MULTI_SCENE File contains more than one scene
6 DICTIONARY Dictionary is present (§6)
7 3D_COORDS Reserved: 3D coordinate extension (not defined in this version)
815 Reserved; must be zero in version 1
1631 Vendor/experimental; renderers must ignore

6. Dictionary

The dictionary is an optional table of UTF-8 string entries. Every named element in the file (components, rigs, scenes, parameters, palette entries) stores a dictionary index rather than an inline string. Stripping the dictionary produces a valid file; all references become opaque indices.

6.1 Dictionary Structure

Field Type Description
entry_count u16 Number of string entries
entries [entry_count]DictEntry

DictEntry:

Field Type Description
length u16 Byte length of the UTF-8 string
data [length]u8 UTF-8 encoded string, not null-terminated

Dictionary index 0 means "unnamed." Dictionary indices stored elsewhere in the file use the 1-based convention (§3.2).


7. Palette Table

The palette is a flat array of color entries. Entry count is recorded in the palette table header. All palette indices elsewhere in the file use the 1-based convention; index 0 means "transparent / no paint."

7.1 Palette Table Header

Field Type Description
entry_count u16 Number of palette entries

Immediately followed by entry_count palette entries.

7.2 Palette Entry

Each entry begins with a 1-byte type tag:

Tag Format Description
0 RGBA32 8 bits per channel, sRGB + linear alpha
1 F16×4 Half-float per channel, linear; requires F16_PALETTE flag
2 Linear Gradient Two palette indices + angle
3 Radial Gradient Two palette indices + center + radius
4 Pattern Ref Index into Pattern Table

RGBA32 entry (5 bytes total):

Field Type Description
tag u8 0
r u8 Red channel
g u8 Green channel
b u8 Blue channel
a u8 Alpha channel

F16×4 entry (9 bytes total):

Field Type Description
tag u8 1
r f16 Red channel
g f16 Green channel
b f16 Blue channel
a f16 Alpha channel

Linear Gradient entry (6 bytes total):

Field Type Description
tag u8 2
color_a palette index From-color (1-based, must be RGBA32 or F16×4)
color_b palette index To-color (1-based, must be RGBA32 or F16×4)
angle f16 Gradient angle in radians

Palette index width is determined by palette entry count per §3.2.

Radial Gradient entry:

Field Type Description
tag u8 3
color_a palette index Inner color
color_b palette index Outer color
cx f32 Center X in normalised scene space (§13)
cy f32 Center Y in normalised scene space
radius f32 Radius in normalised scene space

Pattern Ref entry:

Field Type Description
tag u8 4
pattern_idx pattern index 1-based index into Pattern Table

8. Pattern Table

Patterns are mathematical fill functions evaluated per pixel. Each pattern is a small stack-based bytecode program that maps (x, y) coordinates (in component-local normalised space) to a palette index.

8.1 Pattern Table Header

Field Type Description
entry_count u16 Number of pattern entries

8.2 Pattern Entry

Field Type Description
color_count u8 Number of palette indices in the color set
colors [color_count] palette index Palette entries available to the program
instr_count u16 Number of bytecode instructions
instructions [instr_count]u8 Bytecode (see §8.3)

color_count must be ≥ 1 and ≤ 255. instr_count must be ≤ 256. These limits bound evaluation cost to a fixed maximum per pixel.

8.3 Pattern Bytecode

The evaluator maintains a stack of f32 values. Stack depth must not exceed 16 at any point during execution; a file violating this limit is malformed. Execution always terminates (no jump or loop instructions exist).

The final value on the stack is clamped to [0, color_count - 1], truncated to an integer, and used as an index into the pattern's color set.

Opcode Byte Stack effect Description
PUSH_X 0x01 → x Push current X coordinate
PUSH_Y 0x02 → y Push current Y coordinate
PUSH_F32 0x03 → v Push literal f32; next 4 bytes are the value
ADD 0x10 a b → a+b Add
SUB 0x11 a b → a-b Subtract
MUL 0x12 a b → a*b Multiply
DIV 0x13 a b → a/b Divide; result is 0 if b=0
MOD 0x14 a b → a mod b Modulo; result is 0 if b=0
NEG 0x15 a → -a Negate
ABS 0x20 `a → a
FLOOR 0x21 a → floor(a) Floor
FRACT 0x22 a → fract(a) Fractional part (a - floor(a))
SQRT 0x23 a → sqrt(a) Square root; result is 0 if a < 0
SIN 0x24 a → sin(a) Sine (radians)
COS 0x25 a → cos(a) Cosine (radians)
MIN 0x26 a b → min(a,b) Minimum
MAX 0x27 a b → max(a,b) Maximum
LT 0x30 a b → a<b 1.0 if a < b, else 0.0
GT 0x30 a b → a>b 1.0 if a > b, else 0.0
EQ 0x32 a b → a==b 1.0 if a == b (exact), else 0.0
SELECT 0x40 cond a b → r r = a if cond ≥ 0.5, else b. No branching.
DUP 0x50 a → a a Duplicate top of stack
SWAP 0x51 a b → b a Swap top two values
POP 0x52 a → Discard top of stack

8.4 Material Resolution for Patterns

When a pattern palette entry is used as a shape's material, the renderer evaluates the pattern bytecode at each pixel using that pixel's coordinates in component-local normalised space (§13.1). The result selects a palette entry from the pattern's color set. That entry must itself be a plain color (RGBA32 or F16×4); nested pattern references are not permitted.


9. Component Table

Components are the atomic geometry units of VGF. Each component defines a grid, a set of shapes (closed paths with materials), and optional named anchor points used by the rig system.

9.1 Component Table Header

Field Type Description
entry_count u16 Number of component entries

9.2 Component Entry Header

Field Type Description
dict_name dict index Optional name (0 = unnamed)
grid_w u16 Grid width in integer units
grid_h u16 Grid height in integer units
shape_count u16 Number of shapes in this component
anchor_count u8 Number of named anchor points
byte_size u32 Total byte length of this component entry

Followed by shape_count shape entries, then anchor_count anchor entries.

coord_bytes for this component is derived from max(grid_w, grid_h) per §3.1.

9.3 Shape Entry

Each shape is a closed path with a material.

Field Type Description
material Material Variable-length material field (see §9.4)
winding u8 Fill rule: 0 = non-zero, 1 = even-odd
segment_count u16 Number of path segments
segments [segment_count] Segment Path segments (see §9.5)

Shapes within a component are rendered back-to-front in file order (first shape is furthest back).

9.4 Material Field

The material field uses a variable-size tag-based encoding. The first byte is a field presence bitfield:

Bit Field Description
0 MODE Blend mode present; else Normal
1 PATTERN Pattern override present
2 COLOR One or more color overrides present
37 Reserved

Fields are encoded in bit order (lowest bit first) immediately after the presence byte. Absent fields use their implicit defaults (mode = Normal, pattern = none, color = palette index 0 = transparent).

Mode field (u8, present if bit 0 set):

Value Mode Description
0 Normal Standard alpha compositing
1 Background Render behind existing content
2 Intersect Clip to intersection with layer below
3 Subtract Cut from layer below
4 Lighten Max of source and destination
5 Multiply Multiply source and destination
6 Clip Affect mask stack, not color buffer

Pattern field (pattern index, present if bit 1 set):

A 1-based index into the Pattern Table. If present, the pattern provides the base material; the color set below may override the pattern's declared colors.

Color field (present if bit 2 set):

Field Type Description
color_count u8 Number of palette indices following
colors [color_count] palette index Color overrides

When a pattern is also present, these colors replace the pattern's declared color set (same indexing). When no pattern is present, color_count must be 1 and the single entry is the shape's fill color.

9.5 Path Segments

Each segment begins with a 1-byte tag:

Tag Type Additional data
0 Line end: [coord_bytes × 2] — endpoint
1 Quadratic ctrl: [coord_bytes × 2], end: [coord_bytes × 2]
2 Cubic ctrl1: [coord_bytes × 2], ctrl2: [coord_bytes × 2], end: [coord_bytes × 2]
3 Arc rx, ry: [coord_bytes] each, x_rot: f16, flags: u8, end: [coord_bytes × 2]

Coordinates are unsigned integers in the component's local grid space. The path implicitly begins at the endpoint of the previous segment; the first segment begins at the endpoint of the last segment (closing the path).

Arc flags byte: bit 0 = large-arc, bit 1 = sweep direction (matches SVG arc flag semantics).

9.6 Anchor Points

Anchors are named points within a component's grid space, used as attachment targets for rig constraints.

Field Type Description
dict_name dict index Name of this anchor
x [coord_bytes] X coordinate in grid space
y [coord_bytes] Y coordinate in grid space

A component always has an implicit anchor center at (grid_w / 2, grid_h / 2) even if no anchors are declared.


10. Rig Table

Rigs bind components to a transform skeleton. A rig definition specifies which component it uses, a rest transform, optional constraints, optional parameter declarations, and optional material overrides.

10.1 Rig Table Header

Field Type Description
entry_count u16 Number of rig entries

10.2 Rig Entry Header

Field Type Description
dict_name dict index Optional name
component_idx comp index 1-based index into Component Table
parent_rig_idx rig index 1-based index into Rig Table; 0 = no parent
rest_translation [f32; 2] Rest X, Y in parent space (or scene space if no parent)
rest_rotation f32 Rest rotation in radians
rest_scale f16 Rest scale relative to parent/scene minimum dimension
origin_offset [f16; 2] Local origin offset for rotation/scale, in component grid fractions
constraint_count u8 Number of constraints
param_count u8 Number of declared parameters
material_count u8 Number of material overrides
byte_size u32 Total byte length of this rig entry

Followed by constraint entries, then parameter declarations, then material overrides.

Rig entries may not form cycles through parent_rig_idx. A renderer must validate the parent chain is acyclic at load time.

10.3 Constraints

Each constraint begins with a 1-byte type tag:

Axis Lock — restrict movement to a line:

Field Type Description
tag u8 0
angle f32 Axis angle in radians

Distance — maintain distance to another rig's anchor:

Field Type Description
tag u8 1
target_rig rig index 1-based rig index
target_anchor u8 Anchor index in target rig's component; 0 = center
distance f32 Distance in scene space

Look-At — orient toward another rig's anchor (affects rotation only):

Field Type Description
tag u8 2
target_rig rig index 1-based rig index
target_anchor u8 Anchor index in target rig's component; 0 = center

Rotation Joint — constrain rotation to a range:

Field Type Description
tag u8 3
min_rot f32 Minimum rotation in radians (parent-relative)
max_rot f32 Maximum rotation in radians (parent-relative)

Translation Path — constrain translation to follow a parametric path:

Field Type Description
tag u8 4
segment_count u8 Number of path segments
segments [segment_count] TranslationSegment Scene-space path

Each TranslationSegment is a cubic Bézier: [f32; 2] × 4 (start, ctrl1, ctrl2, end) in scene space. The rig's position on the path is driven by a scalar parameter in [0, 1].

10.4 Parameter Declarations

Parameters are scalar or vector values that can be driven by animation tracks or application bindings.

Field Type Description
dict_name dict index Parameter name
param_type u8 0 = scalar f32, 1 = Vec2 (f32×2), 2 = angle f32
default_value f32 or f32×2 Default value(s)
min_value same type Hard minimum (enforced by renderer)
max_value same type Hard maximum (enforced by renderer)
source_hint u8 Advisory source (see below)

Source hints (advisory; renderers may ignore):

Value Hint Suggested binding
0 None No hint; use default or animation track
1 Timer Seconds elapsed since scene start
2 CursorPos Normalised cursor position in scene viewport
3 CursorAngle Angle from scene center to cursor, in radians
4 ScrollOffset Application scroll offset, normalised
5255 Reserved

10.5 Material Overrides

Material overrides allow a rig instance to substitute palette entries for specific shapes within its component.

Field Type Description
shape_idx u8 0-based index of shape within the component; 255 = all shapes
color_slot u8 Which color slot in the shape's material to override; 255 = all
palette_idx palette index Replacement palette entry (1-based)

11. Scene Table

Scenes are viewports containing a list of rig instances. They are the top-level renderable units of a VGF file.

11.1 Scene Entry Header

Field Type Description
dict_name dict index Optional name
bg_palette_idx palette index Background fill; 0 = transparent
instance_count u16 Number of rig instances in this scene
anim_count u16 Number of scene-level animation tracks
byte_size u32 Total byte length of this scene entry

Followed by instance_count rig instance entries, then anim_count animation track entries (§12).

11.2 Rig Instance

Field Type Description
rig_idx rig index 1-based index into Rig Table
translation [f32; 2] Scene-space translation, overrides rig rest
rotation f32 Scene-space rotation in radians, overrides rig rest
scale f16 Scale override; 0 = use rig rest scale
param_count u8 Number of parameter value overrides
params [param_count] InstanceParam Parameter value overrides
mat_override_count u8 Number of material overrides
mat_overrides [mat_override_count] MaterialOverride Per-instance material overrides

Rig instances within a scene are rendered back-to-front in file order.

InstanceParam:

Field Type Description
param_idx u8 0-based index of parameter in rig's parameter list
value f32 or f32×2 Value to use (width determined by param type)

MaterialOverride has the same structure as §10.5.


12. Animation

Animation is expressed as keyframe tracks on parameters. A track binds to a specific parameter (either on a rig or on a scene-level rig instance) and provides a sequence of timed keyframes.

Animation appears in two contexts:

  • Rig-level animation: defined inside a Rig entry, animates that rig's own declared parameters.
  • Scene-level animation: defined inside a Scene entry, animates the parameters of specific rig instances in that scene.

12.1 Animation Track

Field Type Description
target AnimTarget What this track animates (see below)
loop u8 0 = hold last keyframe, 1 = loop, 2 = ping-pong
keyframe_count u16 Number of keyframes
keyframes [keyframe_count] Keyframe

AnimTarget (2 bytes):

Field Type Description
target_type u8 0 = rig parameter, 1 = material blend parameter, 2 = scene instance parameter
target_idx u8 Parameter index within the target

12.2 Keyframe

Field Type Description
time_ms u32 Time in milliseconds from track start
value f32 or f32×2 Keyframe value (width determined by parameter type)
interp u8 Interpolation to next keyframe: 0=step, 1=linear, 2=ease-in-out

12.3 Material Blend Animation

When target_type is 1 (material blend), the target parameter is a scalar [0, 1] blend factor. At render time the renderer resolves both the from and to materials per pixel (evaluating patterns and gradients as needed) and linearly interpolates the resulting linear-space RGBA values using this factor.

MaterialBlendTarget (replaces the target_idx byte for type 1):

Field Type Description
shape_idx u8 Shape within the component; 255 = all
color_slot u8 Color slot; 255 = all
from_palette palette index From-material
to_palette palette index To-material

Both palette entries are resolved per pixel. The blend factor drives a linear interpolation in linear RGB space. Palette entry types (RGBA32, F16, gradient, pattern) are resolved independently; the result of each resolution is a linear RGBA value at that pixel before blending.


13. Coordinate Spaces

VGF uses three coordinate spaces, each strictly layered.

13.1 Component Grid Space

Integer coordinates, origin at top-left, Y increasing downward. Range [0, grid_w] × [0, grid_h]. Used exclusively for path segment data within component definitions. The implicit center of a component is (grid_w / 2, grid_h / 2).

13.2 Scene Space (Normalised)

Float coordinates used for rig placement and scene-level values (gradient centers, path constraints). Origin at the center of the viewport. Axes are scaled so that 1.0 equals half the viewport's minimum dimension (i.e., min(viewport_width_px, viewport_height_px) / 2).

Examples for a 200×100 px viewport (minimum dimension = 100, half = 50):

  • (0, 0) — viewport center
  • (1, 0) — 50px right of center
  • (-1, 0) — 50px left of center
  • (0, 1) — 50px below center
  • (0, -1) — 50px above center
  • (2, 1) — right edge, bottom edge (for this aspect ratio)

A rig at (0, 0) with scale 1.0 fills the viewport's minimum dimension exactly (the component's grid maps to [-1, 1] on both axes before the rig's own scale and origin offset are applied).

13.3 Component Local Normalised Space

Used by pattern bytecode and per-pixel evaluation. Float coordinates mapping the component grid to [0, 1] × [0, 1], origin at top-left. Pattern programs receive (x, y) in this space.

13.4 Transform Evaluation Order

For a rig instance at render time:

  1. Start with parent rig's resolved transform (or identity if no parent).
  2. Apply scene instance translation override (or rig rest translation if zero).
  3. Apply origin offset (translate so that the declared origin is at the current position).
  4. Apply rotation (scene instance rotation override or rig rest rotation).
  5. Apply scale (scene instance scale override, or rig rest scale if zero).
  6. Undo origin offset translation.
  7. Apply animation-driven parameter adjustments (additive on top of the above).
  8. Enforce constraint bounds.

Component grid coordinates are then mapped into this resolved transform to produce viewport pixel positions.


14. Rendering Model

This section describes the normative rendering intent. Renderer implementations may use any technique that produces equivalent results.

14.1 Scene Rendering

  1. Fill viewport with background palette entry (or transparent if index 0).
  2. For each rig instance in back-to-front order: a. Resolve the rig's component reference. b. Resolve the instance transform (§13.4), including all constraints. c. Allocate a local compositing buffer sized to the transformed component's bounding box (clipped to viewport). d. Render the component into the buffer (§14.2). e. Composite the buffer into the scene accumulation buffer using normal alpha compositing.

14.2 Component Rendering

  1. Allocate local buffer (transparent).
  2. For each shape in back-to-front order: a. Rasterize the closed path using the declared winding rule. b. Evaluate the material per pixel (resolve palette entry; evaluate pattern bytecode if a pattern is active; apply animation blend factor if a material blend is active). c. Composite into the local buffer using the shape's declared blend mode.
  3. Return the flattened local buffer.

14.3 Material Evaluation Per Pixel

Given a pixel at component-local normalised coordinates (x, y):

  1. If a material blend animation is active, evaluate both from and to materials at (x, y) and interpolate linearly in linear RGB space using the blend factor.
  2. Otherwise, resolve the single material:
    • If pattern: evaluate bytecode at (x, y), select color from pattern's color set.
    • If gradient: evaluate gradient function at (x, y), interpolate between the two gradient colors.
    • If plain color: use directly.
  3. Convert to linear RGB if not already (RGBA32 inputs are sRGB; apply gamma decode).

14.4 Depth and Layering

In 2D mode (feature bit 3D_COORDS not set), layering is determined entirely by render order (back-to-front). No depth buffer is used.

When 3D_COORDS is set, the behaviour is renderer-defined. This version of the specification does not define 3D rasterization behaviour.


15. Field Encoding Summary

This section collects the index-width rules for quick reference.

Field context Width formula
Palette index ceil(log2(palette_entry_count + 1) / 8) bytes
Component index ceil(log2(component_entry_count + 1) / 8) bytes
Rig index ceil(log2(rig_entry_count + 1) / 8) bytes
Pattern index ceil(log2(pattern_entry_count + 1) / 8) bytes
Dict index ceil(log2(dict_entry_count + 1) / 8) bytes
Coord (per component) ceil(log2(max(grid_w, grid_h) + 1) / 8) bytes

All index fields use the 1-based convention: 0 encodes "absent / default," value N refers to entry N - 1.

All widths computed from the above formulas yield either 1 or 2 bytes for any practical table size. A renderer may reject files requiring wider index fields (e.g., a component table with more than 65534 entries).


16. Open Questions

The following design points are not yet resolved in this version of the specification. They are recorded here for discussion prior to a v0.2 draft.

16.1 Delta-encoded path coordinates. Storing each coordinate relative to the previous point could reduce coordinate byte width on large grids. This adds parser statefulness but may be worthwhile for compactness. Could be an optional per-component flag.

16.2 Scene-space integer coordinates. Rig placement transforms use f32, which is consistent with the rendering pipeline. An alternative is a scene-level integer grid (similar to components) with a declared resolution, using the same variable-width encoding. This would make scene authoring more consistent with component authoring but complicates the transform pipeline.

16.3 Gradient compatibility for material blending. When blending between two gradient palette entries, the current spec evaluates each gradient independently per pixel. This is correct but more expensive than blending gradient parameters directly. A restriction requiring matching gradient types for blending could allow parameter-level interpolation.

16.4 Explicit maximum scene depth. The spec currently bounds scene graph depth by file structure. An explicit limit (e.g., rig parent chains ≤ 8 deep) would make worst-case rendering cost more statically predictable. This may be worth adding as a validation rule.

16.5 Pattern index vs. field in shape material. Currently patterns are referenced via a dedicated palette entry type (tag 4). An alternative is a direct pattern index field in the material, distinct from the palette. This would simplify the palette type system at the cost of a larger shape material structure.

16.6 Animation on palette entries vs. material blend targets. The current design holds palette entries constant and animates materials via blend targets (two palette entries + a factor). A looser model would allow animation tracks to target palette entries directly. Held constant: GPU-friendly, simpler. Animatable: more expressive but harder to implement efficiently.

16.7 Versioning and deprecation policy. The current spec defines feature flags as advisory (best-effort rendering). A stricter model with "required" flags (like PNG's critical chunk bit) would allow files to signal that certain features must be supported for correct rendering. This trades strictness for compatibility.