Program Listing for File TextureDecode.ixx

Program Listing for File TextureDecode.ixx#

Return to documentation for file (Src/GraphicsEngineVulkan/scene/TextureDecode.ixx)

module;

#include <cstdint>
#include <string>

#include <stb_image.h>

// The image decode every material texture and every skybox face goes through,
// as its own module so it can be exercised without the renderer.
//
// It used to live in Texture.cpp, which means the only way to reach it was to
// link VulkanEngineCore. texture_loading_fuzz_test did exactly that and died
// at startup on the Linux ASan lane - "SEGV on unknown address
// 0x000000000000" inside abseil's flag registry, from fuzztest's own static
// initializer (init_fuzztest.cc:54), before a single test ran. Adding the
// engine's translation units to that link reorders the static initializers
// FuzzTest's abseil flags are registered by; the other fuzz targets stay
// minimal for this reason and Test/fuzz/CMakeLists.txt documents the same
// crash for scene_config_fuzz_test.
//
// Nothing here needs Vulkan, a device, or a logger: it is stb_image plus the
// size arithmetic callers rely on. Kataglyphis::Texture::loadTextureData
// keeps its signature and its spdlog error line, and forwards here.
export module kataglyphis.vulkan.texture_decode;

export namespace Kataglyphis::TextureDecode {

// Decodes an image file to tightly packed RGBA8. Returns nullptr on failure;
// on success `*image_size` is exactly width * height * 4 and the caller owns
// the buffer (free with stbi_image_free).
//
// The size contract is the point, not merely "does not crash". The byte count
// reported here sizes a staging buffer that is then memcpy'd into, so a count
// that disagrees with the returned allocation is a heap overflow rather than a
// bad picture. STBI_rgb_alpha forces 4 channels, which is what makes the
// width * height * 4 arithmetic exact.
//
// On failure the three out-parameters are ZEROED rather than left alone.
// stb_image does not write them on the failure path, so a caller that checked
// the size before the null pointer would otherwise read whatever it had
// initialised them to.
inline unsigned char *decodeImageRGBA8(const std::string &file_name, int *width, int *height, std::uint64_t *image_size)
{
    int channels = 0;
    unsigned char *image = stbi_load(file_name.c_str(), width, height, &channels, STBI_rgb_alpha);

    if (image == nullptr) {
        *width = 0;
        *height = 0;
        *image_size = 0;
        return nullptr;
    }

    *image_size = static_cast<std::uint64_t>(*width) * static_cast<std::uint64_t>(*height) * 4U;

    return image;
}

}// namespace Kataglyphis::TextureDecode