Add image export roundtrip automation

This commit is contained in:
2026-06-02 10:41:34 +02:00
parent 9d05d193a7
commit 9c6b52eb8e
8 changed files with 303 additions and 6 deletions

View File

@@ -9,6 +9,10 @@
#define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h>
#define STB_IMAGE_WRITE_STATIC
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb/stb_image_write.h>
namespace pp::assets {
namespace {
@@ -33,6 +37,17 @@ namespace {
return pp::foundation::Result<std::size_t>::success(static_cast<std::size_t>(bytes));
}
void append_png_bytes(void* context, void* data, int size)
{
if (context == nullptr || data == nullptr || size <= 0) {
return;
}
auto* bytes = static_cast<std::vector<std::byte>*>(context);
const auto* begin = static_cast<const std::byte*>(data);
bytes->insert(bytes->end(), begin, begin + size);
}
}
pp::foundation::Result<Rgba8Image> decode_png_rgba8(std::span<const std::byte> bytes)
@@ -91,4 +106,54 @@ pp::foundation::Result<Rgba8Image> decode_png_rgba8(std::span<const std::byte> b
return pp::foundation::Result<Rgba8Image>::success(std::move(image));
}
pp::foundation::Result<std::vector<std::byte>> encode_png_rgba8(
std::uint32_t width,
std::uint32_t height,
std::span<const std::uint8_t> pixels)
{
if (width == 0 || height == 0) {
return pp::foundation::Result<std::vector<std::byte>>::failure(
pp::foundation::Status::invalid_argument("PNG dimensions must be greater than zero"));
}
if (width > static_cast<std::uint32_t>(std::numeric_limits<int>::max())
|| height > static_cast<std::uint32_t>(std::numeric_limits<int>::max())) {
return pp::foundation::Result<std::vector<std::byte>>::failure(
pp::foundation::Status::out_of_range("PNG dimensions exceed encoder limits"));
}
const auto byte_count = rgba_byte_size(width, height);
if (!byte_count) {
return pp::foundation::Result<std::vector<std::byte>>::failure(byte_count.status());
}
if (pixels.size() != byte_count.value()) {
return pp::foundation::Result<std::vector<std::byte>>::failure(
pp::foundation::Status::invalid_argument("RGBA pixel payload size does not match dimensions"));
}
const auto stride = static_cast<std::uint64_t>(width) * 4ULL;
if (stride > static_cast<std::uint64_t>(std::numeric_limits<int>::max())) {
return pp::foundation::Result<std::vector<std::byte>>::failure(
pp::foundation::Status::out_of_range("PNG row stride exceeds encoder limits"));
}
std::vector<std::byte> encoded;
const auto result = stbi_write_png_to_func(
append_png_bytes,
&encoded,
static_cast<int>(width),
static_cast<int>(height),
4,
pixels.data(),
static_cast<int>(stride));
if (result == 0 || encoded.empty()) {
return pp::foundation::Result<std::vector<std::byte>>::failure(
pp::foundation::Status::invalid_argument("RGBA pixels could not be encoded as PNG"));
}
return pp::foundation::Result<std::vector<std::byte>>::success(std::move(encoded));
}
}