#include "runtime_image.hpp" #include #include #include #include #include namespace dusk::ui { namespace { constexpr borealis::Log Log{"dusk::ui"}; constexpr std::uint32_t kMaxImageDimension = 4096; } // namespace std::optional decode_png( std::span 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(rgbaSurface->w); const auto height = static_cast(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(width) * 4; DecodedImage image{ .pixels = std::vector(rowSize * height), .width = width, .height = height, }; for (std::uint32_t row = 0; row < height; ++row) { const auto* src = static_cast(rgbaSurface->pixels) + static_cast(row) * static_cast(rgbaSurface->pitch); auto* dst = image.pixels.data() + static_cast(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( (static_cast(dst[col + channel]) * alpha) / 255); } } } SDL_DestroySurface(rgbaSurface); return image; } } // namespace dusk::ui