diff --git a/CMakeLists.txt b/CMakeLists.txt index 3dd05d6810..c5883688fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,10 +42,11 @@ topaz_add_library( src/tz/core/trs.cpp src/tz/gpu/rhi_vulkan.cpp src/tz/os/impl_win32.cpp + src/tz/io/image.cpp ) target_include_directories(topaz PUBLIC ${PROJECT_SOURCE_DIR}/include) -target_link_libraries(topaz PRIVATE VulkanMemoryAllocator concurrentqueue) +target_link_libraries(topaz PRIVATE VulkanMemoryAllocator concurrentqueue stb_image) target_link_libraries(topaz PUBLIC textc_lib) ###################### diff --git a/include/tz/io/image.hpp b/include/tz/io/image.hpp new file mode 100644 index 0000000000..61bf33d808 --- /dev/null +++ b/include/tz/io/image.hpp @@ -0,0 +1,52 @@ +#ifndef TOPAZ_IO_IMAGE_HPP +#define TOPAZ_IO_IMAGE_HPP +#include "tz/core/error.hpp" +#include +#include +#include + +namespace tz::io +{ + /** + * @ingroup tz_io + * @brief Describes basic information about an image. + * + * Note that all images loaded are 4-component RGBA with 1 byte ber component. + */ + struct image_header + { + /// Width of the image, in pixels. + unsigned int width = 0u; + /// Height of the image, in pixels. + unsigned int height = 0u; + /// Size of the total image data, in bytes. + std::size_t data_size_bytes = 0u; + }; + + /** + * @ingroup tz_io + * @brief Retrieve info about an image loaded in memory + * @param img_file_data Data read directly from an image file. See below for the list of supported image file formats. + * @return A @ref image_header containing basic information about the image, or some @ref tz::error_code if the image data could not be parsed. + * + * The image file formats guaranteed are: + * - jpg + * - png + * - bmp + * + * Other file formats that aren't listed here might still work, but you should consider anything not on this list an implementation detail that could lose support suddenly in a new release. + */ + std::expected image_info(std::span img_file_data); + /** + * @ingroup tz_io + * @brief Load an image from file data resident in memory. + * @param img_file_data Data read directly from an image file. See the detailsof @ref image_info for a list of supported image file formats. + * @param buffer A buffer of memory into which decoded image data will be written. It is your responsibility to provide a buffer that is of sufficient size. To decipher the size needed, see @ref image_info. + * @return @ref tz::error_code::success If the image data was successfully decoded. + * @return @ref tz::error_code::oom If the buffer you provided was not of sufficient size. + * @return @ref tz::error_code::unknown_error If image decoding failed for some other reason. + */ + tz::error_code parse_image(std::span img_file_data, std::span buffer); +} + +#endif // TOPAZ_IO_IMAGE_HPP \ No newline at end of file diff --git a/include/tz/topaz.hpp b/include/tz/topaz.hpp index d6c02f1978..d327e8b350 100644 --- a/include/tz/topaz.hpp +++ b/include/tz/topaz.hpp @@ -111,6 +111,15 @@ namespace tz * This library contains some pre-created high-level rendering systems used for common tasks, such as rendering 3D models, or user interface. These components are intended to be used in most Topaz games, with the goal of not needing to reinvent the wheel in every single game using the @ref tz_ren. **/ +/** + * @ingroup tz + * @defgroup tz_io IO Library + * @brief High-level library for wrangling files of various formats. + * + * This library contains support for a handful of common file formats, allowing you to import them without having to implement a parser yourself. +**/ + + #include "detail/debug.hpp" #endif // TOPAZ_HPP \ No newline at end of file diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 619d321279..21b9ffb01f 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,3 +1,6 @@ add_subdirectory(concurrentqueue) add_subdirectory(VulkanMemoryAllocator) -add_subdirectory(textc) \ No newline at end of file +add_subdirectory(textc) + +add_library(stb_image INTERFACE) +target_include_directories(stb_image INTERFACE stb) \ No newline at end of file diff --git a/src/tz/io/image.cpp b/src/tz/io/image.cpp new file mode 100644 index 0000000000..800050d418 --- /dev/null +++ b/src/tz/io/image.cpp @@ -0,0 +1,42 @@ +#include "tz/io/image.hpp" +#include "tz/topaz.hpp" +#define STBI_FAILURE_USERMSG +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +namespace tz::io +{ + std::expected image_info(std::span img_file_data) + { + int w, h, channels; + int ok = stbi_info_from_memory(reinterpret_cast(img_file_data.data()), img_file_data.size_bytes(), &w, &h, &channels); + if(ok != 1) + { + UNERR(tz::error_code::precondition_failure, "bad image file data: {}", stbi_failure_reason()); + } + + return image_header + { + .width = static_cast(w), + .height = static_cast(h), + .data_size_bytes = w * h * 4u * 1u + }; + } + + tz::error_code parse_image(std::span img_file_data, std::span buffer) + { + int w, h, channels; + stbi_uc* imgdata = stbi_load_from_memory(reinterpret_cast(img_file_data.data()), img_file_data.size_bytes(), &w, &h, &channels, 4); + std::size_t real_image_size = w * h * 4u * 1u; + if(buffer.size_bytes() < real_image_size) + { + RETERR(tz::error_code::oom, "buffer provided to parse_image was too small ({} bytes), it needs to be at least {} bytes", buffer.size_bytes(), real_image_size); + } + if(imgdata == nullptr) + { + RETERR(tz::error_code::unknown_error, "unknown error occurred during image parsing: {}", stbi_failure_reason()); + } + std::memcpy(buffer.data(), imgdata, real_image_size); + return tz::error_code::success; + } +} \ No newline at end of file