mirror of
https://github.com/Nexoniarz/YAPF.git
synced 2026-11-02 20:44:27 +00:00
- KDE thumbnailer: texture loader, decodes only the mip it needs; GPU blocks, 16-bit and float via the new yapf_tex_to_rgba8. Tested through KDE's plugin loader with KF6 from nixpkgs. - Photoshop: shows the tool's error message; HDR textures open as 32-bit (.hdr), 16-bit stays 16-bit; 16-bit documents save as 16-bit. - CLI: 16-bit PNG export, readable conversion errors. - default.nix: yapf tool, library, header, MIME type and thumbnailer (absolute store path); runs the test suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVrVthWN8GsmgHGUhhsCYm
625 lines
33 KiB
C
625 lines
33 KiB
C
/*
|
||
* yapf.h — Yet Another Picture Format
|
||
* Public API and format specification.
|
||
*
|
||
* Copyright 2026 Nexoniarz
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*
|
||
* -------------------------------------------------------------------------
|
||
*
|
||
* YAPF — lossless, GPU-ready image format.
|
||
*
|
||
* Designed for single-core decode speed. Everything is byte aligned:
|
||
* no bit reader, no entropy coder, no context model. Per row the decoder
|
||
* dispatches one filter; per 8 samples it reads a 4-bit control code and
|
||
* exactly w bytes of payload, unpacked with one 64-bit load and shifts.
|
||
* The encoder does the hard work (filter search, colour transform choice,
|
||
* optimal control codes); the decoder only follows instructions.
|
||
*
|
||
* 1. Per tile: YCoCg-R or subtract-green colour transform, 8-bit modular
|
||
* 2. Per band of 8 rows: one of 12 row filters
|
||
* 3. Residuals in groups of 8, each group at its bit width (0–8);
|
||
* 4-bit control codes, with codes that repeat the previous width
|
||
* 4. Independent 64×64 tiles with a size table → random access and
|
||
* optional parallel decode
|
||
*
|
||
* The file carries GPU metadata (format hint, mip levels, flags) so a
|
||
* loader can upload the decoded buffer straight to a texture without any
|
||
* additional transcoding step.
|
||
*
|
||
* Two versions exist. Version 1 (below) is a single 8-bit 2D image with
|
||
* optional mips. Version 2 is the texture container: GPU block payloads
|
||
* (BCn / ETC2 / ASTC), 16-bit and float samples, cubemaps, arrays and
|
||
* volumes, channel meanings, per-tile checksums, optional entropy-coded
|
||
* planes, notes (metadata) and animation frames. Its complete
|
||
* specification is docs/FORMAT.md; a summary follows the v1 spec.
|
||
*
|
||
* =========================================================================
|
||
* FILE FORMAT (version 1)
|
||
* =========================================================================
|
||
*
|
||
* All multi-byte integers are little-endian. All arithmetic on samples
|
||
* is mod 256.
|
||
*
|
||
* ┌─────────────────────────────────────────────────────────────────────┐
|
||
* │ HEADER — 20 bytes │
|
||
* ├────────┬──────┬────────────────────────────────────────────────────┤
|
||
* │ Offset │ Size │ Field │
|
||
* ├────────┼──────┼────────────────────────────────────────────────────┤
|
||
* │ 0x00 │ 4 │ Magic: "YAPF" │
|
||
* │ 0x04 │ 1 │ Version: 1 │
|
||
* │ 0x05 │ 1 │ Channels: 1 gray / 2 gray+A / 3 RGB / 4 RGBA │
|
||
* │ 0x06 │ 1 │ GPU format hint (YAPF_GPU_* constant) │
|
||
* │ 0x07 │ 1 │ Mip levels (1 = base only, max 16) │
|
||
* │ 0x08 │ 1 │ Flags (see YAPF_FLAG_* bitmask) │
|
||
* │ 0x09 │ 3 │ Reserved (write 0, ignore on read) │
|
||
* │ 0x0C │ 4 │ Base width (uint32, pixels) │
|
||
* │ 0x10 │ 4 │ Base height (uint32, pixels) │
|
||
* └────────┴──────┴────────────────────────────────────────────────────┘
|
||
*
|
||
* ┌─────────────────────────────────────────────────────────────────────┐
|
||
* │ MIP INDEX — 8 bytes × mip_levels, immediately after header │
|
||
* ├────────┬──────┬────────────────────────────────────────────────────┤
|
||
* │ +0 │ 4 │ Byte offset of mip data from start of file (uint32) │
|
||
* │ +4 │ 4 │ Byte size of mip data (uint32) │
|
||
* └────────┴──────┴────────────────────────────────────────────────────┘
|
||
* Mip m has size max(1, width >> m) × max(1, height >> m).
|
||
*
|
||
* ┌─────────────────────────────────────────────────────────────────────┐
|
||
* │ MIP LEVEL DATA │
|
||
* ├─────────────────────────────────────────────────────────────────────┤
|
||
* │ Pixels are divided into 64×64 tiles in row-major order; the last │
|
||
* │ column / row of tiles may be smaller (no padding). │
|
||
* │ │
|
||
* │ uint32 tile_size[tile_count] byte size of each tile │
|
||
* │ tile data, back to back │
|
||
* │ │
|
||
* │ TILE │
|
||
* │ 1 byte header │
|
||
* │ 0x80 stored: tile_w × tile_h × channels raw bytes │
|
||
* │ follow, interleaved, in the original channel order │
|
||
* │ otherwise coded: │
|
||
* │ bits 0–3 bit c set = plane c is constant (only bits below │
|
||
* │ the channel count may be set) │
|
||
* │ bit 4 colour transform: 0 = YCoCg-R, 1 = subtract-green │
|
||
* │ (RGB / RGBA only; must be 0 for gray) │
|
||
* │ bits 5–7 must be 0 │
|
||
* │ coded tiles then hold one value byte per constant plane, and a │
|
||
* │ plane payload per other plane, both in plane order. The tile │
|
||
* │ must end exactly where its last payload ends. │
|
||
* │ │
|
||
* │ PLANES │
|
||
* │ gray / gray+A: the channels as stored. │
|
||
* │ RGB(A), YCoCg-R (>> is an arithmetic shift of the value as int8):│
|
||
* │ planes Y, Co, Cg (+ A) │
|
||
* │ forward: o = R − B; t = B + (o >> 1); q = G − t; │
|
||
* │ Y = t + (q >> 1); Co = o + 128; Cg = q + 128 │
|
||
* │ inverse: o = Co − 128; q = Cg − 128; t = Y − (q >> 1); │
|
||
* │ G = q + t; B = t − (o >> 1); R = B + o │
|
||
* │ RGB(A), subtract-green: │
|
||
* │ planes G, R − G + 128, B − G + 128 (+ A) │
|
||
* │ │
|
||
* │ PLANE PAYLOAD │
|
||
* │ filter[ceil(tile_h / 8)] one byte per band of 8 rows. │
|
||
* │ L / A / D / AR = left, above, above-left, above-right sample; │
|
||
* │ samples outside the tile read as 0, except that AR in the │
|
||
* │ last column reads as A. │
|
||
* │ 0 Left pred = L │
|
||
* │ 1 Gradient pred = L + A − D │
|
||
* │ 2 Up-right pred = AR │
|
||
* │ 3 Up-average pred = (A + AR) >> 1 (not mod 256) │
|
||
* │ 16 + k−1 Up k pred = sample k rows above, k = 1..8 │
|
||
* │ control nibbles, low nibble of each byte first, covering │
|
||
* │ exactly ceil(tile_w × tile_h / 8) groups of 8 consecutive │
|
||
* │ residuals in raster order: │
|
||
* │ 0–8 one group of width w │
|
||
* │ 9–15 2, 3, 4, 8, 16, 32 or 64 groups at the width of │
|
||
* │ the last 0–8 code in this plane (0 if none yet) │
|
||
* │ an odd nibble count is padded with a zero high nibble │
|
||
* │ group data: w bytes per group, the 8 values packed little- │
|
||
* │ endian, value i in bits [i·w, i·w + w) │
|
||
* │ Residual r = (sample − pred) mod 256 is stored folded: │
|
||
* │ v = (r << 1) ^ (r >> 7) (0, −1, 1, −2, … → 0, 1, 2, 3, …) │
|
||
* │ The last group is padded with zero values. │
|
||
* └─────────────────────────────────────────────────────────────────────┘
|
||
*
|
||
* GPU FORMAT HINTS (YAPF_GPU_* values for the gpu_format field)
|
||
* ──────────────────────────────────────────────────────────────
|
||
* 0x00 RGBA8 GL_RGBA8 / VK_FORMAT_R8G8B8A8_UNORM
|
||
* 0x01 RGB8 GL_RGB8 / VK_FORMAT_R8G8B8_UNORM
|
||
* 0x02 RG8 GL_RG8 / VK_FORMAT_R8G8_UNORM
|
||
* 0x03 R8 GL_R8 / VK_FORMAT_R8_UNORM
|
||
* 0x04 SRGB8_A8 GL_SRGB8_ALPHA8
|
||
* 0x05 SRGB8 GL_SRGB8
|
||
*
|
||
* FLAGS (YAPF_FLAG_* bitmask for the flags field)
|
||
* ─────────────────────────────────────────────────
|
||
* bit 0 PREMULT_ALPHA alpha is premultiplied into RGB
|
||
* bit 1 SRGB pixels are in sRGB gamma (informational only)
|
||
* bit 2 COVERAGE (v2) mips keep alpha-test coverage at alpha_ref
|
||
*
|
||
* =========================================================================
|
||
* FILE FORMAT (version 2) — summary; the full text is docs/FORMAT.md
|
||
* =========================================================================
|
||
*
|
||
* HEADER (64 bytes)
|
||
* 0x00 "YAPF" 0x04 version 2 0x05 channels 0x06 format
|
||
* 0x07 mips 0x08 flags 0x09 payload 0x0A sample 0x0B shape
|
||
* 0x0C width u32 0x10 height u32 0x14 depth u32
|
||
* 0x18 layers u16 0x1A frames u16 0x1C needs u32
|
||
* 0x20 channel map[4] 0x24 linear mask 0x25 normal 0x26 primaries
|
||
* 0x27 transfer 0x28 mip filter 0x29 alpha ref 0x2A loop count u16
|
||
* 0x2C notes size u32 0x30 file size u64
|
||
* 0x38 tables check u32 (yapf-sum of bytes [0x40, data start))
|
||
* 0x3C header check u32 (yapf-sum of bytes [0, 0x3C))
|
||
* SURFACE TABLE 16 bytes per surface: offset u64, size u32,
|
||
* tile-table check u32. Surface s of (frame, layer,
|
||
* face, mip) is ((frame·layers + layer)·faces + face)·mips + mip
|
||
* FRAME TABLE u32 duration in µs per frame (only when frames > 1)
|
||
* NOTES tag[4], length u32, bytes — repeated
|
||
* SURFACE DATA smallest mips first, so a file prefix is a complete
|
||
* low-resolution texture. Per surface: u32 book size,
|
||
* code book (Huffman tables by slot and context, only
|
||
* with entropy coding), tile table (size u32 with bit 31 =
|
||
* packed, yapf-sum u32 per tile), then the tiles. Pixel
|
||
* tiles are the v1 tiles plus byte planes for wide samples
|
||
* and entropy-coded planes; block tiles hold 16 × 16 GPU
|
||
* blocks, raw or packed lane by lane.
|
||
* NEEDS a decoder must refuse a file with a needs bit it does
|
||
* not implement; everything else may be ignored safely.
|
||
*/
|
||
|
||
#ifndef YAPF_H
|
||
#define YAPF_H
|
||
|
||
#include <stdint.h>
|
||
#include <stddef.h>
|
||
|
||
/* ── GPU format hints ────────────────────────────────────────────────── */
|
||
|
||
#define YAPF_GPU_RGBA8 0x00
|
||
#define YAPF_GPU_RGB8 0x01
|
||
#define YAPF_GPU_RG8 0x02
|
||
#define YAPF_GPU_R8 0x03
|
||
#define YAPF_GPU_SRGB8_A8 0x04
|
||
#define YAPF_GPU_SRGB8 0x05
|
||
/* v2 pixel hints for wide samples */
|
||
#define YAPF_GPU_RGBA16 0x06
|
||
#define YAPF_GPU_RGB16 0x07
|
||
#define YAPF_GPU_RG16 0x08
|
||
#define YAPF_GPU_R16 0x09
|
||
#define YAPF_GPU_RGBA16F 0x0A
|
||
#define YAPF_GPU_RGB16F 0x0B
|
||
#define YAPF_GPU_RG16F 0x0C
|
||
#define YAPF_GPU_R16F 0x0D
|
||
#define YAPF_GPU_RGBA32F 0x0E
|
||
#define YAPF_GPU_RGB32F 0x0F
|
||
#define YAPF_GPU_RG32F 0x10
|
||
#define YAPF_GPU_R32F 0x11
|
||
/* v2 GPU block formats (payload = YAPF_PAYLOAD_BLOCKS). The sRGB flag
|
||
* selects the _SRGB variant where the API has one. */
|
||
#define YAPF_GPU_BC1 0x40 /* RGB(A1), 8 bytes per 4×4 */
|
||
#define YAPF_GPU_BC2 0x41 /* RGBA, explicit 4-bit alpha */
|
||
#define YAPF_GPU_BC3 0x42 /* RGBA */
|
||
#define YAPF_GPU_BC4 0x43 /* R */
|
||
#define YAPF_GPU_BC4_SNORM 0x44
|
||
#define YAPF_GPU_BC5 0x45 /* RG (normal maps) */
|
||
#define YAPF_GPU_BC5_SNORM 0x46
|
||
#define YAPF_GPU_BC6H 0x47 /* RGB half float, unsigned */
|
||
#define YAPF_GPU_BC6H_SF 0x48 /* RGB half float, signed */
|
||
#define YAPF_GPU_BC7 0x49 /* RGB(A) */
|
||
#define YAPF_GPU_ETC2_RGB8 0x50
|
||
#define YAPF_GPU_ETC2_RGB8A1 0x51
|
||
#define YAPF_GPU_ETC2_RGBA8 0x52
|
||
#define YAPF_GPU_EAC_R11 0x53
|
||
#define YAPF_GPU_EAC_R11_SNORM 0x54
|
||
#define YAPF_GPU_EAC_RG11 0x55
|
||
#define YAPF_GPU_EAC_RG11_SNORM 0x56
|
||
#define YAPF_GPU_ASTC_4x4 0x60 /* ASTC: 0x60 + footprint index */
|
||
#define YAPF_GPU_ASTC_5x4 0x61
|
||
#define YAPF_GPU_ASTC_5x5 0x62
|
||
#define YAPF_GPU_ASTC_6x5 0x63
|
||
#define YAPF_GPU_ASTC_6x6 0x64
|
||
#define YAPF_GPU_ASTC_8x5 0x65
|
||
#define YAPF_GPU_ASTC_8x6 0x66
|
||
#define YAPF_GPU_ASTC_8x8 0x67
|
||
#define YAPF_GPU_ASTC_10x5 0x68
|
||
#define YAPF_GPU_ASTC_10x6 0x69
|
||
#define YAPF_GPU_ASTC_10x8 0x6A
|
||
#define YAPF_GPU_ASTC_10x10 0x6B
|
||
#define YAPF_GPU_ASTC_12x10 0x6C
|
||
#define YAPF_GPU_ASTC_12x12 0x6D
|
||
|
||
/* ── Flags ───────────────────────────────────────────────────────────── */
|
||
|
||
#define YAPF_FLAG_PREMULT_ALPHA 0x01
|
||
#define YAPF_FLAG_SRGB 0x02
|
||
#define YAPF_FLAG_COVERAGE 0x04 /* v2: mips preserve alpha-test coverage */
|
||
|
||
/* ── Channel constants ───────────────────────────────────────────────── */
|
||
|
||
#define YAPF_CHANNELS_GRAY 1
|
||
#define YAPF_CHANNELS_GRAY_ALPHA 2
|
||
#define YAPF_CHANNELS_RGB 3
|
||
#define YAPF_CHANNELS_RGBA 4
|
||
|
||
/* ── Return codes ────────────────────────────────────────────────────── */
|
||
|
||
#define YAPF_OK 0
|
||
#define YAPF_ERR_INVALID -1
|
||
#define YAPF_ERR_IO -2
|
||
#define YAPF_ERR_OOM -3
|
||
#define YAPF_ERR_CORRUPT -4
|
||
#define YAPF_ERR_UNSUPPORTED -5 /* valid file, feature this decoder lacks */
|
||
#define YAPF_ERR_CHECKSUM -6 /* a yapf-sum does not match */
|
||
#define YAPF_ERR_TRUNCATED -7 /* data lies beyond the bytes available yet */
|
||
|
||
/* ── Limits ──────────────────────────────────────────────────────────── */
|
||
|
||
#define YAPF_MAX_DIM 65535u
|
||
#define YAPF_MAX_MIPS 16u
|
||
#define YAPF_TILE_SIZE 64u
|
||
#define YAPF_BLOCK_TILE 16u /* v2 block tiles: 16 × 16 GPU blocks */
|
||
|
||
/* ── Image descriptor ────────────────────────────────────────────────── */
|
||
|
||
typedef struct {
|
||
uint32_t width;
|
||
uint32_t height;
|
||
uint8_t channels; /* 1–4 */
|
||
uint8_t gpu_format; /* YAPF_GPU_* */
|
||
uint8_t flags; /* YAPF_FLAG_* bitmask */
|
||
uint8_t mip_levels; /* number of mip levels stored (1 = base only) */
|
||
uint8_t *pixels; /* base-level pixels, malloc-owned */
|
||
uint8_t **mips; /* mips[0]=base alias, mips[1..mip_levels-1] owned */
|
||
} yapf_image_t;
|
||
|
||
/* ── Version 2: texture description ──────────────────────────────────── */
|
||
|
||
/* payload */
|
||
#define YAPF_PAYLOAD_PIXELS 0 /* lossless YAPF tiles */
|
||
#define YAPF_PAYLOAD_BLOCKS 1 /* GPU blocks, uploaded as they are */
|
||
|
||
/* sample: how one channel value is stored (pixel payload); for block
|
||
* payloads, the type of the decoded texels (U8, or F16 for BC6H) */
|
||
#define YAPF_SAMPLE_U8 0
|
||
#define YAPF_SAMPLE_U16 1
|
||
#define YAPF_SAMPLE_F16 2 /* IEEE half, as uint16_t bits */
|
||
#define YAPF_SAMPLE_F32 3
|
||
|
||
/* shape */
|
||
#define YAPF_SHAPE_2D 0
|
||
#define YAPF_SHAPE_CUBE 1 /* 6 faces: +X −X +Y −Y +Z −Z; with layers
|
||
> 1 a cube array */
|
||
#define YAPF_SHAPE_VOLUME 2 /* depth slices; mips halve depth too */
|
||
|
||
/* needs: features a decoder must implement to read the file */
|
||
#define YAPF_NEED_WIDE 0x01 /* sample other than U8 (pixel payload) */
|
||
#define YAPF_NEED_BLOCKS 0x02 /* GPU block payload */
|
||
#define YAPF_NEED_ENTROPY 0x04 /* code books: entropy planes, packed blocks */
|
||
#define YAPF_NEED_LAYOUT 0x08 /* cube, volume or layers > 1 */
|
||
#define YAPF_NEED_FRAMES 0x10 /* frames > 1 */
|
||
#define YAPF_NEED_ALL 0x1F /* everything this library implements */
|
||
|
||
/* channel meanings (channel_map[c]); 0 = default for the channel count */
|
||
#define YAPF_CH_DEFAULT 0
|
||
#define YAPF_CH_RED 1
|
||
#define YAPF_CH_GREEN 2
|
||
#define YAPF_CH_BLUE 3
|
||
#define YAPF_CH_ALPHA 4
|
||
#define YAPF_CH_LUMA 5
|
||
#define YAPF_CH_NORMAL_X 6
|
||
#define YAPF_CH_NORMAL_Y 7
|
||
#define YAPF_CH_NORMAL_Z 8
|
||
#define YAPF_CH_ROUGHNESS 9
|
||
#define YAPF_CH_GLOSS 10 /* smoothness = 1 − roughness */
|
||
#define YAPF_CH_METALNESS 11
|
||
#define YAPF_CH_OCCLUSION 12 /* ambient occlusion */
|
||
#define YAPF_CH_HEIGHT 13
|
||
#define YAPF_CH_SPECULAR 14
|
||
#define YAPF_CH_EMISSIVE 15
|
||
#define YAPF_CH_OPACITY 16 /* alpha-test / cut-out mask */
|
||
#define YAPF_CH_MASK 17 /* generic mask */
|
||
#define YAPF_CH_CAVITY 18
|
||
#define YAPF_CH_THICKNESS 19
|
||
#define YAPF_CH_ID 20 /* material or object id */
|
||
#define YAPF_CH_UNUSED 255
|
||
|
||
/* normal: bits 0–1 convention, bits 2–3 encoding */
|
||
#define YAPF_NORMAL_NONE 0x00
|
||
#define YAPF_NORMAL_GL 0x01 /* +Y up (OpenGL, Blender, Unity) */
|
||
#define YAPF_NORMAL_DX 0x02 /* −Y (DirectX, Unreal) */
|
||
#define YAPF_NORMAL_XYZ 0x00
|
||
#define YAPF_NORMAL_XY 0x04 /* Z = sqrt(1 − x² − y²) is reconstructed */
|
||
#define YAPF_NORMAL_OCT 0x08 /* octahedral */
|
||
|
||
/* primaries / transfer (0 = unspecified: the sRGB flag decides) */
|
||
#define YAPF_PRIMARIES_UNSPECIFIED 0
|
||
#define YAPF_PRIMARIES_BT709 1 /* = sRGB */
|
||
#define YAPF_PRIMARIES_DISPLAY_P3 2
|
||
#define YAPF_PRIMARIES_BT2020 3
|
||
#define YAPF_PRIMARIES_ACES_AP0 4
|
||
#define YAPF_PRIMARIES_ACES_AP1 5 /* ACEScg */
|
||
#define YAPF_PRIMARIES_ADOBE_RGB 6
|
||
|
||
#define YAPF_TRANSFER_UNSPECIFIED 0
|
||
#define YAPF_TRANSFER_SRGB 1
|
||
#define YAPF_TRANSFER_LINEAR 2
|
||
#define YAPF_TRANSFER_GAMMA22 3
|
||
#define YAPF_TRANSFER_PQ 4 /* SMPTE ST 2084 */
|
||
#define YAPF_TRANSFER_HLG 5
|
||
#define YAPF_TRANSFER_BT1886 6 /* gamma 2.4 */
|
||
|
||
/* mip filter (bits 0–6) | YAPF_MIP_LINEAR_LIGHT */
|
||
#define YAPF_MIP_UNKNOWN 0
|
||
#define YAPF_MIP_BOX 1
|
||
#define YAPF_MIP_TENT 2
|
||
#define YAPF_MIP_KAISER 3
|
||
#define YAPF_MIP_LANCZOS 4
|
||
#define YAPF_MIP_MITCHELL 5
|
||
#define YAPF_MIP_AUTHORED 6 /* made by hand / by an external tool */
|
||
#define YAPF_MIP_LINEAR_LIGHT 0x80 /* sRGB channels were filtered in linear light */
|
||
|
||
/* load flags */
|
||
#define YAPF_LOAD_TRUST 0x01 /* skip checksum verification */
|
||
|
||
/* encode flags */
|
||
#define YAPF_ENC_ENTROPY 0x01 /* allow entropy-coded planes and packed
|
||
blocks where they are smaller: ~10 %
|
||
smaller photos, ~3× smaller GPU blocks
|
||
on disk; slower to decode */
|
||
|
||
/* A note (metadata entry). Well-known tags: "AUTH" author, "LICN"
|
||
* licence, "SRCE" source, "TOOL" creating tool, "TITL" title, "DESC"
|
||
* description, "DATE" ISO 8601 date (all UTF-8 text); "ICCP" ICC profile;
|
||
* "ORNT" orientation (1 byte); "DPI " 2 × u32 milli-dots per inch;
|
||
* "CLL " 2 × u16 max content / max frame-average light level (cd/m²).
|
||
* Tags that start with a lowercase letter belong to applications. */
|
||
typedef struct {
|
||
char tag[4];
|
||
uint32_t size;
|
||
uint8_t *data;
|
||
} yapf_note_t;
|
||
|
||
/*
|
||
* A version 2 texture. Surfaces are numbered
|
||
* s = ((frame × layers + layer) × faces + face) × mip_levels + mip
|
||
* (faces = 6 for cubes, else 1) and data[s] holds the whole surface:
|
||
* pixels: width_m × height_m × depth_m × channels samples, rows top to
|
||
* bottom, slices in order; samples in the native byte order
|
||
* (uint8_t, uint16_t, half bits as uint16_t, float);
|
||
* blocks: ceil(width_m / bw) × ceil(height_m / bh) blocks per slice,
|
||
* row-major — exactly what glCompressedTexImage / Vulkan take.
|
||
* width_m = max(1, width >> m), likewise height and (volumes) depth.
|
||
*/
|
||
typedef struct {
|
||
uint32_t width, height, depth; /* depth: volumes only, else 1 */
|
||
uint16_t layers; /* array layers, >= 1 */
|
||
uint16_t frames; /* animation frames, >= 1 */
|
||
uint8_t shape; /* YAPF_SHAPE_* */
|
||
uint8_t mip_levels; /* 1–16 */
|
||
uint8_t payload; /* YAPF_PAYLOAD_* */
|
||
uint8_t format; /* YAPF_GPU_*: block format, or hint */
|
||
uint8_t sample; /* YAPF_SAMPLE_* */
|
||
uint8_t channels; /* 1–4 */
|
||
uint8_t flags; /* YAPF_FLAG_* */
|
||
uint8_t channel_map[4]; /* YAPF_CH_* per channel */
|
||
uint8_t linear_mask; /* bit c: channel c is linear even in
|
||
an sRGB image (e.g. alpha, masks) */
|
||
uint8_t normal; /* YAPF_NORMAL_* */
|
||
uint8_t primaries, transfer; /* YAPF_PRIMARIES_*, YAPF_TRANSFER_* */
|
||
uint8_t mip_filter; /* YAPF_MIP_* */
|
||
uint8_t alpha_ref; /* coverage reference (with COVERAGE) */
|
||
uint16_t loop_count; /* animation loops, 0 = forever */
|
||
uint32_t *frame_us; /* frames durations in µs, or NULL */
|
||
uint32_t note_count;
|
||
yapf_note_t *notes;
|
||
void **data; /* one pointer per surface */
|
||
void *internal_; /* library use: file tables, ownership */
|
||
} yapf_texture_t;
|
||
|
||
/* ── Public API ──────────────────────────────────────────────────────── */
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
/*
|
||
* yapf_load — decode a .yapf file.
|
||
* Returns NULL on any failure (bad magic, truncated data, OOM, I/O error).
|
||
* The returned image must be released with yapf_free().
|
||
*/
|
||
yapf_image_t *yapf_load(const char *filename);
|
||
|
||
/*
|
||
* yapf_load_memory — decode a .yapf file already in memory (e.g. from a
|
||
* game archive). Same ownership and failure rules as yapf_load().
|
||
*/
|
||
yapf_image_t *yapf_load_memory(const void *buffer, size_t size);
|
||
|
||
/*
|
||
* yapf_load_mt / yapf_load_memory_mt — same as above, but decode tiles on
|
||
* up to `threads` threads (0 = one per CPU core). Output is identical.
|
||
* Without thread support (YAPF_NO_THREADS, or an unknown platform) these
|
||
* decode on the calling thread.
|
||
*/
|
||
yapf_image_t *yapf_load_mt(const char *filename, int threads);
|
||
yapf_image_t *yapf_load_memory_mt(const void *buffer, size_t size, int threads);
|
||
|
||
/*
|
||
* yapf_save — encode an image to disk.
|
||
* img->mip_levels controls how many levels are written; set to 1 to write
|
||
* only the base level. img->mips may be NULL when mip_levels == 1.
|
||
* Returns YAPF_OK or a negative YAPF_ERR_* code.
|
||
*/
|
||
int yapf_save(const char *filename, const yapf_image_t *img);
|
||
|
||
/*
|
||
* yapf_encode — encode an image into memory.
|
||
* On success *out_data points to *out_size bytes of a complete .yapf file;
|
||
* release it with yapf_free_buffer(). Same rules and return codes as
|
||
* yapf_save() (no YAPF_ERR_IO).
|
||
*/
|
||
int yapf_encode(const yapf_image_t *img, void **out_data, size_t *out_size);
|
||
|
||
/* yapf_free_buffer — release memory returned by yapf_encode(). NULL-safe. */
|
||
void yapf_free_buffer(void *data);
|
||
|
||
/*
|
||
* yapf_free — release all memory owned by an image returned by yapf_load().
|
||
* NULL-safe.
|
||
*/
|
||
void yapf_free(yapf_image_t *img);
|
||
|
||
/*
|
||
* The v1 functions above also read version 2 files that hold 8-bit pixels
|
||
* of a 2D, cube or array texture: they return frame 0, layer 0, face 0
|
||
* with all its mips. yapf_save / yapf_encode always write version 1.
|
||
*/
|
||
|
||
/* ── Version 2: textures ─────────────────────────────────────────────── */
|
||
|
||
/*
|
||
* yapf_tex_create — a library-owned texture with the description of
|
||
* `desc` (every field up to loop_count; notes, frame times and data are
|
||
* not copied) and zeroed storage for every surface. frame_us is
|
||
* allocated (zeroed) when frames > 1. NULL on invalid description / OOM.
|
||
*/
|
||
yapf_texture_t *yapf_tex_create(const yapf_texture_t *desc);
|
||
|
||
/* yapf_tex_free — release a texture made by the library. NULL-safe. */
|
||
void yapf_tex_free(yapf_texture_t *tex);
|
||
|
||
/* Surface helpers. */
|
||
uint32_t yapf_tex_faces(const yapf_texture_t *tex); /* 6 or 1 */
|
||
uint32_t yapf_tex_surface_count(const yapf_texture_t *tex);
|
||
uint32_t yapf_tex_surface(const yapf_texture_t *tex, uint32_t frame,
|
||
uint32_t layer, uint32_t face, uint32_t mip);
|
||
void yapf_tex_mip_size(const yapf_texture_t *tex, uint32_t mip,
|
||
uint32_t *w, uint32_t *h, uint32_t *d);
|
||
size_t yapf_tex_surface_bytes(const yapf_texture_t *tex, uint32_t mip);
|
||
size_t yapf_sample_bytes(uint8_t sample); /* 1, 2 or 4 */
|
||
|
||
/* Notes: add (library-owned textures only; data is copied) and find. */
|
||
int yapf_tex_add_note(yapf_texture_t *tex, const char tag[4],
|
||
const void *data, uint32_t size);
|
||
const yapf_note_t *yapf_tex_find_note(const yapf_texture_t *tex, const char tag[4]);
|
||
|
||
/*
|
||
* yapf_tex_encode / yapf_tex_save — write a version 2 file. `flags` is
|
||
* YAPF_ENC_*. Works on library-owned and caller-built textures alike
|
||
* (nothing is taken over). Returns YAPF_OK or a YAPF_ERR_* code.
|
||
*/
|
||
int yapf_tex_encode(const yapf_texture_t *tex, int flags,
|
||
void **out_data, size_t *out_size);
|
||
int yapf_tex_save(const char *filename, const yapf_texture_t *tex, int flags);
|
||
|
||
/*
|
||
* yapf_tex_load / yapf_tex_load_memory — read a version 1 or 2 file and
|
||
* decode every surface on up to `threads` threads (0 = one per core).
|
||
* `flags` is YAPF_LOAD_*. On failure NULL is returned and *err (if not
|
||
* NULL) receives the YAPF_ERR_* reason.
|
||
*/
|
||
yapf_texture_t *yapf_tex_load(const char *filename, int flags, int threads, int *err);
|
||
yapf_texture_t *yapf_tex_load_memory(const void *buffer, size_t size,
|
||
int flags, int threads, int *err);
|
||
|
||
/*
|
||
* Streaming and partial decoding.
|
||
*
|
||
* yapf_tex_open — read only the header, tables and notes. `avail` may be
|
||
* less than the whole file (a download in progress): it must cover the
|
||
* front matter, else YAPF_ERR_TRUNCATED. All data[] pointers are NULL.
|
||
*
|
||
* yapf_tex_stream_bytes — how many leading bytes of the file hold every
|
||
* surface of mip `first_mip` and smaller (the file stores small mips
|
||
* first, so these are a usable texture on their own).
|
||
*
|
||
* yapf_tex_decode_surface — decode surface s into tex->data[s]
|
||
* (allocated). `buffer`/`avail` is the file or a prefix of it; returns
|
||
* YAPF_ERR_TRUNCATED when the surface is not in it yet.
|
||
*
|
||
* yapf_tex_tile_grid / yapf_tex_decode_tile — virtual texturing: the
|
||
* surfaces of mip m are cut into tiles (64 × 64 pixels, or 16 × 16
|
||
* blocks), tx across, ty down, per slice. decode_tile writes one tile
|
||
* into `out`, rows `stride` bytes apart, and reports its pixel rectangle.
|
||
*/
|
||
yapf_texture_t *yapf_tex_open(const void *buffer, size_t avail, int flags, int *err);
|
||
uint64_t yapf_tex_stream_bytes(const yapf_texture_t *tex, uint32_t first_mip);
|
||
int yapf_tex_decode_surface(yapf_texture_t *tex, const void *buffer, size_t avail,
|
||
uint32_t surface, int threads);
|
||
void yapf_tex_tile_grid(const yapf_texture_t *tex, uint32_t mip,
|
||
uint32_t *tiles_x, uint32_t *tiles_y);
|
||
int yapf_tex_decode_tile(const yapf_texture_t *tex, const void *buffer, size_t avail,
|
||
uint32_t surface, uint32_t tx, uint32_t ty, uint32_t slice,
|
||
void *out, size_t stride,
|
||
uint32_t *x, uint32_t *y, uint32_t *w, uint32_t *h);
|
||
|
||
/*
|
||
* yapf_tex_build_mips — replace the mips of a library-owned pixel texture
|
||
* with a chain made from mip 0: `levels` levels (0 = down to 1 × 1).
|
||
* 2 × 2 (× 2 for volumes) box filter; sRGB channels are averaged in
|
||
* linear light. alpha_ref > 0 rescales alpha on every mip so the share
|
||
* of texels above alpha_ref stays that of mip 0 (fences and foliage do
|
||
* not thin out); it sets YAPF_FLAG_COVERAGE. Records the filter used.
|
||
*/
|
||
int yapf_tex_build_mips(yapf_texture_t *tex, uint32_t levels, uint8_t alpha_ref);
|
||
|
||
/* ── GPU blocks ──────────────────────────────────────────────────────── */
|
||
|
||
/* Block footprint and size of a YAPF_GPU_* block format; 0 if not one. */
|
||
int yapf_block_info(uint8_t format, uint32_t *block_w, uint32_t *block_h,
|
||
uint32_t *block_bytes);
|
||
|
||
/*
|
||
* yapf_tex_compress — a new library-owned texture holding `src` (pixel
|
||
* payload) as GPU blocks. Built-in encoders: BC1, BC3, BC4, BC5, BC7 and
|
||
* BC6H (unsigned). LDR formats take 8-bit pixels; BC6H takes 16-bit,
|
||
* half or float pixels. For other formats bring blocks from your own
|
||
* encoder: a payload = BLOCKS texture with data[] filled.
|
||
*/
|
||
int yapf_tex_compress(const yapf_texture_t *src, uint8_t format,
|
||
yapf_texture_t **out);
|
||
|
||
/*
|
||
* yapf_blocks_decode — decode a w × h image of BC1–BC7 blocks (previews,
|
||
* tools). LDR formats write RGBA8 (BC4 / BC5: R / RG, then 0, 255;
|
||
* snorm values shifted by +128); BC6H writes RGB half floats. Returns
|
||
* YAPF_ERR_UNSUPPORTED for ETC2 / EAC / ASTC.
|
||
*/
|
||
int yapf_blocks_decode(uint8_t format, const void *blocks, uint32_t w, uint32_t h,
|
||
void *out);
|
||
|
||
/*
|
||
* yapf_tex_to_rgba8 — one slice of a decoded surface as 8-bit RGBA for
|
||
* display (thumbnails, previews): gray is expanded, 16-bit is rounded to
|
||
* 8 bits, float and half are clamped to 0..1 and sRGB encoded (they are
|
||
* linear), GPU blocks are decoded. out holds w × h × 4 bytes of that
|
||
* mip. Returns YAPF_ERR_UNSUPPORTED for ETC2 / EAC / ASTC blocks.
|
||
*/
|
||
int yapf_tex_to_rgba8(const yapf_texture_t *tex, uint32_t surface, uint32_t slice,
|
||
uint8_t *out);
|
||
|
||
/* yapf-sum, the checksum used by version 2 files. */
|
||
uint32_t yapf_checksum(const void *data, size_t size);
|
||
|
||
/* Half float conversion helpers. */
|
||
uint16_t yapf_float_to_half(float f);
|
||
float yapf_half_to_float(uint16_t h);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* YAPF_H */
|