mirror of
https://github.com/Nexoniarz/YAPF.git
synced 2026-11-03 04:54:26 +00:00
Language bindings, KDE thumbnails, yapf compare, in-memory encoding
- yapf_encode() / yapf_free_buffer(): encode to memory; yapf_save uses it - yapf.c also compiles as C++ - `yapf compare`: size and decode speed of the original format vs YAPF, with a lossless check - bindings with tested examples: C++ (header-only), C# (P/Invoke), Rust (crate, builds the C code), Go (cgo), Java (FFM API, Java 22+), Zig (@cImport), LuaJIT (FFI), TypeScript types for the JS port; Python now encodes in memory - docs/LANGUAGES.md and examples/ with measured results - KDE Dolphin thumbnail plugin (KF6, CMake) - README: honest status for Photoshop / VS Code / Figma (not run in the apps yet) and macOS (cross-compiled only), platform table Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
2ca726e3af
commit
d7f494a0c9
52 changed files with 1926 additions and 106 deletions
18
bindings/cpp/README.md
Normal file
18
bindings/cpp/README.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# YAPF for C++
|
||||
|
||||
`yapf.hpp` is a header-only C++17 wrapper: RAII, exceptions, `std::vector`.
|
||||
Add `../../yapf.c` to your project and include the header.
|
||||
|
||||
```cpp
|
||||
#include "yapf.hpp"
|
||||
|
||||
yapf::Image img = yapf::Image::load("texture.yapf"); // or Image::decode(bytes)
|
||||
upload(img.width(), img.height(), img.channels(), img.pixels());
|
||||
|
||||
std::vector<uint8_t> file = yapf::Image(256, 256, 4, rgba).encode();
|
||||
```
|
||||
|
||||
`yapf.c` compiles as C or as C++, so you can simply add it to a C++ project
|
||||
(Visual Studio, CMake, …).
|
||||
|
||||
Example: `g++ -O2 -std=c++17 example.cpp -x c++ ../../yapf.c -o example -pthread && ./example`
|
||||
33
bindings/cpp/example.cpp
Normal file
33
bindings/cpp/example.cpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// g++ -O2 -std=c++17 example.cpp ../../yapf.c -o example -pthread && ./example ../../other/YAPF.YAPF
|
||||
#include "yapf.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
const char *path = argc > 1 ? argv[1] : "../../other/YAPF.YAPF";
|
||||
std::ifstream f(path, std::ios::binary);
|
||||
std::vector<uint8_t> bytes((std::istreambuf_iterator<char>(f)), {});
|
||||
|
||||
yapf::Image img = yapf::Image::decode(bytes);
|
||||
double ms = 1e9; // best of 10, as in a running program
|
||||
for (int i = 0; i < 10; i++) {
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
img = yapf::Image::decode(bytes);
|
||||
ms = std::min(ms, std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - t0).count());
|
||||
}
|
||||
std::printf("%s: %ux%u, %u channels, decoded in %.2f ms, file is %.1f%% of raw\n", path,
|
||||
img.width(), img.height(), img.channels(), ms, 100.0 * bytes.size() / img.size());
|
||||
|
||||
std::vector<uint8_t> again = img.encode();
|
||||
std::printf("re-encoded: %zu bytes, identical to the file: %s\n", again.size(),
|
||||
again == bytes ? "yes" : "no");
|
||||
|
||||
std::vector<uint8_t> gray(64 * 64);
|
||||
for (size_t i = 0; i < gray.size(); i++) gray[i] = uint8_t(i % 64 * 4);
|
||||
yapf::Image(64, 64, 1, gray.data()).save("gray.yapf");
|
||||
std::printf("wrote gray.yapf\n");
|
||||
}
|
||||
98
bindings/cpp/yapf.hpp
Normal file
98
bindings/cpp/yapf.hpp
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
// yapf.hpp — header-only C++17 wrapper for YAPF. Copyright 2026 Nexoniarz — Apache 2.0.
|
||||
//
|
||||
// #include "yapf.hpp" // and compile/link ../../yapf.c
|
||||
// yapf::Image img = yapf::Image::load("texture.yapf");
|
||||
// img.width(); img.height(); img.channels(); img.pixels(); // span of bytes
|
||||
// std::vector<uint8_t> file = img.encode();
|
||||
// yapf::Image mine(256, 256, 4, my_rgba_bytes); mine.save("out.yapf");
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
#include "../../yapf.h"
|
||||
}
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace yapf {
|
||||
|
||||
struct Error : std::runtime_error {
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
class Image {
|
||||
public:
|
||||
/* Decode a file or a memory buffer; threads = 0 uses every core, 1 the calling thread. */
|
||||
static Image load(const std::string &path, int threads = 1) {
|
||||
yapf_image_t *p = yapf_load_mt(path.c_str(), threads);
|
||||
if (!p) throw Error("YAPF: cannot load " + path);
|
||||
return Image(p);
|
||||
}
|
||||
static Image decode(const void *data, size_t size, int threads = 1) {
|
||||
yapf_image_t *p = yapf_load_memory_mt(data, size, threads);
|
||||
if (!p) throw Error("YAPF: invalid or corrupt data");
|
||||
return Image(p);
|
||||
}
|
||||
static Image decode(const std::vector<uint8_t> &bytes, int threads = 1) {
|
||||
return decode(bytes.data(), bytes.size(), threads);
|
||||
}
|
||||
|
||||
/* A new image from your pixels (copied). channels: 1 gray, 2 gray+A, 3 RGB, 4 RGBA. */
|
||||
Image(uint32_t width, uint32_t height, uint8_t channels, const uint8_t *pixels,
|
||||
uint8_t flags = YAPF_FLAG_SRGB)
|
||||
: own_(pixels, pixels + size_t(width) * height * channels) {
|
||||
std::memset(&view_, 0, sizeof(view_));
|
||||
view_.width = width; view_.height = height; view_.channels = channels;
|
||||
view_.flags = flags; view_.mip_levels = 1;
|
||||
view_.gpu_format = channels == 4 ? YAPF_GPU_SRGB8_A8 : channels == 3 ? YAPF_GPU_SRGB8
|
||||
: channels == 2 ? YAPF_GPU_RG8 : YAPF_GPU_R8;
|
||||
view_.pixels = own_.data();
|
||||
}
|
||||
|
||||
Image(Image &&o) noexcept : img_(std::exchange(o.img_, nullptr)), own_(std::move(o.own_)), view_(o.view_) {
|
||||
if (!img_) view_.pixels = own_.data();
|
||||
}
|
||||
Image &operator=(Image &&o) noexcept {
|
||||
if (this != &o) { yapf_free(img_); img_ = std::exchange(o.img_, nullptr); own_ = std::move(o.own_);
|
||||
view_ = o.view_; if (!img_) view_.pixels = own_.data(); }
|
||||
return *this;
|
||||
}
|
||||
Image(const Image &) = delete;
|
||||
Image &operator=(const Image &) = delete;
|
||||
~Image() { yapf_free(img_); }
|
||||
|
||||
uint32_t width() const { return raw().width; }
|
||||
uint32_t height() const { return raw().height; }
|
||||
uint8_t channels() const { return raw().channels; }
|
||||
uint8_t flags() const { return raw().flags; }
|
||||
uint8_t mipLevels() const { return raw().mip_levels; }
|
||||
const uint8_t *pixels() const { return raw().pixels; }
|
||||
size_t size() const { return size_t(width()) * height() * channels(); }
|
||||
const uint8_t *mip(int level) const { return raw().mips ? raw().mips[level] : raw().pixels; }
|
||||
|
||||
std::vector<uint8_t> encode() const {
|
||||
void *data = nullptr;
|
||||
size_t n = 0;
|
||||
if (yapf_encode(&raw(), &data, &n) != YAPF_OK) throw Error("YAPF: encoding failed");
|
||||
std::vector<uint8_t> out(static_cast<uint8_t *>(data), static_cast<uint8_t *>(data) + n);
|
||||
yapf_free_buffer(data);
|
||||
return out;
|
||||
}
|
||||
void save(const std::string &path) const {
|
||||
if (yapf_save(path.c_str(), &raw()) != YAPF_OK) throw Error("YAPF: cannot write " + path);
|
||||
}
|
||||
|
||||
const yapf_image_t &raw() const { return img_ ? *img_ : view_; }
|
||||
|
||||
private:
|
||||
explicit Image(yapf_image_t *p) : img_(p) { std::memset(&view_, 0, sizeof(view_)); }
|
||||
yapf_image_t *img_ = nullptr; // decoded by the library
|
||||
std::vector<uint8_t> own_; // or pixels we own
|
||||
yapf_image_t view_{};
|
||||
};
|
||||
|
||||
} // namespace yapf
|
||||
Loading…
Reference in a new issue