mirror of
https://github.com/Nexoniarz/YAPF.git
synced 2026-11-03 04:54:26 +00:00
YAPF v1: fast byte-aligned format, SIMD, tools and plugins
Format (version 1, replaces the earlier experimental versions): - per-tile YCoCg-R or subtract-green colour transform, 8-bit modular - 12 row filters per 8-row band (Left, Gradient, Up-right, Up-average, Up 1-8 rows), none with more than a one-add serial dependency - residuals in groups of 8 at the group's width (w bits x 8 = w bytes), 4-bit control codes with repeat-previous-width runs chosen by DP - constant and stored tiles, independent 64x64 tiles, mips, GPU hints - all sizes validated before decoding; corrupt input is rejected Library: - SIMD on by default: SSSE3 (runtime CPUID check; GCC, Clang, MSVC, MinGW) and NEON on ARM64; -DYAPF_NO_SIMD for plain C - yapf_load_memory, yapf_load_mt, yapf_load_memory_mt - 6-10x faster decoding than PNG (stb_image) on one core; -O2 and -O3 builds perform the same - fixes for macOS (sysconf) and the Windows import library exports Tools and integrations: - `yapf` CLI: convert PNG/JPG/TGA/BMP/PSD/GIF <-> YAPF, info, bench, thumbnail, mips - JavaScript and Python bindings (byte-identical to the C encoder) - GIMP 3 plugin: exports the flattened image, optional mipmaps - Blender extension: import (also as plane), export, drag and drop - VS Code preview, Figma plugin, Photoshop scripts, Linux MIME type and thumbnailer - Makefile, updated build.sh / build.bat, tools/build_release.sh - tests/test_yapf.c (784 checks) and bindings/js/test.js Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
b9b8c93a22
commit
2ca726e3af
48 changed files with 14905 additions and 710 deletions
58
tools/build_release.sh
Executable file
58
tools/build_release.sh
Executable file
|
|
@ -0,0 +1,58 @@
|
|||
#!/usr/bin/env bash
|
||||
# Builds every release download into dist/release/ — for maintainers.
|
||||
#
|
||||
# tools/build_release.sh
|
||||
#
|
||||
# Needs: zig (cross-compiles all six platforms from one machine), python3,
|
||||
# and node + npx for the VS Code package. Outputs:
|
||||
# yapf-<platform>.zip command-line tool + shared/static library + header
|
||||
# yapf_image-blender.zip Blender extension with libraries for all platforms
|
||||
# yapf-preview.vsix VS Code extension
|
||||
# yapf-figma.zip Figma plugin (import via manifest.json)
|
||||
# yapf-photoshop.zip Photoshop scripts
|
||||
# yapf-gimp-source.zip GIMP plugin source (build with gimptool)
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
OUT=dist/release
|
||||
LIBS=dist/libs
|
||||
rm -rf "$OUT" "$LIBS"
|
||||
mkdir -p "$OUT" "$LIBS"
|
||||
|
||||
command -v zig >/dev/null || { echo "zig not found: https://ziglang.org/download/" >&2; exit 1; }
|
||||
export ZIG_GLOBAL_CACHE_DIR=${ZIG_GLOBAL_CACHE_DIR:-$PWD/dist/.zig-cache}
|
||||
|
||||
# zig target folder library tool
|
||||
targets="x86_64-linux-gnu.2.17 linux-x64 libyapf.so yapf
|
||||
aarch64-linux-gnu.2.17 linux-arm64 libyapf.so yapf
|
||||
x86_64-windows-gnu windows-x64 yapf.dll yapf.exe
|
||||
aarch64-windows-gnu windows-arm64 yapf.dll yapf.exe
|
||||
x86_64-macos macos-x64 libyapf.dylib yapf
|
||||
aarch64-macos macos-arm64 libyapf.dylib yapf"
|
||||
|
||||
echo "$targets" | while read -r tgt dir lib tool; do
|
||||
pkg=dist/pkg/yapf-$dir
|
||||
rm -rf "$pkg" && mkdir -p "$pkg" "$LIBS/$dir"
|
||||
zig cc -target "$tgt" -O2 -std=c99 -shared -fPIC -s yapf.c -o "$LIBS/$dir/$lib"
|
||||
zig cc -target "$tgt" -O2 -std=c99 -s tools/yapf_cli.c yapf.c -o "$pkg/$tool" -lm
|
||||
zig cc -target "$tgt" -O2 -std=c99 -c yapf.c -o "$pkg/yapf.o"
|
||||
zig ar rcs "$pkg/libyapf.a" "$pkg/yapf.o" && rm "$pkg/yapf.o"
|
||||
cp "$LIBS/$dir/$lib" yapf.h LICENSE "$pkg/"
|
||||
rm -f "$pkg"/*.lib "$pkg"/*.pdb "$LIBS/$dir"/*.lib "$LIBS/$dir"/*.pdb
|
||||
(cd dist/pkg && python3 -m zipfile -c "../../$OUT/yapf-$dir.zip" "yapf-$dir")
|
||||
echo "built yapf-$dir.zip"
|
||||
done
|
||||
|
||||
python3 extensions/blender/build.py "$LIBS"
|
||||
mv dist/yapf_image-blender.zip "$OUT/"
|
||||
|
||||
if command -v npx >/dev/null; then
|
||||
(cd extensions/vscode && npm run --silent package && mv yapf-preview-*.vsix "../../$OUT/yapf-preview.vsix")
|
||||
else
|
||||
echo "node/npx not found: skipped the VS Code package" >&2
|
||||
fi
|
||||
|
||||
(cd extensions/figma && node build.js 2>/dev/null || true)
|
||||
python3 -m zipfile -c "$OUT/yapf-figma.zip" extensions/figma/manifest.json extensions/figma/code.js extensions/figma/ui.html
|
||||
python3 -m zipfile -c "$OUT/yapf-photoshop.zip" extensions/photoshop
|
||||
python3 -m zipfile -c "$OUT/yapf-gimp-source.zip" extensions/gimp yapf.c yapf.h
|
||||
ls -la "$OUT"
|
||||
345
tools/yapf_cli.c
Normal file
345
tools/yapf_cli.c
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
* yapf_cli.c — the `yapf` command-line tool.
|
||||
*
|
||||
* 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 <input> <output> [options] convert; formats chosen by extension
|
||||
* yapf info <file.yapf> print header information
|
||||
* yapf bench <file.yapf> time decoding
|
||||
* yapf thumbnail <in.yapf> <out.png> [size] small preview (default 256)
|
||||
*
|
||||
* Reads PNG, JPEG, BMP, TGA, GIF (first frame), PSD (composite), PNM, HDR
|
||||
* (tone-clamped) and YAPF. Writes YAPF, PNG, BMP, TGA and JPEG.
|
||||
*
|
||||
* Options for writing YAPF:
|
||||
* --mips also store a full mip chain (2×2 box filter)
|
||||
* --linear mark pixels as linear instead of sRGB
|
||||
* --premult mark alpha as premultiplied
|
||||
* Options for reading YAPF:
|
||||
* --mip N export mip level N instead of the base image
|
||||
*
|
||||
* Build: cc -O2 -std=c99 -Ithird_party tools/yapf_cli.c yapf.c -o yapf -lm -pthread
|
||||
*/
|
||||
|
||||
#if !defined(_WIN32) && !defined(_POSIX_C_SOURCE)
|
||||
# define _POSIX_C_SOURCE 200809L
|
||||
#endif
|
||||
|
||||
#include "../yapf.h"
|
||||
|
||||
/* Third-party, public domain; keep their warnings out of our build. */
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wunused-function"
|
||||
# pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||
# pragma GCC diagnostic ignored "-Wsign-compare"
|
||||
#endif
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_FAILURE_USERMSG
|
||||
#include "../third_party/stb_image.h"
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "../third_party/stb_image_write.h"
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
|
||||
static int ends_with(const char *s, const char *ext) {
|
||||
size_t n = strlen(s), m = strlen(ext);
|
||||
if (n < m) return 0;
|
||||
for (size_t i = 0; i < m; i++)
|
||||
if (tolower((unsigned char)s[n - m + i]) != ext[i]) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int is_yapf(const char *path) { return ends_with(path, ".yapf"); }
|
||||
|
||||
static double now_s(void) {
|
||||
#if defined(_WIN32)
|
||||
return (double)clock() / CLOCKS_PER_SEC;
|
||||
#else
|
||||
struct timespec t;
|
||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||
return (double)t.tv_sec + (double)t.tv_nsec * 1e-9;
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint8_t gpu_hint(int ch, int srgb) {
|
||||
switch (ch) {
|
||||
case 1: return YAPF_GPU_R8;
|
||||
case 2: return YAPF_GPU_RG8;
|
||||
case 3: return srgb ? YAPF_GPU_SRGB8 : YAPF_GPU_RGB8;
|
||||
default: return srgb ? YAPF_GPU_SRGB8_A8 : YAPF_GPU_RGBA8;
|
||||
}
|
||||
}
|
||||
|
||||
/* Full mip chain with a 2×2 box filter (edges clamp for odd sizes). */
|
||||
static int build_mips(yapf_image_t *img) {
|
||||
int levels = 1;
|
||||
while (levels < (int)YAPF_MAX_MIPS &&
|
||||
((img->width >> levels) > 0 || (img->height >> levels) > 0))
|
||||
levels++;
|
||||
img->mips = (uint8_t **)calloc((size_t)levels, sizeof(uint8_t *));
|
||||
if (!img->mips) return -1;
|
||||
img->mips[0] = img->pixels;
|
||||
img->mip_levels = (uint8_t)levels;
|
||||
|
||||
int ch = img->channels;
|
||||
for (int m = 1; m < levels; m++) {
|
||||
uint32_t pw = img->width >> (m - 1), ph = img->height >> (m - 1);
|
||||
uint32_t w = img->width >> m, h = img->height >> m;
|
||||
if (pw < 1) pw = 1;
|
||||
if (ph < 1) ph = 1;
|
||||
if (w < 1) w = 1;
|
||||
if (h < 1) h = 1;
|
||||
uint8_t *src = img->mips[m - 1];
|
||||
uint8_t *dst = (uint8_t *)malloc((size_t)w * h * ch);
|
||||
if (!dst) return -1;
|
||||
for (uint32_t y = 0; y < h; y++)
|
||||
for (uint32_t x = 0; x < w; x++)
|
||||
for (int c = 0; c < ch; c++) {
|
||||
uint32_t x0 = 2 * x < pw ? 2 * x : pw - 1, x1 = 2 * x + 1 < pw ? 2 * x + 1 : pw - 1;
|
||||
uint32_t y0 = 2 * y < ph ? 2 * y : ph - 1, y1 = 2 * y + 1 < ph ? 2 * y + 1 : ph - 1;
|
||||
unsigned s = src[((size_t)y0 * pw + x0) * ch + c] + src[((size_t)y0 * pw + x1) * ch + c]
|
||||
+ src[((size_t)y1 * pw + x0) * ch + c] + src[((size_t)y1 * pw + x1) * ch + c];
|
||||
dst[((size_t)y * w + x) * ch + c] = (uint8_t)((s + 2) / 4);
|
||||
}
|
||||
img->mips[m] = dst;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_image(const char *path, const uint8_t *px, int w, int h, int ch) {
|
||||
if (ends_with(path, ".png")) return stbi_write_png(path, w, h, ch, px, w * ch);
|
||||
if (ends_with(path, ".bmp")) return stbi_write_bmp(path, w, h, ch, px);
|
||||
if (ends_with(path, ".tga")) return stbi_write_tga(path, w, h, ch, px);
|
||||
if (ends_with(path, ".jpg") || ends_with(path, ".jpeg"))
|
||||
return stbi_write_jpg(path, w, h, ch, px, 95);
|
||||
fprintf(stderr, "yapf: unsupported output format: %s (use .yapf .png .bmp .tga .jpg)\n", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *gpu_name(int g) {
|
||||
static const char *n[] = { "RGBA8", "RGB8", "RG8", "R8", "SRGB8_A8", "SRGB8" };
|
||||
return g >= 0 && g < 6 ? n[g] : "unknown";
|
||||
}
|
||||
|
||||
static int cmd_info(const char *path) {
|
||||
FILE *f = fopen(path, "rb");
|
||||
uint8_t hdr[20];
|
||||
if (!f || fread(hdr, 1, 20, f) != 20 || memcmp(hdr, "YAPF", 4) != 0) {
|
||||
fprintf(stderr, "yapf: %s is not a YAPF file\n", path);
|
||||
if (f) fclose(f);
|
||||
return 1;
|
||||
}
|
||||
fseek(f, 0, SEEK_END);
|
||||
long size = ftell(f);
|
||||
fclose(f);
|
||||
unsigned w = hdr[12] | hdr[13] << 8 | (unsigned)hdr[14] << 16 | (unsigned)hdr[15] << 24;
|
||||
unsigned h = hdr[16] | hdr[17] << 8 | (unsigned)hdr[18] << 16 | (unsigned)hdr[19] << 24;
|
||||
printf("%s\n", path);
|
||||
printf(" format version : %u\n", hdr[4]);
|
||||
printf(" size : %u x %u, %u channel(s)\n", w, h, hdr[5]);
|
||||
printf(" mip levels : %u\n", hdr[7]);
|
||||
printf(" gpu format : %s\n", gpu_name(hdr[6]));
|
||||
printf(" flags : %s%s\n", hdr[8] & YAPF_FLAG_SRGB ? "sRGB " : "linear ",
|
||||
hdr[8] & YAPF_FLAG_PREMULT_ALPHA ? "premultiplied-alpha" : "");
|
||||
printf(" file size : %ld bytes (%.1f%% of raw)\n", size,
|
||||
100.0 * (double)size / ((double)w * h * (hdr[5] ? hdr[5] : 1)));
|
||||
yapf_image_t *img = yapf_load(path);
|
||||
printf(" decodes : %s\n", img ? "ok" : "FAILED (corrupt or unsupported)");
|
||||
yapf_free(img);
|
||||
return img ? 0 : 1;
|
||||
}
|
||||
|
||||
static int cmd_bench(const char *path) {
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) { fprintf(stderr, "yapf: cannot open %s\n", path); return 1; }
|
||||
fseek(f, 0, SEEK_END);
|
||||
long n = ftell(f);
|
||||
rewind(f);
|
||||
uint8_t *buf = (uint8_t *)malloc((size_t)n);
|
||||
if (!buf || fread(buf, 1, (size_t)n, f) != (size_t)n) { fclose(f); free(buf); return 1; }
|
||||
fclose(f);
|
||||
yapf_image_t *img = yapf_load_memory(buf, (size_t)n);
|
||||
if (!img) { fprintf(stderr, "yapf: %s does not decode\n", path); free(buf); return 1; }
|
||||
double raw = (double)img->width * img->height * img->channels;
|
||||
yapf_free(img);
|
||||
for (int mt = 0; mt < 2; mt++) {
|
||||
/* Best of 15 rounds; each round repeats enough decodes to last
|
||||
* ~20 ms, so short images are measured warm and not timer-bound. */
|
||||
double one = now_s();
|
||||
yapf_free(mt ? yapf_load_memory_mt(buf, (size_t)n, 0) : yapf_load_memory(buf, (size_t)n));
|
||||
one = now_s() - one;
|
||||
int reps = (int)(0.02 / (one > 1e-6 ? one : 1e-6)) + 1;
|
||||
double best = 1e9;
|
||||
for (int r = 0; r < 15; r++) {
|
||||
double t = now_s();
|
||||
for (int k = 0; k < reps; k++)
|
||||
yapf_free(mt ? yapf_load_memory_mt(buf, (size_t)n, 0)
|
||||
: yapf_load_memory(buf, (size_t)n));
|
||||
t = (now_s() - t) / reps;
|
||||
if (t < best) best = t;
|
||||
}
|
||||
printf(" %-12s %8.3f ms %6.2f GB/s\n", mt ? "all cores" : "1 thread", best * 1e3, raw / best / 1e9);
|
||||
}
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Preview no larger than size×size: start from the smallest mip that is
|
||||
* still big enough, then average boxes of source pixels. */
|
||||
static int cmd_thumbnail(const char *in, const char *out, int size) {
|
||||
yapf_image_t *img = yapf_load_mt(in, 0);
|
||||
if (!img) { fprintf(stderr, "yapf: cannot read %s\n", in); return 1; }
|
||||
if (size < 1) size = 256;
|
||||
int m = 0;
|
||||
while (m + 1 < img->mip_levels &&
|
||||
(int)(img->width >> (m + 1)) >= size && (int)(img->height >> (m + 1)) >= size)
|
||||
m++;
|
||||
uint32_t sw = img->width >> m ? img->width >> m : 1, sh = img->height >> m ? img->height >> m : 1;
|
||||
const uint8_t *src = img->mips[m];
|
||||
double k = (double)size / (double)(sw > sh ? sw : sh);
|
||||
if (k > 1.0) k = 1.0;
|
||||
uint32_t dw = (uint32_t)(sw * k + 0.5), dh = (uint32_t)(sh * k + 0.5);
|
||||
if (dw < 1) dw = 1;
|
||||
if (dh < 1) dh = 1;
|
||||
int ch = img->channels;
|
||||
uint8_t *dst = (uint8_t *)malloc((size_t)dw * dh * ch);
|
||||
if (!dst) { yapf_free(img); return 1; }
|
||||
for (uint32_t y = 0; y < dh; y++) {
|
||||
uint32_t y0 = (uint32_t)((uint64_t)y * sh / dh), y1 = (uint32_t)((uint64_t)(y + 1) * sh / dh);
|
||||
if (y1 <= y0) y1 = y0 + 1;
|
||||
for (uint32_t x = 0; x < dw; x++) {
|
||||
uint32_t x0 = (uint32_t)((uint64_t)x * sw / dw), x1 = (uint32_t)((uint64_t)(x + 1) * sw / dw);
|
||||
if (x1 <= x0) x1 = x0 + 1;
|
||||
for (int c = 0; c < ch; c++) {
|
||||
uint64_t sum = 0;
|
||||
for (uint32_t yy = y0; yy < y1; yy++)
|
||||
for (uint32_t xx = x0; xx < x1; xx++)
|
||||
sum += src[((size_t)yy * sw + xx) * ch + c];
|
||||
uint64_t n = (uint64_t)(y1 - y0) * (x1 - x0);
|
||||
dst[((size_t)y * dw + x) * ch + c] = (uint8_t)((sum + n / 2) / n);
|
||||
}
|
||||
}
|
||||
}
|
||||
int ok = stbi_write_png(out, (int)dw, (int)dh, ch, dst, (int)dw * ch);
|
||||
free(dst);
|
||||
yapf_free(img);
|
||||
if (!ok) fprintf(stderr, "yapf: cannot write %s\n", out);
|
||||
return ok ? 0 : 1;
|
||||
}
|
||||
|
||||
static void usage(void) {
|
||||
fprintf(stderr,
|
||||
"usage: yapf <input> <output> [options] convert (formats by extension)\n"
|
||||
" yapf info <file.yapf> show header information\n"
|
||||
" yapf bench <file.yapf> time decoding\n"
|
||||
" yapf thumbnail <in.yapf> <out.png> [size] preview, default 256 px\n"
|
||||
"\n"
|
||||
"reads: .yapf .png .jpg .bmp .tga .gif .psd .pnm .hdr\n"
|
||||
"writes: .yapf .png .jpg .bmp .tga\n"
|
||||
"\n"
|
||||
"options when writing .yapf:\n"
|
||||
" --mips store a full mip chain\n"
|
||||
" --linear pixels are linear, not sRGB\n"
|
||||
" --premult alpha is premultiplied\n"
|
||||
"options when reading .yapf:\n"
|
||||
" --mip N export mip level N\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc == 3 && strcmp(argv[1], "info") == 0) return cmd_info(argv[2]);
|
||||
if (argc == 3 && strcmp(argv[1], "bench") == 0) return cmd_bench(argv[2]);
|
||||
if ((argc == 4 || argc == 5) && strcmp(argv[1], "thumbnail") == 0)
|
||||
return cmd_thumbnail(argv[2], argv[3], argc == 5 ? atoi(argv[4]) : 256);
|
||||
if (argc < 3) { usage(); return 2; }
|
||||
|
||||
const char *in = argv[1], *out = argv[2];
|
||||
int mips = 0, srgb = 1, premult = 0, mip_out = 0;
|
||||
for (int i = 3; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--mips") == 0) mips = 1;
|
||||
else if (strcmp(argv[i], "--linear") == 0) srgb = 0;
|
||||
else if (strcmp(argv[i], "--premult") == 0) premult = 1;
|
||||
else if (strcmp(argv[i], "--mip") == 0 && i + 1 < argc) mip_out = atoi(argv[++i]);
|
||||
else { usage(); return 2; }
|
||||
}
|
||||
|
||||
/* Read */
|
||||
yapf_image_t src;
|
||||
yapf_image_t *loaded = NULL;
|
||||
uint8_t *stb_px = NULL;
|
||||
memset(&src, 0, sizeof(src));
|
||||
if (is_yapf(in)) {
|
||||
loaded = yapf_load_mt(in, 0);
|
||||
if (!loaded) { fprintf(stderr, "yapf: cannot read %s (missing, corrupt or not YAPF v1)\n", in); return 1; }
|
||||
src = *loaded;
|
||||
} else {
|
||||
int w, h, ch;
|
||||
if (stbi_is_16_bit(in))
|
||||
fprintf(stderr, "yapf: note: %s has 16-bit channels; YAPF stores 8 bits per channel\n", in);
|
||||
stb_px = stbi_load(in, &w, &h, &ch, 0);
|
||||
if (!stb_px) { fprintf(stderr, "yapf: cannot read %s: %s\n", in, stbi_failure_reason()); return 1; }
|
||||
src.width = (uint32_t)w; src.height = (uint32_t)h; src.channels = (uint8_t)ch;
|
||||
src.pixels = stb_px; src.mip_levels = 1;
|
||||
src.flags = srgb ? YAPF_FLAG_SRGB : 0;
|
||||
src.gpu_format = gpu_hint(ch, srgb);
|
||||
}
|
||||
|
||||
/* Write */
|
||||
int rc = 0;
|
||||
if (is_yapf(out)) {
|
||||
yapf_image_t dst = src;
|
||||
if (!loaded) {
|
||||
if (premult) dst.flags |= YAPF_FLAG_PREMULT_ALPHA;
|
||||
}
|
||||
dst.mips = NULL;
|
||||
dst.mip_levels = 1;
|
||||
if (mips && build_mips(&dst) != 0) { fprintf(stderr, "yapf: out of memory\n"); rc = 1; }
|
||||
if (!rc && yapf_save(out, &dst) != YAPF_OK) { fprintf(stderr, "yapf: cannot write %s\n", out); rc = 1; }
|
||||
if (dst.mips) {
|
||||
for (int m = 1; m < dst.mip_levels; m++) free(dst.mips[m]);
|
||||
free(dst.mips);
|
||||
}
|
||||
} else {
|
||||
const uint8_t *px = src.pixels;
|
||||
uint32_t w = src.width, h = src.height;
|
||||
if (mip_out > 0) {
|
||||
if (!loaded || mip_out >= src.mip_levels) {
|
||||
fprintf(stderr, "yapf: %s has no mip level %d\n", in, mip_out);
|
||||
rc = 1;
|
||||
} else {
|
||||
px = src.mips[mip_out];
|
||||
w = src.width >> mip_out ? src.width >> mip_out : 1;
|
||||
h = src.height >> mip_out ? src.height >> mip_out : 1;
|
||||
}
|
||||
}
|
||||
if (!rc && !write_image(out, px, (int)w, (int)h, src.channels)) {
|
||||
fprintf(stderr, "yapf: cannot write %s\n", out);
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
|
||||
yapf_free(loaded);
|
||||
stbi_image_free(stb_px);
|
||||
return rc;
|
||||
}
|
||||
Loading…
Reference in a new issue