mirror of
https://github.com/Nexoniarz/YAPF.git
synced 2026-11-03 04:54:26 +00:00
Adds format version 2 alongside version 1 (still written for plain 8-bit images, so existing readers keep working). Full specification in docs/FORMAT.md. - GPU block payloads: BC1-BC7, BC6H, ETC2/EAC, ASTC 4x4-12x12, in tiles of 16x16 blocks. Built-in BC1/BC3/BC4/BC5/BC7/BC6H encoders and BC1-BC7 decoders (BC6H/BC7 bit-exact with a reference decoder). - Lossless 16-bit, half and float pixels as byte planes over the 8-bit coder; order-preserving float mapping, transforms at sample width. - Cubemaps, cube arrays, texture arrays, volumes, animation frames. - Channel meanings, normal convention, linear mask, primaries/transfer, mip filter and coverage-preserving mips (yapf_tex_build_mips). - Small mips stored first, file size in the header, surface/tile tables: decode from a file prefix and single tiles (virtual texturing). - yapf-sum checksums on header, tables and every tile; needs bits so decoders refuse cleanly what they cannot read. - Optional entropy stage: per-surface Huffman code books with a neighbourhood context (~10% smaller photos) and per-lane packing of GPU blocks (~3x smaller on disk); planes decoded interleaved. - Notes: author, licence, source, tool, ICC and application tags. - CLI: --format, --entropy, --coverage, --normal, --map, colour options, 16-bit/HDR input, pack cube/array/volume/anim, info, verify, export. - Tests for all of the above, including fuzzing; v1 API reads 8-bit v2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVrVthWN8GsmgHGUhhsCYm
450 lines
18 KiB
Markdown
450 lines
18 KiB
Markdown
# YAPF file format
|
||
|
||
This is the complete specification of YAPF files. `yapf.c` is the
|
||
reference implementation; where this text and the code disagree, that is a
|
||
bug in one of them.
|
||
|
||
YAPF has two versions:
|
||
|
||
| Version | What it holds | Who writes it |
|
||
| :------ | :------------ | :------------ |
|
||
| **1** | one 8-bit image (1–4 channels) with optional mips | `yapf_save` / `yapf_encode`, and the CLI for plain 8-bit images |
|
||
| **2** | a texture: GPU blocks or lossless pixels in 8/16-bit or float, cubes, arrays, volumes, animation, channel meanings, colour description, notes, checksums, optional entropy coding | `yapf_tex_save` / `yapf_tex_encode`, and the CLI whenever a v2 feature is used |
|
||
|
||
Version 1 is specified at the top of [`yapf.h`](../yapf.h) and is not
|
||
repeated here, except that version 2 reuses its tiles, filters and plane
|
||
payloads (the "group form") byte for byte.
|
||
|
||
---
|
||
|
||
## Design: how YAPF 2 does things its own way
|
||
|
||
YAPF 2 is not a wrapper around someone else's container. A few ideas
|
||
carry the whole format:
|
||
|
||
1. **Everything is tiles.** Pixels are cut into 64 × 64 tiles, GPU
|
||
blocks into tiles of 16 × 16 blocks. Each tile has its own size and
|
||
checksum in a table, so any tile can be found, checked and decoded on its
|
||
own: for multithreaded loading, for partial updates, and for virtual
|
||
texturing / "megatexture" streaming.
|
||
2. **Small mips first.** Surface data is stored from the smallest mip
|
||
to the largest. The first *N* bytes of a file are a complete, lower
|
||
resolution texture, so an engine can show something after reading a
|
||
few kilobytes and stream in the large mips later. The header carries
|
||
the file size, so a loader knows how much is still to come.
|
||
3. **Needs, not versions.** New capabilities are announced by *needs*
|
||
bits. A decoder refuses a file with a needs bit it does not know, and
|
||
can ignore everything else safely. No more version bumps that break
|
||
every reader.
|
||
4. **Wide samples are byte planes.** 16-bit and float samples are split
|
||
into byte planes (high bytes, low bytes) and coded with the same fast
|
||
8-bit machinery as version 1: smooth high bytes compress very well, and
|
||
the decoder stays branch-light and SIMD friendly.
|
||
5. **Shared code books.** The optional entropy stage keeps its Huffman
|
||
tables once per surface, keyed by a small neighbourhood context, so
|
||
tiles stay independent and their own headers stay tiny. For GPU
|
||
blocks the tables are per byte lane, so endpoint bytes and index bytes
|
||
are coded separately.
|
||
6. **Meaning travels with the data.** Channel meanings, the normal-map
|
||
convention, linear channels inside sRGB images, colour primaries and
|
||
transfer, and how the mips were made are header fields, not file-name
|
||
suffixes.
|
||
|
||
---
|
||
|
||
## Conventions
|
||
|
||
All integers are little-endian. `u8`, `u16`, `u32` and `u64` are unsigned
|
||
integers of that many bits. Offsets are from the start of the file.
|
||
|
||
### yapf-sum
|
||
|
||
The checksum of a byte string of length *n*. `K = 0x9E3779B97F4A7C15`;
|
||
all arithmetic is modulo 2⁶⁴.
|
||
|
||
```
|
||
lane[i] = (i + 1)·K XOR n for i = 0, 1, 2, 3
|
||
for each 8-byte word w_j of the input (little-endian; the last word is
|
||
padded with zero bytes):
|
||
a = lane[j mod 4]
|
||
a = (a XOR w_j) · K
|
||
a = a XOR (a >> 32)
|
||
lane[j mod 4] = a
|
||
h = lane[0] XOR rotl(lane[1], 16) XOR rotl(lane[2], 32) XOR rotl(lane[3], 48)
|
||
h = h · K
|
||
h = h XOR (h >> 32)
|
||
result = low 32 bits of h
|
||
```
|
||
|
||
Test vectors: `""` → `0xd7550ed0`, `"YAPF"` → `0xc8cc41d4`,
|
||
`"The quick brown fox jumps over the lazy dog"` → `0xbce4f410`.
|
||
|
||
---
|
||
|
||
## Version 2 layout
|
||
|
||
```
|
||
HEADER 64 bytes
|
||
SURFACE TABLE 16 bytes per surface
|
||
FRAME TABLE 4 bytes per frame, only when frames > 1
|
||
NOTES notes size bytes
|
||
SURFACE DATA in stream order: smallest mip first
|
||
```
|
||
|
||
The *front matter* is everything before the surface data:
|
||
`64 + 16·surfaces + (frames > 1 ? 4·frames : 0) + notes size` bytes.
|
||
|
||
### Header
|
||
|
||
| Offset | Size | Field | Notes |
|
||
| -----: | ---: | :---- | :---- |
|
||
| 0x00 | 4 | magic | `"YAPF"` |
|
||
| 0x04 | 1 | version | 2 |
|
||
| 0x05 | 1 | channels | 1–4 |
|
||
| 0x06 | 1 | format | block format (block payload) or GPU hint (pixel payload); see [GPU formats](#gpu-formats) |
|
||
| 0x07 | 1 | mip levels | 1–16 |
|
||
| 0x08 | 1 | flags | bit 0 premultiplied alpha, bit 1 sRGB, bit 2 coverage-preserving mips; other bits 0 |
|
||
| 0x09 | 1 | payload | 0 pixels (lossless tiles), 1 GPU blocks |
|
||
| 0x0A | 1 | sample | pixel payload: 0 u8, 1 u16, 2 half float, 3 float. Block payload: type of the decoded texels (0, or 2 for BC6H) |
|
||
| 0x0B | 1 | shape | 0 2D, 1 cube, 2 volume |
|
||
| 0x0C | 4 | width | 1–65535 |
|
||
| 0x10 | 4 | height | 1–65535; cubes: width = height |
|
||
| 0x14 | 4 | depth | volumes: 1–65535; otherwise 1 |
|
||
| 0x18 | 2 | layers | ≥ 1 (array layers; with a cube, a cube array); volumes: 1 |
|
||
| 0x1A | 2 | frames | ≥ 1 (animation frames) |
|
||
| 0x1C | 4 | needs | see [Needs](#needs) |
|
||
| 0x20 | 4 | channel map | meaning of channels 0–3, see [Channel meanings](#channel-meanings) |
|
||
| 0x24 | 1 | linear mask | bit *c*: channel *c* is linear even when the sRGB flag is set |
|
||
| 0x25 | 1 | normal | bits 0–1 convention (0 none, 1 OpenGL +Y, 2 DirectX −Y); bits 2–3 encoding (0 XYZ, 1 XY with Z reconstructed, 2 octahedral) |
|
||
| 0x26 | 1 | primaries | 0 unspecified, 1 BT.709/sRGB, 2 Display P3, 3 BT.2020, 4 ACES AP0, 5 ACEScg (AP1), 6 Adobe RGB |
|
||
| 0x27 | 1 | transfer | 0 unspecified (the sRGB flag decides), 1 sRGB, 2 linear, 3 gamma 2.2, 4 PQ (ST 2084), 5 HLG, 6 BT.1886 |
|
||
| 0x28 | 1 | mip filter | bits 0–6: 0 unknown, 1 box, 2 tent, 3 Kaiser, 4 Lanczos, 5 Mitchell, 6 authored; bit 7: sRGB channels were filtered in linear light |
|
||
| 0x29 | 1 | alpha ref | with the coverage flag: alpha-test reference 0–255 |
|
||
| 0x2A | 2 | loop count | animation repeats, 0 = forever |
|
||
| 0x2C | 4 | notes size | bytes of the notes area |
|
||
| 0x30 | 8 | file size | total bytes of the file |
|
||
| 0x38 | 4 | tables check | yapf-sum of bytes [0x40, front matter end) |
|
||
| 0x3C | 4 | header check | yapf-sum of bytes [0x00, 0x3C) |
|
||
|
||
The header check covers the tables check, so the whole front matter is
|
||
protected.
|
||
|
||
### Surfaces
|
||
|
||
A *surface* is one mip level of one face of one layer of one frame. With
|
||
`faces = 6` for cubes (order +X, −X, +Y, −Y, +Z, −Z) and 1 otherwise,
|
||
surface number
|
||
|
||
```
|
||
s = ((frame · layers + layer) · faces + face) · mip_levels + mip
|
||
```
|
||
|
||
Mip *m* is `max(1, width >> m) × max(1, height >> m)`, and for volumes
|
||
`max(1, depth >> m)` slices deep. A volume surface holds all its slices.
|
||
|
||
**Surface table entry** (16 bytes, one per surface, in surface order):
|
||
|
||
| Offset | Size | Field |
|
||
| -----: | ---: | :---- |
|
||
| +0 | 8 | offset of the surface data |
|
||
| +8 | 4 | size of the surface data |
|
||
| +12 | 4 | tile-table check: yapf-sum of the surface's book size, book and tile table |
|
||
|
||
**Stream order.** Writers store surface data for mip `mip_levels − 1`
|
||
first (all frames, layers and faces in surface order), then the next larger
|
||
mip, and so on, ending with mip 0. Readers must use the offsets and must not
|
||
rely on the order. The number of leading bytes that hold every surface of
|
||
mip *m* and smaller is the largest `offset + size` over those surfaces.
|
||
|
||
### Frame table
|
||
|
||
When `frames > 1`: one `u32` per frame, its display time in microseconds.
|
||
|
||
### Notes
|
||
|
||
A sequence of entries that exactly fills the notes area:
|
||
|
||
```
|
||
tag 4 bytes
|
||
length u32
|
||
data length bytes
|
||
```
|
||
|
||
| Tag | Content |
|
||
| :-- | :------ |
|
||
| `AUTH` | author (UTF-8) |
|
||
| `LICN` | licence (UTF-8, e.g. an SPDX id) |
|
||
| `SRCE` | source / origin (UTF-8, e.g. a URL or path) |
|
||
| `TOOL` | the tool that wrote the file (UTF-8) |
|
||
| `TITL` | title (UTF-8) |
|
||
| `DESC` | description (UTF-8) |
|
||
| `DATE` | date, ISO 8601 (UTF-8) |
|
||
| `ICCP` | an ICC colour profile |
|
||
| `ORNT` | orientation, 1 byte: bits 0–1 clockwise quarter turns, bit 2 mirror left–right after turning |
|
||
| `DPI ` | 2 × u32: horizontal and vertical dots per inch × 1000 |
|
||
| `CLL ` | 2 × u16: maximum content light level, maximum frame-average light level (cd/m²) |
|
||
|
||
Tags that begin with a lowercase letter belong to applications (for
|
||
example `keel`). Readers ignore tags they do not know. Tags may repeat;
|
||
order is kept.
|
||
|
||
### Needs
|
||
|
||
| Bit | Name | Set when |
|
||
| --: | :--- | :------- |
|
||
| 0 | WIDE | pixel payload with sample ≠ u8 |
|
||
| 1 | BLOCKS | block payload |
|
||
| 2 | ENTROPY | any surface has a code book |
|
||
| 3 | LAYOUT | shape ≠ 2D, or layers > 1 |
|
||
| 4 | FRAMES | frames > 1 |
|
||
|
||
A decoder must refuse a file that sets a needs bit it does not implement
|
||
(this library: any bit above 4). A file that uses a feature without its
|
||
bit is corrupt. Everything that is not a needs feature — channel map,
|
||
colour fields, mip filter, notes, frame times — may be ignored.
|
||
|
||
A decoder that only implements 8-bit 2D pixels can therefore read every
|
||
version 2 file whose needs are 0, and cleanly refuse the rest.
|
||
|
||
---
|
||
|
||
## Surface data
|
||
|
||
```
|
||
book size u32 0 when the surface has no code book
|
||
book book size bytes
|
||
tile table 8 bytes per tile: u32 size (bit 31: packed), u32 yapf-sum of the tile bytes
|
||
tiles back to back; their sizes must add up exactly to the rest of the surface
|
||
```
|
||
|
||
Tiles are numbered slice by slice, rows top to bottom, tiles left to right.
|
||
For pixels the grid is `ceil(w / 64) × ceil(h / 64)` tiles per slice (edge
|
||
tiles are smaller, no padding); for blocks it is
|
||
`ceil(bx / 16) × ceil(by / 16)` tiles of 16 × 16 blocks, where
|
||
`bx = ceil(w / block width)` and `by = ceil(h / block height)`.
|
||
|
||
Bit 31 of a tile size (*packed*) is allowed only in block tiles of a
|
||
surface with a code book.
|
||
|
||
---
|
||
|
||
## Pixel tiles
|
||
|
||
Let *B* be the bytes per sample (1, 2 or 4), *ch* the channels, and the
|
||
tile `tw × th` pixels.
|
||
|
||
### Tile header
|
||
|
||
```
|
||
byte 0 0x80 stored: the raw samples follow, interleaved in
|
||
channel order, each B bytes little-endian;
|
||
exactly tw·th·ch·B bytes
|
||
otherwise coded:
|
||
bits 0–3 B = 1: bit c set = plane c is constant; B > 1: 0
|
||
bit 4 colour transform subtract-green
|
||
bit 5 an entropy mask follows
|
||
bit 6 no colour transform
|
||
bit 7 0
|
||
bits 4 and 6 are exclusive and need ch >= 3; with neither
|
||
set, an RGB(A) tile uses YCoCg-R
|
||
B > 1 u16 constant mask, bit p = plane p is constant
|
||
bit 5 entropy mask: u8 (B = 1) or u16 (B > 1); bit p = plane p is
|
||
entropy coded. It must not include constant planes.
|
||
then one value byte per constant plane, in plane order;
|
||
one payload per other plane, in plane order;
|
||
the tile ends exactly where its last payload ends
|
||
```
|
||
|
||
With B = 1 and neither bit 5 nor bit 6 set, a coded tile is exactly a
|
||
version 1 tile.
|
||
|
||
### Planes
|
||
|
||
The colour transform turns R, G, B (+ A) into transformed channels; it is
|
||
skipped for 1–2 channels and with bit 6.
|
||
|
||
* **YCoCg-R** at the sample width *n* = 8·B bits, all arithmetic mod 2ⁿ,
|
||
`>>` an arithmetic shift of the *n*-bit two's complement value, `h = 2ⁿ⁻¹`:
|
||
|
||
```
|
||
forward: o = R − B; t = B + (o >> 1); q = G − t;
|
||
Y = t + (q >> 1); Co = o + h; Cg = q + h
|
||
inverse: o = Co − h; q = Cg − h; t = Y − (q >> 1);
|
||
G = q + t; B = t − (o >> 1); R = B + o
|
||
```
|
||
* **subtract-green**: `G, R − G + h, B − G + h` (+ A), mod 2ⁿ.
|
||
|
||
Before the transform, float samples (half or float) are mapped to
|
||
integers that keep their order: if the sign bit is set every bit is
|
||
flipped, otherwise the sign bit is set. The decoder undoes this after the
|
||
inverse transform.
|
||
|
||
Transformed channel *c* gives B planes: plane `c·B + k` holds byte
|
||
`B − 1 − k` of the value (the most significant byte first). For B = 1
|
||
the planes are the transformed channels.
|
||
|
||
### Plane payloads
|
||
|
||
A plane payload has one of two forms, chosen per plane by the entropy mask.
|
||
|
||
**Group form** — exactly the version 1 plane payload: a filter byte per
|
||
band of 8 rows, 4-bit control codes, and groups of 8 residuals at the
|
||
group's bit width. See `yapf.h`.
|
||
|
||
**Entropy form**
|
||
|
||
```
|
||
filter[ceil(th / 8)] as in the group form
|
||
u16 L bytes of the bit stream
|
||
L bytes the folded residuals of the plane in raster order
|
||
```
|
||
|
||
Each residual is coded with table `(slot, context)` of the surface's code
|
||
book:
|
||
|
||
* `slot = t · 16 + p`, where *p* is the plane and *t* is 1 with
|
||
subtract-green, 2 with no transform, else 0;
|
||
* `context = min(6, floor(log2(s + 1)))`, `s = left + above`, the folded
|
||
residuals of the left and upper neighbours in this plane (0 outside the
|
||
tile) — so 0, 1–2, 3–6, 7–14, 15–30, 31–62, 63 and up.
|
||
|
||
The stream must end in its last byte. Folded residuals then go through
|
||
the filters exactly as in the group form.
|
||
|
||
---
|
||
|
||
## Block tiles
|
||
|
||
A block tile of `tbw × tbh` blocks of *S* bytes is either
|
||
|
||
* **raw**: the blocks, rows top to bottom, exactly `tbw·tbh·S` bytes — as
|
||
the GPU takes them; or
|
||
* **packed** (bit 31 of its size): one bit stream holding byte 0 of every
|
||
block (in raster order), then byte 1 of every block, …, byte S − 1.
|
||
Byte lane *k* is coded with table `(slot k, context 0)` of the surface's
|
||
code book. The stream must end in the last byte of the tile.
|
||
|
||
Decoded block surfaces are row-major blocks, slice after slice, ready for
|
||
`glCompressedTexImage*` / `vkCmdCopyBufferToImage`.
|
||
|
||
---
|
||
|
||
## Code book
|
||
|
||
```
|
||
u16 count number of tables, 1–336
|
||
count × {
|
||
u8 slot 0–47
|
||
u8 context 0–6
|
||
code table
|
||
}
|
||
```
|
||
|
||
Each `(slot, context)` pair appears at most once. The book must fill its
|
||
size exactly.
|
||
|
||
**Code table**: `u8 n − 1`, then *n* 4-bit code lengths (symbols
|
||
0 … n − 1, low nibble first, an odd count padded with a zero nibble).
|
||
Length 0 = no code; otherwise 1–11. At least one code; Kraft sum ≤ 1.
|
||
Codes are canonical — by length, then by symbol, the first code of each
|
||
length one more than the last code of the previous length, shifted left —
|
||
and stored least significant bit first (the first bit of a code is the
|
||
lowest unread bit of the stream), so a decoder can look up 11 bits at a
|
||
time.
|
||
|
||
---
|
||
|
||
## GPU formats
|
||
|
||
Pixel payload hints (`format` field):
|
||
|
||
| Value | Name | Vulkan |
|
||
| ----: | :--- | :----- |
|
||
| 0x00 | RGBA8 | R8G8B8A8_UNORM |
|
||
| 0x01 | RGB8 | R8G8B8_UNORM |
|
||
| 0x02 | RG8 | R8G8_UNORM |
|
||
| 0x03 | R8 | R8_UNORM |
|
||
| 0x04 | SRGB8_A8 | R8G8B8A8_SRGB |
|
||
| 0x05 | SRGB8 | R8G8B8_SRGB |
|
||
| 0x06–0x09 | RGBA16, RGB16, RG16, R16 | R16G16B16A16_UNORM … R16_UNORM |
|
||
| 0x0A–0x0D | RGBA16F, RGB16F, RG16F, R16F | R16G16B16A16_SFLOAT … R16_SFLOAT |
|
||
| 0x0E–0x11 | RGBA32F, RGB32F, RG32F, R32F | R32G32B32A32_SFLOAT … R32_SFLOAT |
|
||
|
||
Block formats (block payload). With the sRGB flag, use the `_SRGB`
|
||
variant where one exists.
|
||
|
||
| Value | Name | Block | Bytes | Vulkan / DXGI |
|
||
| ----: | :--- | :---: | ----: | :------------ |
|
||
| 0x40 | BC1 | 4×4 | 8 | BC1_RGB(A)_UNORM / BC1_UNORM |
|
||
| 0x41 | BC2 | 4×4 | 16 | BC2_UNORM |
|
||
| 0x42 | BC3 | 4×4 | 16 | BC3_UNORM |
|
||
| 0x43 | BC4 | 4×4 | 8 | BC4_UNORM |
|
||
| 0x44 | BC4_SNORM | 4×4 | 8 | BC4_SNORM |
|
||
| 0x45 | BC5 | 4×4 | 16 | BC5_UNORM |
|
||
| 0x46 | BC5_SNORM | 4×4 | 16 | BC5_SNORM |
|
||
| 0x47 | BC6H | 4×4 | 16 | BC6H_UFLOAT / BC6H_UF16 |
|
||
| 0x48 | BC6H_SF | 4×4 | 16 | BC6H_SFLOAT / BC6H_SF16 |
|
||
| 0x49 | BC7 | 4×4 | 16 | BC7_UNORM |
|
||
| 0x50 | ETC2_RGB8 | 4×4 | 8 | ETC2_R8G8B8_UNORM |
|
||
| 0x51 | ETC2_RGB8A1 | 4×4 | 8 | ETC2_R8G8B8A1_UNORM |
|
||
| 0x52 | ETC2_RGBA8 | 4×4 | 16 | ETC2_R8G8B8A8_UNORM |
|
||
| 0x53 | EAC_R11 | 4×4 | 8 | EAC_R11_UNORM |
|
||
| 0x54 | EAC_R11_SNORM | 4×4 | 8 | EAC_R11_SNORM |
|
||
| 0x55 | EAC_RG11 | 4×4 | 16 | EAC_R11G11_UNORM |
|
||
| 0x56 | EAC_RG11_SNORM | 4×4 | 16 | EAC_R11G11_SNORM |
|
||
| 0x60–0x6D | ASTC 4×4, 5×4, 5×5, 6×5, 6×6, 8×5, 8×6, 8×8, 10×5, 10×6, 10×8, 10×10, 12×10, 12×12 | as named | 16 | ASTC_*_UNORM (HDR: transfer linear, sample half) |
|
||
|
||
---
|
||
|
||
## Channel meanings
|
||
|
||
| Value | Meaning | Value | Meaning |
|
||
| ----: | :------ | ----: | :------ |
|
||
| 0 | default for the channel count (Y / YA / RGB / RGBA) | 11 | metalness |
|
||
| 1 | red | 12 | ambient occlusion |
|
||
| 2 | green | 13 | height |
|
||
| 3 | blue | 14 | specular |
|
||
| 4 | alpha | 15 | emissive |
|
||
| 5 | luma | 16 | opacity (alpha-test mask) |
|
||
| 6 | normal X | 17 | mask |
|
||
| 7 | normal Y | 18 | cavity |
|
||
| 8 | normal Z | 19 | thickness |
|
||
| 9 | roughness | 20 | id |
|
||
| 10 | gloss (1 − roughness) | 255 | unused |
|
||
|
||
Alpha (an explicit 4 or 16, or the last channel of a 2- or 4-channel image
|
||
with default meanings) is always linear. The linear mask marks further
|
||
channels that are linear inside an sRGB image, such as masks packed next to
|
||
colour.
|
||
|
||
---
|
||
|
||
## Mips
|
||
|
||
`mip filter` records how the mips were made. This library's
|
||
`yapf_tex_build_mips` uses a 2 × 2 box (2 × 2 × 2 for volumes, edges
|
||
repeat), averages sRGB 8-bit channels in linear light (bit 7), and with an
|
||
alpha reference rescales alpha on every mip so that the share of texels
|
||
with alpha *above* the reference stays that of mip 0 (coverage flag,
|
||
`alpha ref`). Alpha-tested foliage and fences then keep their density at
|
||
a distance instead of thinning out.
|
||
|
||
---
|
||
|
||
## Validation summary
|
||
|
||
A decoder rejects a file when:
|
||
|
||
* the magic, version, a size, shape or count is out of range, or a cube is
|
||
not square;
|
||
* the header or tables check does not match (unless the reader was told to
|
||
trust the file);
|
||
* a needs bit is unknown (*unsupported*), or a feature is used without its
|
||
needs bit (*corrupt*);
|
||
* a surface lies outside the file, its book is malformed or present without
|
||
ENTROPY, or its tiles do not fill it exactly;
|
||
* a tile's yapf-sum does not match, or the tile does not decode to exactly
|
||
its size (reserved bits, invalid filters, codes, masks or lengths).
|
||
|
||
Because every structure is validated before use, corrupt or hostile input
|
||
is rejected, never read out of bounds.
|