Files
dusklight/src/dusk/ui/runtime_image.cpp
T
2026-09-01 23:07:40 -06:00

74 lines
2.5 KiB
C++

#include "runtime_image.hpp"
#include <SDL3/SDL_iostream.h>
#include <SDL3/SDL_surface.h>
#include <borealis/log.hpp>
#include <cstddef>
#include <cstring>
namespace dusk::ui {
namespace {
constexpr borealis::Log Log{"dusk::ui"};
constexpr std::uint32_t kMaxImageDimension = 4096;
} // namespace
std::optional<DecodedImage> decode_png(
std::span<const std::uint8_t> data, std::string_view source) {
SDL_IOStream* stream = SDL_IOFromConstMem(data.data(), data.size());
if (stream == nullptr) {
Log.warn("Failed to open image stream for '{}': {}", source, SDL_GetError());
return std::nullopt;
}
SDL_Surface* loadedSurface = SDL_LoadPNG_IO(stream, true);
if (loadedSurface == nullptr) {
Log.warn("Failed to decode image '{}': {}", source, SDL_GetError());
return std::nullopt;
}
SDL_Surface* rgbaSurface = SDL_ConvertSurface(loadedSurface, SDL_PIXELFORMAT_RGBA32);
SDL_DestroySurface(loadedSurface);
if (rgbaSurface == nullptr) {
Log.warn("Failed to convert image '{}': {}", source, SDL_GetError());
return std::nullopt;
}
const auto width = static_cast<std::uint32_t>(rgbaSurface->w);
const auto height = static_cast<std::uint32_t>(rgbaSurface->h);
if (width == 0 || height == 0 || width > kMaxImageDimension || height > kMaxImageDimension) {
Log.warn("Image '{}' has unsupported dimensions {}x{}", source, width, height);
SDL_DestroySurface(rgbaSurface);
return std::nullopt;
}
const std::size_t rowSize = static_cast<std::size_t>(width) * 4;
DecodedImage image{
.pixels = std::vector<std::uint8_t>(rowSize * height),
.width = width,
.height = height,
};
for (std::uint32_t row = 0; row < height; ++row) {
const auto* src =
static_cast<const std::uint8_t*>(rgbaSurface->pixels) +
static_cast<std::size_t>(row) * static_cast<std::size_t>(rgbaSurface->pitch);
auto* dst = image.pixels.data() + static_cast<std::size_t>(row) * rowSize;
std::memcpy(dst, src, rowSize);
for (std::size_t col = 0; col < rowSize; col += 4) {
const std::uint8_t alpha = dst[col + 3];
for (std::size_t channel = 0; channel < 3; ++channel) {
dst[col + channel] = static_cast<std::uint8_t>(
(static_cast<std::uint32_t>(dst[col + channel]) * alpha) / 255);
}
}
}
SDL_DestroySurface(rgbaSurface);
return image;
}
} // namespace dusk::ui