Add files via upload

This commit is contained in:
Nexoniarz 2026-06-20 15:03:21 +02:00 • committed by GitHub
commit b9b8c93a22
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 1060 additions and 0 deletions

262
README.md Normal file
View file

@ -0,0 +1,262 @@
# YAPF — Yet Another Picture Format
**Fast. Small. Easy.**
YAPF is a lossless image codec built around a single principle: decoding should
be as close to a memory copy as possible. Instead of running a general-purpose
compressor like DEFLATE over pixel data, YAPF encodes images with five cheap
operations — a pixel cache lookup, two flavours of delta encoding, run-length
encoding, and verbatim pixel storage — producing files that are smaller than
uncompressed formats while decoding nearly as fast as `memcpy`.
---
## Why YAPF?
Standard lossless formats like PNG compress well, but decompressing DEFLATE
streams is CPU-intensive. On large images (4K and above) or in
latency-sensitive applications (game engines, real-time pipelines, embedded
systems) this is a meaningful bottleneck.
YAPF trades a modest compression ratio advantage for dramatically lower decode
complexity. There are no Huffman tables to build, no sliding windows to
maintain, and no bit-level unpacking. The decoder is a tight byte-dispatch
loop that any modern CPU handles at memory bandwidth.
### Performance
Benchmark: loading a 5K professional photograph (5120 × 2880 px).
| Format | Load time |
| :----- | ----------: |
| **YAPF** | **0.111 s** |
| PNG | 0.194 s |
YAPF decodes approximately **1.7× faster** than PNG on the same hardware
without SIMD or threading.
### When YAPF excels
- **Pixel art, sprites, UI textures** — large flat regions of identical colour
compress to single RUN bytes; palette-like images benefit heavily from the
cache.
- **Screenshots and diagrams** — solid backgrounds and repeated gradients hit
DIFF/LUMA and the cache constantly.
- **Game asset pipelines** — load textures at startup without paying a
decompression tax every frame.
- **Embedded / resource-constrained targets** — the entire codec fits in ~200
lines of portable C with zero external dependencies.
---
## Integration
The core library is two files. Drop them into your project and compile:
```
yapf.h — public API and format documentation
yapf.c — encoder / decoder implementation
```
```c
#include "yapf.h"
int main(void) {
/* Load */
yapf_image_t *img = yapf_load("photo.yapf");
if (!img) { /* file not found or not a valid YAPF stream */ }
printf("%u x %u, %u channel(s)\n",
img->width, img->height, img->channels);
/* Save */
int rc = yapf_save("output.yapf", img);
if (rc != YAPF_OK) { /* write error */ }
yapf_free(img);
return 0;
}
```
Compile with any C99-capable compiler — no flags required for the baseline:
```sh
gcc -O2 -std=c99 main.c yapf.c -o myapp
```
---
## API Reference
### Types
```c
typedef struct {
uint32_t width; /* pixels per row */
uint32_t height; /* rows */
uint8_t channels; /* 1 gray / 2 gray+alpha / 3 RGB / 4 RGBA */
uint8_t *pixels; /* raw pixel buffer, malloc-owned */
} yapf_image_t;
```
`pixels` is a tightly packed, row-major byte array of `width × height × channels`
bytes. The layout per pixel matches the `channels` value:
| `channels` | Layout per pixel |
| :--------- | :--------------- |
| 1 | `Y` |
| 2 | `Y A` |
| 3 | `R G B` |
| 4 | `R G B A` |
### Functions
```c
yapf_image_t *yapf_load(const char *filename);
```
Decodes a YAPF file from disk. Returns a heap-allocated `yapf_image_t` on
success, `NULL` on any failure (file not found, invalid magic, unsupported
version, out of memory). Free with `yapf_free()`.
```c
int yapf_save(const char *filename, const yapf_image_t *img);
```
Encodes an image to disk. Returns `YAPF_OK` (0) on success or a negative
`YAPF_ERR_*` code on failure.
```c
void yapf_free(yapf_image_t *img);
```
Releases memory returned by `yapf_load()`. Passing `NULL` is safe.
### Return codes
| Constant | Value | Meaning |
| :---------------- | ----: | :----------------------------------- |
| `YAPF_OK` | 0 | Success |
| `YAPF_ERR_INVALID`| –1 | NULL or logically invalid argument |
| `YAPF_ERR_IO` | –2 | File could not be opened or written |
### Channel constants
```c
YAPF_CHANNELS_GRAY 1
YAPF_CHANNELS_GRAY_ALPHA 2
YAPF_CHANNELS_RGB 3
YAPF_CHANNELS_RGBA 4
```
---
## Building the libraries
The build scripts uses the [LLVM](https://releases.llvm.org/) compiler
```sh
# Unix (Linux / macOS)
chmod +x build.sh
./build.sh
# Windows
build.bat
```
Output goes to `dist/` with one subdirectory per platform:
```
dist/
├── windows-x64/ yapf.dll yapf.lib
├── windows-arm64/ yapf.dll yapf.lib
├── macos-x64/ libyapf.dylib
├── macos-arm64/ libyapf.dylib
├── linux-x64/ libyapf.so libyapf.a
└── linux-arm64/ libyapf.so libyapf.a
```
### Optional build flags
| Flag | Effect |
| :---------------- | :-------------------------------------------------- |
| `-DYAPF_USE_SIMD` | Reserved for future SIMD acceleration paths |
The interactive build script will prompt for these options.
---
## Technical Specification
### File header — 14 bytes
| Offset | Size | Type | Description |
| :----- | ---: | :--------- | :-------------------------------------- |
| 0x00 | 4 | `char[4]` | Magic: `"YAPF"` |
| 0x04 | 1 | `uint8_t` | Format version (currently `1`) |
| 0x05 | 4 | `uint32_t` | Width in pixels, little-endian |
| 0x09 | 4 | `uint32_t` | Height in pixels, little-endian |
| 0x0D | 1 | `uint8_t` | Channel count: 1 / 2 / 3 / 4 |
All multi-byte integers are always stored little-endian, regardless of host
architecture.
### Byte-stream opcodes
The payload begins immediately after the header and is decoded left-to-right.
A 64-slot pixel cache (zero-initialised) and a running "previous pixel" state
(`r=0, g=0, b=0, a=255`) are maintained throughout.
| Byte range | Opcode | Size | Description |
| :---------- | :----- | ------: | :-------------------------------------------- |
| 0x00–0x3F | INDEX | 1 byte | Restore pixel from cache slot `byte & 0x3F` |
| 0x40–0x7F | DIFF | 1 byte | Per-channel deltas: dr = bits[5:4]–2, dg = bits[3:2]–2, db = bits[1:0]–2. Range –2..+1. Alpha unchanged. |
| 0x80–0xBF | LUMA | 2 bytes | Green delta dg = (byte & 0x3F)–32. Second byte: dr = ((b1>>4)&0xF)–8+dg, db = (b1&0xF)–8+dg. Alpha unchanged. |
| 0xC0–0xFD | RUN | 1 byte | Repeat previous pixel (byte&0x3F)+1 times (1–62). Cache not updated. |
| 0xFE | RGB | 4 bytes | Next 3 bytes = R, G, B. Alpha unchanged. |
| 0xFF | RGBA | 5 bytes | Next 4 bytes = R, G, B, A. |
After every non-RUN opcode, the cache slot `HASH(r,g,b,a)` is updated and the
pixel becomes the new previous pixel.
```
HASH(r,g,b,a) = (r×3 + g×5 + b×7 + a×11) mod 64
```
The stream ends when exactly `width × height` pixels have been decoded; no
end-of-stream marker is required.
### Pixel buffer layout
Within the decoded buffer, each pixel occupies `channels` consecutive bytes in
the order shown in the API table above. Grayscale images store a single
luminance byte per pixel; the encoder internally promotes gray values to
`r = g = b = Y` for uniform opcode selection.
---
## GIMP Plugin
A plugin for GIMP is located in `extensions/gimp/`. See the
[build instructions](extensions/gimp/howtocompile.txt) for details.
Supported colour modes: Gray, Gray+Alpha, RGB, RGBA.
File extension: `.yapf`. MIME type: `image/x-yapf`.
---
## Platforms
| Platform | Architectures | Library format |
| :------------- | :------------ | :------------------ |
| Windows | x64, ARM64 | `.dll` + `.lib` |
| macOS | ARM64 | `.dylib` |
| Linux (glibc) | x64, ARM64 | `.so` |
| Linux (musl) | x64, ARM64 | `.a` (static) |
The core library (`yapf.c` / `yapf.h`) uses only the C99 standard library
(`<stdio.h>`, `<stdlib.h>`, `<string.h>`, `<stdint.h>`) and compiles cleanly
on any conforming C99 or C++ toolchain with no configuration required.
---
## License
Apache License 2.0 — see [LICENSE](LICENSE).