diff --git a/CMakeLists.txt b/CMakeLists.txt index 528625c..2e0947b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,11 +4,47 @@ project(vectorail-core VERSION 0.1.0 LANGUAGES CXX) include(GNUInstallDirs) include(CMakePackageConfigHelpers) +include(FetchContent) find_package(OpenGL REQUIRED) -find_package(PNG REQUIRED) -find_package(SDL3 CONFIG REQUIRED) -find_package(glm CONFIG REQUIRED) + +set(VECTORAIL_CORE_FETCHED_DEPENDENCIES OFF) + +find_package(SDL3 CONFIG QUIET) +if(NOT TARGET SDL3::SDL3) + set(SDL_INSTALL OFF CACHE BOOL "" FORCE) + FetchContent_Declare(SDL3 + GIT_REPOSITORY https://github.com/libsdl-org/SDL.git + GIT_TAG main + ) + FetchContent_MakeAvailable(SDL3) + set(VECTORAIL_CORE_FETCHED_DEPENDENCIES ON) +endif() + +find_package(glm CONFIG QUIET) +if(NOT TARGET glm::glm) + FetchContent_Declare(glm + GIT_REPOSITORY https://github.com/g-truc/glm.git + GIT_TAG master + ) + FetchContent_MakeAvailable(glm) + set(VECTORAIL_CORE_FETCHED_DEPENDENCIES ON) +endif() + +FetchContent_Declare(stb + GIT_REPOSITORY https://github.com/nothings/stb.git + GIT_TAG 2c980bb59875b0d32144a71867fbdebb2f77cd20 +) +FetchContent_MakeAvailable(stb) + +if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR AND + NOT VECTORAIL_CORE_FETCHED_DEPENDENCIES) + set(VECTORAIL_CORE_INSTALL_DEFAULT ON) +else() + set(VECTORAIL_CORE_INSTALL_DEFAULT OFF) +endif() +option(VECTORAIL_CORE_INSTALL "Generate VectorailCore install rules" + ${VECTORAIL_CORE_INSTALL_DEFAULT}) add_library(vectorail-core src/DdsTexture.cpp @@ -24,12 +60,13 @@ target_include_directories(vectorail-core PUBLIC $ $ + PRIVATE + ${stb_SOURCE_DIR} ) target_link_libraries(vectorail-core PUBLIC SDL3::SDL3 OpenGL::GL - PNG::PNG glm::glm ) @@ -39,33 +76,34 @@ set_target_properties(vectorail-core PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR} ) -install(TARGETS vectorail-core - EXPORT VectorailCoreTargets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) -install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -install(EXPORT VectorailCoreTargets - FILE VectorailCoreTargets.cmake - NAMESPACE Vectorail:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VectorailCore -) - -configure_package_config_file( - cmake/VectorailCoreConfig.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/VectorailCoreConfig.cmake - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VectorailCore -) -write_basic_package_version_file( - ${CMAKE_CURRENT_BINARY_DIR}/VectorailCoreConfigVersion.cmake - VERSION ${PROJECT_VERSION} - COMPATIBILITY SameMajorVersion -) -install(FILES - ${CMAKE_CURRENT_BINARY_DIR}/VectorailCoreConfig.cmake - ${CMAKE_CURRENT_BINARY_DIR}/VectorailCoreConfigVersion.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VectorailCore -) +if(VECTORAIL_CORE_INSTALL) + install(TARGETS vectorail-core + EXPORT VectorailCoreTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) + install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + install(EXPORT VectorailCoreTargets + FILE VectorailCoreTargets.cmake + NAMESPACE Vectorail:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VectorailCore + ) + configure_package_config_file( + cmake/VectorailCoreConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/VectorailCoreConfig.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VectorailCore + ) + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/VectorailCoreConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion + ) + install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/VectorailCoreConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/VectorailCoreConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VectorailCore + ) +endif() diff --git a/cmake/VectorailCoreConfig.cmake.in b/cmake/VectorailCoreConfig.cmake.in index 0927a12..0e43619 100644 --- a/cmake/VectorailCoreConfig.cmake.in +++ b/cmake/VectorailCoreConfig.cmake.in @@ -2,9 +2,7 @@ include(CMakeFindDependencyMacro) find_dependency(OpenGL) -find_dependency(PNG) find_dependency(SDL3 CONFIG) find_dependency(glm CONFIG) include("${CMAKE_CURRENT_LIST_DIR}/VectorailCoreTargets.cmake") - diff --git a/include/vectorail/core/gl_loader.hpp b/include/vectorail/core/gl_loader.hpp index 6252937..a28ed07 100644 --- a/include/vectorail/core/gl_loader.hpp +++ b/include/vectorail/core/gl_loader.hpp @@ -5,6 +5,9 @@ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif +#ifndef NOMINMAX +#define NOMINMAX +#endif #include #endif diff --git a/src/PngTexture.cpp b/src/PngTexture.cpp index 1e2b505..c25f616 100644 --- a/src/PngTexture.cpp +++ b/src/PngTexture.cpp @@ -2,11 +2,13 @@ #include "vectorail/core/gl_loader.hpp" -#include +#define STBI_ONLY_PNG +#define STB_IMAGE_IMPLEMENTATION +#include -#include -#include +#include #include +#include #include #include @@ -35,56 +37,34 @@ bool readFile(const std::string& path, std::vector& bytes) { return static_cast(file) || file.eof(); } -struct PngMemoryReader { - std::span bytes; - size_t offset = 0; -}; - -void readPngMemory(png_structp png, png_bytep destination, png_size_t length) { - auto* reader = static_cast(png_get_io_ptr(png)); - if (!reader || length > reader->bytes.size() - reader->offset) { - png_error(png, "truncated PNG stream"); - return; - } - std::memcpy(destination, reader->bytes.data() + reader->offset, length); - reader->offset += length; -} - bool decodePng(std::span bytes, DdsTexture& out, std::string* error) { out = {}; - if (bytes.size() < 8 || png_sig_cmp(bytes.data(), 0, 8) != 0) { + constexpr uint8_t signature[] = {0x89, 'P', 'N', 'G', 0x0d, 0x0a, 0x1a, 0x0a}; + if (bytes.size() < sizeof(signature) || + !std::equal(std::begin(signature), std::end(signature), bytes.begin())) { if (error) *error = "not a PNG stream"; return false; } - - png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - png_infop info = png ? png_create_info_struct(png) : nullptr; - if (!png || !info || setjmp(png_jmpbuf(png))) { - if (error) *error = "could not decode PNG"; - if (png) png_destroy_read_struct(&png, info ? &info : nullptr, nullptr); + if (bytes.size() > static_cast(std::numeric_limits::max())) { + if (error) *error = "PNG stream is too large"; return false; } - PngMemoryReader reader{bytes}; - png_set_read_fn(png, &reader, readPngMemory); - png_read_info(png, info); - const png_uint_32 width = png_get_image_width(png, info); - const png_uint_32 height = png_get_image_height(png, info); - const int colorType = png_get_color_type(png, info); - const int bitDepth = png_get_bit_depth(png, info); - if (bitDepth == 16) png_set_strip_16(png); - if (colorType == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png); - if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) png_set_expand_gray_1_2_4_to_8(png); - if (png_get_valid(png, info, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png); - if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png); - if ((colorType & PNG_COLOR_MASK_ALPHA) == 0) png_set_add_alpha(png, 0xff, PNG_FILLER_AFTER); - png_read_update_info(png, info); - - std::vector rgba(static_cast(width) * height * 4); - std::vector rows(height); - for (png_uint_32 y = 0; y < height; ++y) rows[y] = rgba.data() + static_cast(y) * width * 4; - png_read_image(png, rows.data()); - png_destroy_read_struct(&png, &info, nullptr); + int width = 0; + int height = 0; + int sourceChannels = 0; + stbi_uc* rgba = stbi_load_from_memory( + bytes.data(), static_cast(bytes.size()), + &width, &height, &sourceChannels, STBI_rgb_alpha); + if (!rgba || width <= 0 || height <= 0) { + if (error) { + const char* reason = stbi_failure_reason(); + *error = reason ? std::string("could not decode PNG: ") + reason + : "could not decode PNG"; + } + stbi_image_free(rgba); + return false; + } glGenTextures(1, &out.id); glBindTexture(GL_TEXTURE_2D, out.id); @@ -92,11 +72,12 @@ bool decodePng(std::span bytes, DdsTexture& out, std::string* err glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, static_cast(width), static_cast(height), - 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba.data()); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba); glBindTexture(GL_TEXTURE_2D, 0); - out.width = static_cast(width); - out.height = static_cast(height); + stbi_image_free(rgba); + out.width = width; + out.height = height; return true; }