Add image export roundtrip automation
This commit is contained in:
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,4 +18,9 @@ struct Rgba8Image {
|
||||
[[nodiscard]] pp::foundation::Result<Rgba8Image> decode_png_rgba8(
|
||||
std::span<const std::byte> bytes);
|
||||
|
||||
[[nodiscard]] 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);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user