/* * yapf.c — Yet Another Picture Format * Encoder and decoder implementation. * * 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. */ #include "yapf.h" #include #include #include /* ── Constants ───────────────────────────────────────────────────────── */ #define YAPF__MAGIC "YAPF" #define YAPF__VERSION 2 #define YAPF__HDR_LEN 20 #define YAPF__MIP_ENTRY 8 #define YAPF__MAX_TILE_PX (YAPF_TILE_SIZE * YAPF_TILE_SIZE) /* 1024 */ /* ── Tile pixel type ─────────────────────────────────────────────────── */ /* * After YCoCg-R, channels 1 (Co) and 2 (Cg) span [-255, 255] and need * 9 bits, so we use int16_t for all channels internally within a tile. * Grayscale and alpha channels stay in [0, 255]. */ typedef struct { int16_t v[4]; } yapf__tpx; /* ── I/O helpers ─────────────────────────────────────────────────────── */ static uint32_t yapf__r32le(const uint8_t *p) { return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24); } static void yapf__w32le(uint8_t *p, uint32_t v) { p[0] = (uint8_t)(v); p[1] = (uint8_t)(v >> 8); p[2] = (uint8_t)(v >> 16); p[3] = (uint8_t)(v >> 24); } /* ── Bit-writer ──────────────────────────────────────────────────────── */ typedef struct { uint8_t *buf; size_t cap; size_t len; uint8_t acc; /* accumulator */ int bits; /* bits filled in acc (0-7) */ } yapf__bw; static int yapf__bw_init(yapf__bw *w, size_t cap) { w->buf = (uint8_t *)malloc(cap ? cap : 64); if (!w->buf) return -1; w->cap = cap ? cap : 64; w->len = 0; w->acc = 0; w->bits = 0; return 0; } static int yapf__bw_grow(yapf__bw *w, size_t need) { if (w->len + need <= w->cap) return 0; size_t nc = w->cap * 2 + need; uint8_t *nb = (uint8_t *)realloc(w->buf, nc); if (!nb) return -1; w->buf = nb; w->cap = nc; return 0; } /* Write n bits from v, MSB first; n in [1, 32]. */ static int yapf__bw_put(yapf__bw *w, uint32_t v, int n) { if (yapf__bw_grow(w, (size_t)(n + 7) / 8 + 2) != 0) return -1; for (int i = n - 1; i >= 0; i--) { w->acc = (uint8_t)((w->acc << 1) | ((v >> i) & 1u)); if (++w->bits == 8) { w->buf[w->len++] = w->acc; w->acc = 0; w->bits = 0; } } return 0; } /* Flush partial byte (zero-padded) and reset to byte boundary. */ static void yapf__bw_flush(yapf__bw *w) { if (w->bits > 0) { w->buf[w->len++] = (uint8_t)(w->acc << (8 - w->bits)); w->acc = 0; w->bits = 0; } } /* ── Bit-reader ──────────────────────────────────────────────────────── */ typedef struct { const uint8_t *buf; size_t len; size_t pos; uint8_t acc; int bits; } yapf__br; static void yapf__br_init(yapf__br *r, const uint8_t *buf, size_t len) { r->buf = buf; r->len = len; r->pos = 0; r->acc = 0; r->bits = 0; } /* Read n bits MSB-first into *out. Returns 0 on success, -1 on underflow. */ static int yapf__br_get(yapf__br *r, int n, uint32_t *out) { uint32_t v = 0; for (int i = 0; i < n; i++) { if (r->bits == 0) { if (r->pos >= r->len) return -1; r->acc = r->buf[r->pos++]; r->bits = 8; } v = (v << 1) | ((r->acc >> 7) & 1u); r->acc = (uint8_t)(r->acc << 1); r->bits--; } *out = v; return 0; } /* Discard remaining bits in current byte (tile boundary alignment). */ static void yapf__br_align(yapf__br *r) { r->acc = 0; r->bits = 0; } /* ── YCoCg-R transform ───────────────────────────────────────────────── */ /* * Lossless, integer, fully reversible. * Y ∈ [0,255], Co ∈ [-255,255], Cg ∈ [-255,255]. * Alpha passes through unchanged. */ static void yapf__forward(yapf__tpx *dst, const uint8_t *src, int ch) { if (ch >= 3) { int r = src[0], g = src[1], b = src[2]; int co = r - b; int t = b + (co >> 1); int cg = g - t; int y = t + (cg >> 1); dst->v[0] = (int16_t)y; dst->v[1] = (int16_t)co; dst->v[2] = (int16_t)cg; if (ch == 4) dst->v[3] = (int16_t)src[3]; } else { for (int c = 0; c < ch; c++) dst->v[c] = (int16_t)src[c]; } } static void yapf__inverse(uint8_t *dst, const yapf__tpx *src, int ch) { if (ch >= 3) { int y = src->v[0]; int co = src->v[1]; int cg = src->v[2]; int t = y - (cg >> 1); int g = cg + t; int b = t - (co >> 1); int r = b + co; dst[0] = (uint8_t)(r < 0 ? 0 : r > 255 ? 255 : r); dst[1] = (uint8_t)(g < 0 ? 0 : g > 255 ? 255 : g); dst[2] = (uint8_t)(b < 0 ? 0 : b > 255 ? 255 : b); if (ch == 4) dst[3] = (uint8_t)src->v[3]; } else { for (int c = 0; c < ch; c++) dst[c] = (uint8_t)src->v[c]; } } /* ── Predictor ───────────────────────────────────────────────────────── */ /* left + above - diag, unclipped (works for signed int16 channels). */ static int32_t yapf__predict(int16_t L, int16_t A, int16_t D) { return (int32_t)L + (int32_t)A - (int32_t)D; } /* ── Signed bit width ────────────────────────────────────────────────── */ /* Minimum two's-complement bits to represent v. Returns 0 if v == 0. */ static int yapf__sbits(int v) { if (v == 0) return 0; if (v < 0) v = ~v; /* largest positive needing same width */ int b = 1; /* sign bit */ while (v > 0) { v >>= 1; b++; } return b; } /* First-pixel bit widths: Co (ch1) and Cg (ch2) need 9 bits each because * YCoCg-R maps them to [-255, 255]; all other channels stay at 8 bits. */ static int yapf__first_bits(int c, int ch) { return (ch >= 3 && (c == 1 || c == 2)) ? 9 : 8; } /* ── Tile encode ─────────────────────────────────────────────────────── */ static int yapf__encode_tile( yapf__bw *w, const uint8_t *pixels, uint32_t img_w, uint32_t tx, uint32_t ty, uint32_t tw, uint32_t th, int ch) { yapf__tpx raw[YAPF__MAX_TILE_PX]; int max_abs[4] = {0, 0, 0, 0}; int bd[4]; size_t total = (size_t)tw * th; /* Gather + transform */ for (uint32_t y = 0; y < th; y++) { for (uint32_t x = 0; x < tw; x++) { const uint8_t *src = pixels + ((size_t)(ty + y) * img_w + (tx + x)) * (size_t)ch; yapf__forward(&raw[y * tw + x], src, ch); } } /* Compute residuals and track max |residual| per channel. * Pixel (0,0) is stored raw; all others use 2-D prediction. */ for (size_t i = 1; i < total; i++) { uint32_t x = (uint32_t)(i % tw); uint32_t y = (uint32_t)(i / tw); for (int c = 0; c < ch; c++) { int16_t L = (x > 0) ? raw[y * tw + (x-1)].v[c] : 0; int16_t A = (y > 0) ? raw[(y-1)*tw + x].v[c] : 0; int16_t D = (x > 0 && y > 0) ? raw[(y-1)*tw + (x-1)].v[c] : 0; int32_t pred = yapf__predict(L, A, D); int res = (int)raw[i].v[c] - (int)pred; int ab = res < 0 ? -res : res; if (ab > max_abs[c]) max_abs[c] = ab; } } /* Write per-channel residual bit depths */ for (int c = 0; c < ch; c++) { bd[c] = yapf__sbits(max_abs[c]); if (yapf__bw_put(w, (uint32_t)bd[c], 8) != 0) return -1; } /* Write first pixel with channel-appropriate width */ for (int c = 0; c < ch; c++) { int nbits = yapf__first_bits(c, ch); uint32_t bits = (uint32_t)((int32_t)raw[0].v[c] & ((1 << nbits) - 1)); if (yapf__bw_put(w, bits, nbits) != 0) return -1; } /* Write packed residuals for pixels 1 .. total-1 */ for (size_t i = 1; i < total; i++) { uint32_t x = (uint32_t)(i % tw); uint32_t y = (uint32_t)(i / tw); for (int c = 0; c < ch; c++) { if (bd[c] == 0) continue; int16_t L = (x > 0) ? raw[y * tw + (x-1)].v[c] : 0; int16_t A = (y > 0) ? raw[(y-1)*tw + x].v[c] : 0; int16_t D = (x > 0 && y > 0) ? raw[(y-1)*tw + (x-1)].v[c] : 0; int32_t pred = yapf__predict(L, A, D); int res = (int)raw[i].v[c] - (int)pred; uint32_t bits = (uint32_t)(res & ((1 << bd[c]) - 1)); if (yapf__bw_put(w, bits, bd[c]) != 0) return -1; } } yapf__bw_flush(w); return 0; } /* ── Tile decode ─────────────────────────────────────────────────────── */ static int yapf__decode_tile( yapf__br *r, uint8_t *pixels, uint32_t img_w, uint32_t tx, uint32_t ty, uint32_t tw, uint32_t th, int ch) { yapf__tpx raw[YAPF__MAX_TILE_PX]; int bd[4]; uint32_t bits; size_t total = (size_t)tw * th; /* Read residual bit depths */ for (int c = 0; c < ch; c++) { if (yapf__br_get(r, 8, &bits) != 0) return -1; bd[c] = (int)bits; } /* Read first pixel. * 8-bit channels (Y, A, grayscale) are unsigned [0,255] — no sign-extend. * 9-bit channels (Co, Cg) are signed [-255,255] — two's complement. */ for (int c = 0; c < ch; c++) { int nbits = yapf__first_bits(c, ch); if (yapf__br_get(r, nbits, &bits) != 0) return -1; int v; if (nbits == 9) { v = (int)(bits & 0xFFu); if (bits & 0x100u) v -= 256; } else { v = (int)bits; /* unsigned: 0–255 */ } raw[0].v[c] = (int16_t)v; } /* Decode pixels 1 .. total-1 */ for (size_t i = 1; i < total; i++) { uint32_t x = (uint32_t)(i % tw); uint32_t y = (uint32_t)(i / tw); for (int c = 0; c < ch; c++) { int16_t L = (x > 0) ? raw[y * tw + (x-1)].v[c] : 0; int16_t A = (y > 0) ? raw[(y-1)*tw + x].v[c] : 0; int16_t D = (x > 0 && y > 0) ? raw[(y-1)*tw + (x-1)].v[c] : 0; int32_t pred = yapf__predict(L, A, D); int res = 0; if (bd[c] > 0) { if (yapf__br_get(r, bd[c], &bits) != 0) return -1; int sb = 1 << (bd[c] - 1); res = (int)(bits & (uint32_t)(sb - 1)); if (bits & (uint32_t)sb) res -= sb; } raw[i].v[c] = (int16_t)((int)pred + res); } } yapf__br_align(r); /* Inverse transform and write to output buffer */ for (uint32_t y = 0; y < th; y++) { for (uint32_t x = 0; x < tw; x++) { uint8_t *dst = pixels + ((size_t)(ty + y) * img_w + (tx + x)) * (size_t)ch; yapf__inverse(dst, &raw[y * tw + x], ch); } } return 0; } /* ── Level encode / decode ───────────────────────────────────────────── */ static int yapf__encode_level( yapf__bw *w, const uint8_t *pixels, uint32_t mw, uint32_t mh, int ch) { uint32_t tx_count = (mw + YAPF_TILE_SIZE - 1) / YAPF_TILE_SIZE; uint32_t ty_count = (mh + YAPF_TILE_SIZE - 1) / YAPF_TILE_SIZE; for (uint32_t ty = 0; ty < ty_count; ty++) { for (uint32_t tx = 0; tx < tx_count; tx++) { uint32_t tw = (tx + 1) * YAPF_TILE_SIZE <= mw ? YAPF_TILE_SIZE : mw - tx * YAPF_TILE_SIZE; uint32_t th = (ty + 1) * YAPF_TILE_SIZE <= mh ? YAPF_TILE_SIZE : mh - ty * YAPF_TILE_SIZE; if (yapf__encode_tile(w, pixels, mw, tx * YAPF_TILE_SIZE, ty * YAPF_TILE_SIZE, tw, th, ch) != 0) return -1; } } return 0; } static int yapf__decode_level( const uint8_t *data, size_t data_len, uint8_t *pixels, uint32_t mw, uint32_t mh, int ch) { yapf__br r; yapf__br_init(&r, data, data_len); uint32_t tx_count = (mw + YAPF_TILE_SIZE - 1) / YAPF_TILE_SIZE; uint32_t ty_count = (mh + YAPF_TILE_SIZE - 1) / YAPF_TILE_SIZE; for (uint32_t ty = 0; ty < ty_count; ty++) { for (uint32_t tx = 0; tx < tx_count; tx++) { uint32_t tw = (tx + 1) * YAPF_TILE_SIZE <= mw ? YAPF_TILE_SIZE : mw - tx * YAPF_TILE_SIZE; uint32_t th = (ty + 1) * YAPF_TILE_SIZE <= mh ? YAPF_TILE_SIZE : mh - ty * YAPF_TILE_SIZE; if (yapf__decode_tile(&r, pixels, mw, tx * YAPF_TILE_SIZE, ty * YAPF_TILE_SIZE, tw, th, ch) != 0) return -1; } } return 0; } /* ======================================================================== * yapf_load * ======================================================================== */ yapf_image_t *yapf_load(const char *filename) { if (!filename) return NULL; FILE *f = fopen(filename, "rb"); if (!f) return NULL; uint8_t hdr[YAPF__HDR_LEN]; if (fread(hdr, 1, YAPF__HDR_LEN, f) != YAPF__HDR_LEN) goto fail_f; if (memcmp(hdr, YAPF__MAGIC, 4) != 0 || hdr[4] != YAPF__VERSION) goto fail_f; uint8_t channels = hdr[5]; uint8_t gpu_fmt = hdr[6]; uint8_t mip_levels = hdr[7]; uint8_t flags = hdr[8]; uint32_t base_w = yapf__r32le(hdr + 12); uint32_t base_h = yapf__r32le(hdr + 16); if (channels < 1 || channels > 4) goto fail_f; if (mip_levels < 1 || mip_levels > YAPF_MAX_MIPS) goto fail_f; if (base_w == 0 || base_w > YAPF_MAX_DIM) goto fail_f; if (base_h == 0 || base_h > YAPF_MAX_DIM) goto fail_f; /* Mip index */ size_t idx_bytes = (size_t)mip_levels * YAPF__MIP_ENTRY; uint8_t *mip_idx = (uint8_t *)malloc(idx_bytes); if (!mip_idx) goto fail_f; if (fread(mip_idx, 1, idx_bytes, f) != idx_bytes) { free(mip_idx); goto fail_f; } yapf_image_t *img = (yapf_image_t *)calloc(1, sizeof(yapf_image_t)); if (!img) { free(mip_idx); goto fail_f; } img->width = base_w; img->height = base_h; img->channels = channels; img->gpu_format = gpu_fmt; img->flags = flags; img->mip_levels = mip_levels; img->mips = (uint8_t **)calloc(mip_levels, sizeof(uint8_t *)); if (!img->mips) { free(mip_idx); goto fail_img; } for (uint8_t m = 0; m < mip_levels; m++) { uint32_t offset = yapf__r32le(mip_idx + m * YAPF__MIP_ENTRY); uint32_t size = yapf__r32le(mip_idx + m * YAPF__MIP_ENTRY + 4); uint32_t mw = base_w >> m; if (mw < 1) mw = 1; uint32_t mh = base_h >> m; if (mh < 1) mh = 1; uint8_t *data = (uint8_t *)malloc(size); if (!data) { free(mip_idx); goto fail_mips; } if (fseek(f, (long)offset, SEEK_SET) != 0 || fread(data, 1, size, f) != size) { free(data); free(mip_idx); goto fail_mips; } size_t px_bytes = (size_t)mw * mh * channels; img->mips[m] = (uint8_t *)malloc(px_bytes); if (!img->mips[m]) { free(data); free(mip_idx); goto fail_mips; } if (yapf__decode_level(data, size, img->mips[m], mw, mh, channels) != 0) { free(data); free(mip_idx); goto fail_mips; } free(data); } free(mip_idx); img->pixels = img->mips[0]; fclose(f); return img; fail_mips: for (uint8_t m = 0; m < mip_levels; m++) free(img->mips[m]); free(img->mips); fail_img: free(img); fail_f: fclose(f); return NULL; } /* ======================================================================== * yapf_save * ======================================================================== */ int yapf_save(const char *filename, const yapf_image_t *img) { if (!filename || !img || !img->pixels) return YAPF_ERR_INVALID; if (img->width == 0 || img->width > YAPF_MAX_DIM) return YAPF_ERR_INVALID; if (img->height == 0 || img->height > YAPF_MAX_DIM) return YAPF_ERR_INVALID; if (img->channels < 1 || img->channels > 4) return YAPF_ERR_INVALID; if (img->mip_levels < 1 || img->mip_levels > YAPF_MAX_MIPS) return YAPF_ERR_INVALID; int ch = (int)img->channels; uint8_t mip_levels = img->mip_levels; /* Encode each mip level */ yapf__bw bws[YAPF_MAX_MIPS]; memset(bws, 0, sizeof(bws)); for (uint8_t m = 0; m < mip_levels; m++) { uint32_t mw = img->width >> m; if (mw < 1) mw = 1; uint32_t mh = img->height >> m; if (mh < 1) mh = 1; const uint8_t *src = (m == 0) ? img->pixels : (img->mips ? img->mips[m] : NULL); if (!src) { for (uint8_t j = 0; j < m; j++) free(bws[j].buf); return YAPF_ERR_INVALID; } size_t est = (size_t)mw * mh * (size_t)ch + 256; if (yapf__bw_init(&bws[m], est) != 0) { for (uint8_t j = 0; j < m; j++) free(bws[j].buf); return YAPF_ERR_OOM; } if (yapf__encode_level(&bws[m], src, mw, mh, ch) != 0) { for (uint8_t j = 0; j <= m; j++) free(bws[j].buf); return YAPF_ERR_OOM; } } /* Compute mip index offsets */ uint32_t offsets[YAPF_MAX_MIPS]; uint32_t cur = YAPF__HDR_LEN + (uint32_t)mip_levels * YAPF__MIP_ENTRY; for (uint8_t m = 0; m < mip_levels; m++) { offsets[m] = cur; cur += (uint32_t)bws[m].len; } FILE *f = fopen(filename, "wb"); if (!f) { for (uint8_t m = 0; m < mip_levels; m++) free(bws[m].buf); return YAPF_ERR_IO; } /* Header */ uint8_t hdr[YAPF__HDR_LEN]; memset(hdr, 0, sizeof(hdr)); memcpy(hdr, YAPF__MAGIC, 4); hdr[4] = YAPF__VERSION; hdr[5] = img->channels; hdr[6] = img->gpu_format; hdr[7] = mip_levels; hdr[8] = img->flags; yapf__w32le(hdr + 12, img->width); yapf__w32le(hdr + 16, img->height); fwrite(hdr, 1, YAPF__HDR_LEN, f); /* Mip index */ for (uint8_t m = 0; m < mip_levels; m++) { uint8_t entry[YAPF__MIP_ENTRY]; yapf__w32le(entry + 0, offsets[m]); yapf__w32le(entry + 4, (uint32_t)bws[m].len); fwrite(entry, 1, YAPF__MIP_ENTRY, f); } /* Payload */ int err = YAPF_OK; for (uint8_t m = 0; m < mip_levels; m++) { if (fwrite(bws[m].buf, 1, bws[m].len, f) != bws[m].len) err = YAPF_ERR_IO; free(bws[m].buf); } fclose(f); return err; } /* ======================================================================== * yapf_free * ======================================================================== */ void yapf_free(yapf_image_t *img) { if (!img) return; if (img->mips) { for (uint8_t m = 0; m < img->mip_levels; m++) free(img->mips[m]); free(img->mips); } free(img); }