From 87a9c2643fbf93e14e35683abfa59a6c02fd837d Mon Sep 17 00:00:00 2001 From: craftablescience Date: Sun, 8 Sep 2024 06:00:08 -0400 Subject: [PATCH 1/5] chore: make doxygen main page filename more soothing --- Doxyfile | 4 ++-- docs/{DOXYMAINPAGE.md => index.md} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename docs/{DOXYMAINPAGE.md => index.md} (100%) diff --git a/Doxyfile b/Doxyfile index 8e99946a2..e8f283383 100644 --- a/Doxyfile +++ b/Doxyfile @@ -948,7 +948,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = docs/DOXYMAINPAGE.md \ +INPUT = docs/index.md \ include \ src @@ -1165,7 +1165,7 @@ FILTER_SOURCE_PATTERNS = # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. -USE_MDFILE_AS_MAINPAGE = docs/DOXYMAINPAGE.md +USE_MDFILE_AS_MAINPAGE = docs/index.md # The Fortran standard specifies that for fixed formatted Fortran code all # characters from position 72 are to be considered as comment. A common diff --git a/docs/DOXYMAINPAGE.md b/docs/index.md similarity index 100% rename from docs/DOXYMAINPAGE.md rename to docs/index.md From e3461bd9c83301c7523f5efecda80a1acb7ad61b Mon Sep 17 00:00:00 2001 From: craftablescience Date: Fri, 30 Aug 2024 22:46:33 -0400 Subject: [PATCH 2/5] feat: add benchmarking, opencl, tbb, and print options at cmake configure time --- CMakeLists.txt | 95 +++- cmake/AddSourcePPLibrary.cmake | 16 +- cmake/PrintOptions.cmake | 10 + ext/_ext.cmake | 35 +- include/sourcepp/math/Float.h | 8 +- include/vtfpp/vtfpp.h | 2 +- test/bench/_bench.cpp | 3 + test/bench/vtfpp.cmake | 10 + test/bench/vtfpp.cpp | 94 ++++ test/res/vtfpp/{fmt_a8.vtf => fmt/A8.vtf} | Bin .../{fmt_abgr8888.vtf => fmt/ABGR8888.vtf} | Bin .../{fmt_argb8888.vtf => fmt/ARGB8888.vtf} | Bin .../vtfpp/{fmt_bgr565.vtf => fmt/BGR565.vtf} | Bin .../vtfpp/{fmt_bgr888.vtf => fmt/BGR888.vtf} | Bin .../BGR888_BLUESCREEN.vtf} | Bin .../{fmt_bgra4444.vtf => fmt/BGRA4444.vtf} | Bin .../{fmt_bgra5551.vtf => fmt/BGRA5551.vtf} | Bin 131160 -> 131160 bytes .../{fmt_bgra8888.vtf => fmt/BGRA8888.vtf} | Bin .../{fmt_bgrx5551.vtf => fmt/BGRX5551.vtf} | Bin .../{fmt_bgrx8888.vtf => fmt/BGRX8888.vtf} | Bin test/res/vtfpp/{fmt_dxt1.vtf => fmt/DXT1.vtf} | Bin .../DXT1_ONE_BIT_ALPHA.vtf} | Bin 32856 -> 32856 bytes test/res/vtfpp/{fmt_dxt3.vtf => fmt/DXT3.vtf} | Bin test/res/vtfpp/{fmt_dxt5.vtf => fmt/DXT5.vtf} | Bin test/res/vtfpp/{fmt_i8.vtf => fmt/I8.vtf} | Bin test/res/vtfpp/{fmt_ia88.vtf => fmt/IA88.vtf} | Bin .../vtfpp/{fmt_rgb565.vtf => fmt/RGB565.vtf} | Bin .../vtfpp/{fmt_rgb888.vtf => fmt/RGB888.vtf} | Bin .../RGB888_BLUESCREEN.vtf} | Bin .../{fmt_rgba8888.vtf => fmt/RGBA8888.vtf} | Bin test/res/vtfpp/{fmt_uv88.vtf => fmt/UV88.vtf} | Bin .../{fmt_uvlx8888.vtf => fmt/UVLX8888.vtf} | Bin .../{fmt_uvwq8888.vtf => fmt/UVWQ8888.vtf} | Bin test/res/vtfpp/{ => ver}/v70.vtf | Bin test/res/vtfpp/{ => ver}/v70_nomip.vtf | Bin test/res/vtfpp/{ => ver}/v70_nothumb.vtf | Bin .../res/vtfpp/{ => ver}/v70_nothumb_nomip.vtf | Bin test/res/vtfpp/{ => ver}/v71.vtf | Bin test/res/vtfpp/{ => ver}/v71_nomip.vtf | Bin test/res/vtfpp/{ => ver}/v71_nothumb.vtf | Bin .../res/vtfpp/{ => ver}/v71_nothumb_nomip.vtf | Bin test/res/vtfpp/{ => ver}/v72.vtf | Bin test/res/vtfpp/{ => ver}/v72_nomip.vtf | Bin test/res/vtfpp/{ => ver}/v72_nothumb.vtf | Bin .../res/vtfpp/{ => ver}/v72_nothumb_nomip.vtf | Bin test/res/vtfpp/{ => ver}/v75.vtf | Bin test/res/vtfpp/{ => ver}/v75_nomip.vtf | Bin test/res/vtfpp/{ => ver}/v75_nothumb.vtf | Bin .../res/vtfpp/{ => ver}/v75_nothumb_nomip.vtf | Bin test/res/vtfpp/{ => ver}/v76_c9.vtf | Bin test/res/vtfpp/{ => ver}/v76_nomip_c9.vtf | Bin test/vtfpp.cpp | 529 ++---------------- 52 files changed, 291 insertions(+), 511 deletions(-) create mode 100644 cmake/PrintOptions.cmake create mode 100644 test/bench/_bench.cpp create mode 100644 test/bench/vtfpp.cmake create mode 100644 test/bench/vtfpp.cpp rename test/res/vtfpp/{fmt_a8.vtf => fmt/A8.vtf} (100%) rename test/res/vtfpp/{fmt_abgr8888.vtf => fmt/ABGR8888.vtf} (100%) rename test/res/vtfpp/{fmt_argb8888.vtf => fmt/ARGB8888.vtf} (100%) rename test/res/vtfpp/{fmt_bgr565.vtf => fmt/BGR565.vtf} (100%) rename test/res/vtfpp/{fmt_bgr888.vtf => fmt/BGR888.vtf} (100%) rename test/res/vtfpp/{fmt_bgr888_bluescreen.vtf => fmt/BGR888_BLUESCREEN.vtf} (100%) rename test/res/vtfpp/{fmt_bgra4444.vtf => fmt/BGRA4444.vtf} (100%) rename test/res/vtfpp/{fmt_bgra5551.vtf => fmt/BGRA5551.vtf} (99%) rename test/res/vtfpp/{fmt_bgra8888.vtf => fmt/BGRA8888.vtf} (100%) rename test/res/vtfpp/{fmt_bgrx5551.vtf => fmt/BGRX5551.vtf} (100%) rename test/res/vtfpp/{fmt_bgrx8888.vtf => fmt/BGRX8888.vtf} (100%) rename test/res/vtfpp/{fmt_dxt1.vtf => fmt/DXT1.vtf} (100%) rename test/res/vtfpp/{fmt_dxt1_one_bit_alpha.vtf => fmt/DXT1_ONE_BIT_ALPHA.vtf} (99%) rename test/res/vtfpp/{fmt_dxt3.vtf => fmt/DXT3.vtf} (100%) rename test/res/vtfpp/{fmt_dxt5.vtf => fmt/DXT5.vtf} (100%) rename test/res/vtfpp/{fmt_i8.vtf => fmt/I8.vtf} (100%) rename test/res/vtfpp/{fmt_ia88.vtf => fmt/IA88.vtf} (100%) rename test/res/vtfpp/{fmt_rgb565.vtf => fmt/RGB565.vtf} (100%) rename test/res/vtfpp/{fmt_rgb888.vtf => fmt/RGB888.vtf} (100%) rename test/res/vtfpp/{fmt_rgb888_bluescreen.vtf => fmt/RGB888_BLUESCREEN.vtf} (100%) rename test/res/vtfpp/{fmt_rgba8888.vtf => fmt/RGBA8888.vtf} (100%) rename test/res/vtfpp/{fmt_uv88.vtf => fmt/UV88.vtf} (100%) rename test/res/vtfpp/{fmt_uvlx8888.vtf => fmt/UVLX8888.vtf} (100%) rename test/res/vtfpp/{fmt_uvwq8888.vtf => fmt/UVWQ8888.vtf} (100%) rename test/res/vtfpp/{ => ver}/v70.vtf (100%) rename test/res/vtfpp/{ => ver}/v70_nomip.vtf (100%) rename test/res/vtfpp/{ => ver}/v70_nothumb.vtf (100%) rename test/res/vtfpp/{ => ver}/v70_nothumb_nomip.vtf (100%) rename test/res/vtfpp/{ => ver}/v71.vtf (100%) rename test/res/vtfpp/{ => ver}/v71_nomip.vtf (100%) rename test/res/vtfpp/{ => ver}/v71_nothumb.vtf (100%) rename test/res/vtfpp/{ => ver}/v71_nothumb_nomip.vtf (100%) rename test/res/vtfpp/{ => ver}/v72.vtf (100%) rename test/res/vtfpp/{ => ver}/v72_nomip.vtf (100%) rename test/res/vtfpp/{ => ver}/v72_nothumb.vtf (100%) rename test/res/vtfpp/{ => ver}/v72_nothumb_nomip.vtf (100%) rename test/res/vtfpp/{ => ver}/v75.vtf (100%) rename test/res/vtfpp/{ => ver}/v75_nomip.vtf (100%) rename test/res/vtfpp/{ => ver}/v75_nothumb.vtf (100%) rename test/res/vtfpp/{ => ver}/v75_nothumb_nomip.vtf (100%) rename test/res/vtfpp/{ => ver}/v76_c9.vtf (100%) rename test/res/vtfpp/{ => ver}/v76_nomip_c9.vtf (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index a0295d750..25ecb5e85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,22 +13,27 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# Options -option(SOURCEPP_LIBS_START_ENABLED "Libraries will all build by default" ON) -option(SOURCEPP_USE_BSPPP "Build bsppp library" ${SOURCEPP_LIBS_START_ENABLED}) -option(SOURCEPP_USE_DMXPP "Build dmxpp library" ${SOURCEPP_LIBS_START_ENABLED}) -option(SOURCEPP_USE_GAMEPP "Build gamepp library" ${SOURCEPP_LIBS_START_ENABLED}) -option(SOURCEPP_USE_KVPP "Build kvpp library" ${SOURCEPP_LIBS_START_ENABLED}) -option(SOURCEPP_USE_MDLPP "Build mdlpp library" ${SOURCEPP_LIBS_START_ENABLED}) -option(SOURCEPP_USE_STEAMPP "Build steampp library" ${SOURCEPP_LIBS_START_ENABLED}) -option(SOURCEPP_USE_TOOLPP "Build toolpp library" ${SOURCEPP_LIBS_START_ENABLED}) -option(SOURCEPP_USE_VICEPP "Build vicepp library" ${SOURCEPP_LIBS_START_ENABLED}) -option(SOURCEPP_USE_VPKPP "Build vpkpp library" ${SOURCEPP_LIBS_START_ENABLED}) -option(SOURCEPP_USE_VTFPP "Build vtfpp library" ${SOURCEPP_LIBS_START_ENABLED}) -option(SOURCEPP_BUILD_C_WRAPPERS "Build C wrappers for supported libraries" OFF) -option(SOURCEPP_BUILD_TESTS "Build tests for enabled libraries" OFF) -option(SOURCEPP_BUILD_WIN7_COMPAT "Build with Windows 7 compatibility" OFF) -option(SOURCEPP_LINK_STATIC_MSVC_RUNTIME "Link to static MSVC runtime library" OFF) +# Options (update print_options at the bottom of this file when modifying) +option(SOURCEPP_LIBS_START_ENABLED "Libraries will all build by default" ON) +option(SOURCEPP_USE_BSPPP "Build bsppp library" ${SOURCEPP_LIBS_START_ENABLED}) +option(SOURCEPP_USE_DMXPP "Build dmxpp library" ${SOURCEPP_LIBS_START_ENABLED}) +option(SOURCEPP_USE_GAMEPP "Build gamepp library" ${SOURCEPP_LIBS_START_ENABLED}) +option(SOURCEPP_USE_KVPP "Build kvpp library" ${SOURCEPP_LIBS_START_ENABLED}) +option(SOURCEPP_USE_MDLPP "Build mdlpp library" ${SOURCEPP_LIBS_START_ENABLED}) +option(SOURCEPP_USE_STEAMPP "Build steampp library" ${SOURCEPP_LIBS_START_ENABLED}) +option(SOURCEPP_USE_TOOLPP "Build toolpp library" ${SOURCEPP_LIBS_START_ENABLED}) +option(SOURCEPP_USE_VICEPP "Build vicepp library" ${SOURCEPP_LIBS_START_ENABLED}) +option(SOURCEPP_USE_VPKPP "Build vpkpp library" ${SOURCEPP_LIBS_START_ENABLED}) +option(SOURCEPP_USE_VTFPP "Build vtfpp library" ${SOURCEPP_LIBS_START_ENABLED}) + +option(SOURCEPP_BUILD_BENCHMARKS "Build benchmarks for supported libraries" OFF) +option(SOURCEPP_BUILD_C_WRAPPERS "Build C wrappers for supported libraries" OFF) +option(SOURCEPP_BUILD_WITH_OPENCL "Build with support for GPU compute" OFF) +option(SOURCEPP_BUILD_WITH_TBB "Build with support for std::execution" OFF) +option(SOURCEPP_BUILD_TESTS "Build tests for supported libraries" OFF) +option(SOURCEPP_BUILD_WIN7_COMPAT "Build with Windows 7 compatibility" OFF) + +option(SOURCEPP_LINK_STATIC_MSVC_RUNTIME "Link to static MSVC runtime library" OFF) # Option overrides @@ -43,6 +48,11 @@ if(SOURCEPP_USE_VPKPP) set(SOURCEPP_USE_KVPP ON CACHE INTERNAL "" FORCE) endif() +if(MSVC) + # MSVC does not rely on tbb for std::execution policies, so we can force this on + set(SOURCEPP_BUILD_WITH_TBB ON CACHE INTERNAL "" FORCE) +endif() + # Set defaults after project call if(PROJECT_IS_TOP_LEVEL) @@ -59,6 +69,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") include(AddPrettyParser) include(AddSourcePPLibrary) include(IncludeSubdirectory) +include(PrintOptions) # Include thirdparty libraries @@ -82,7 +93,7 @@ if(SOURCEPP_BUILD_TESTS) FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG v1.14.0) + GIT_TAG v1.15.2) FetchContent_MakeAvailable(googletest) enable_testing() set(${SOURCEPP_TEST_NAME}_SOURCES "") @@ -90,17 +101,32 @@ if(SOURCEPP_BUILD_TESTS) endif() +# Benchmarks, part 1 +if(SOURCEPP_BUILD_BENCHMARKS) + set(SOURCEPP_BENCH_NAME "${PROJECT_NAME}_bench") + include(FetchContent) + FetchContent_Declare( + benchmark + GIT_REPOSITORY https://github.com/google/benchmark.git + GIT_TAG v1.9.0) + set(BENCHMARK_ENABLE_TESTING OFF CACHE INTERNAL "" FORCE) + FetchContent_MakeAvailable(benchmark) + list(APPEND ${SOURCEPP_BENCH_NAME}_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/test/bench/_bench.cpp") + list(APPEND ${SOURCEPP_BENCH_NAME}_DEPS benchmark::benchmark) +endif() + + # Add libraries -add_sourcepp_library(bsppp NO_TEST) # sourcepp::bsppp -add_sourcepp_library(dmxpp) # sourcepp::dmxpp -add_sourcepp_library(gamepp) # sourcepp::gamepp -add_sourcepp_library(kvpp) # sourcepp::kvpp -add_sourcepp_library(mdlpp) # sourcepp::mdlpp -add_sourcepp_library(steampp C) # sourcepp::steampp -add_sourcepp_library(toolpp) # sourcepp::toolpp -add_sourcepp_library(vicepp C CSHARP) # sourcepp::vicepp -add_sourcepp_library(vpkpp C CSHARP NO_TEST) # sourcepp::vpkpp -add_sourcepp_library(vtfpp) # sourcepp::vtfpp +add_sourcepp_library(bsppp NO_TEST ) # sourcepp::bsppp +add_sourcepp_library(dmxpp ) # sourcepp::dmxpp +add_sourcepp_library(gamepp ) # sourcepp::gamepp +add_sourcepp_library(kvpp ) # sourcepp::kvpp +add_sourcepp_library(mdlpp ) # sourcepp::mdlpp +add_sourcepp_library(steampp C ) # sourcepp::steampp +add_sourcepp_library(toolpp ) # sourcepp::toolpp +add_sourcepp_library(vicepp C CSHARP ) # sourcepp::vicepp +add_sourcepp_library(vpkpp C CSHARP NO_TEST ) # sourcepp::vpkpp +add_sourcepp_library(vtfpp BENCH) # sourcepp::vtfpp # Tests, part 2 @@ -111,3 +137,18 @@ if(SOURCEPP_BUILD_TESTS) include(GoogleTest) gtest_discover_tests(${SOURCEPP_TEST_NAME}) endif() + + +# Benchmarks, part 2 +if(SOURCEPP_BUILD_BENCHMARKS) + add_executable(${SOURCEPP_BENCH_NAME} ${${SOURCEPP_BENCH_NAME}_SOURCES}) + target_link_libraries(${SOURCEPP_BENCH_NAME} PUBLIC ${${SOURCEPP_BENCH_NAME}_DEPS}) + target_compile_definitions(${SOURCEPP_BENCH_NAME} PUBLIC ASSET_ROOT="${CMAKE_CURRENT_SOURCE_DIR}/test/res/") +endif() + + +# Print options +print_options(OPTIONS + USE_BSPPP USE_DMXPP USE_GAMEPP USE_KVPP USE_MDLPP USE_STEAMPP USE_TOOLPP USE_VICEPP USE_VPKPP USE_VTFPP + BUILD_BENCHMARKS BUILD_C_WRAPPERS BUILD_WITH_OPENCL BUILD_WITH_TBB BUILD_TESTS BUILD_WIN7_COMPAT + LINK_STATIC_MSVC_RUNTIME) diff --git a/cmake/AddSourcePPLibrary.cmake b/cmake/AddSourcePPLibrary.cmake index 8fb393cc7..b2d503455 100644 --- a/cmake/AddSourcePPLibrary.cmake +++ b/cmake/AddSourcePPLibrary.cmake @@ -1,5 +1,5 @@ function(add_sourcepp_library TARGET) - cmake_parse_arguments(PARSE_ARGV 1 OPTIONS "C;CSHARP;NO_TEST" "" "") + cmake_parse_arguments(PARSE_ARGV 1 OPTIONS "C;CSHARP;NO_TEST;BENCH" "" "") string(TOUPPER ${TARGET} TARGET_UPPER) if(SOURCEPP_USE_${TARGET_UPPER}) # Add C++ @@ -17,11 +17,23 @@ function(add_sourcepp_library TARGET) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/lang/csharp/src/sourcepp/TARGET.csproj.in" "${CMAKE_CURRENT_SOURCE_DIR}/lang/csharp/src/${TARGET}/${TARGET}.csproj") endif() + set(PROPAGATE_VARS "") + # Add tests if(NOT OPTIONS_NO_TEST AND SOURCEPP_BUILD_TESTS) list(APPEND ${SOURCEPP_TEST_NAME}_DEPS ${TARGET}) list(APPEND ${SOURCEPP_TEST_NAME}_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/test/${TARGET}.cpp") - return(PROPAGATE ${SOURCEPP_TEST_NAME}_DEPS ${SOURCEPP_TEST_NAME}_SOURCES) + list(APPEND PROPAGATE_VARS ${SOURCEPP_TEST_NAME}_DEPS ${SOURCEPP_TEST_NAME}_SOURCES) + endif() + + # Add benchmarks + if(OPTIONS_BENCH AND SOURCEPP_BUILD_BENCHMARKS) + include("${CMAKE_CURRENT_SOURCE_DIR}/test/bench/${TARGET}.cmake") + list(APPEND ${SOURCEPP_BENCH_NAME}_DEPS ${TARGET}) + list(APPEND ${SOURCEPP_BENCH_NAME}_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/test/bench/${TARGET}.cpp") + list(APPEND PROPAGATE_VARS ${SOURCEPP_BENCH_NAME}_DEPS ${SOURCEPP_BENCH_NAME}_SOURCES) endif() + + return(PROPAGATE ${PROPAGATE_VARS}) endif() endfunction() diff --git a/cmake/PrintOptions.cmake b/cmake/PrintOptions.cmake new file mode 100644 index 000000000..dc37a9f4d --- /dev/null +++ b/cmake/PrintOptions.cmake @@ -0,0 +1,10 @@ +function(print_options) + cmake_parse_arguments(PARSE_ARGV 0 IN "" "" "OPTIONS") + foreach (OPT ${IN_OPTIONS}) + if(SOURCEPP_${OPT}) + message(STATUS "[X] SOURCEPP_${OPT}") + else() + message(STATUS "[ ] SOURCEPP_${OPT}") + endif() + endforeach() +endfunction() diff --git a/ext/_ext.cmake b/ext/_ext.cmake index 7ce06c195..9f9d9dc3c 100644 --- a/ext/_ext.cmake +++ b/ext/_ext.cmake @@ -10,8 +10,8 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/compressonator") # cryptopp if(NOT TARGET cryptopp::cryptopp) - set(CRYPTOPP_BUILD_TESTING OFF CACHE INTERNAL "") - set(CRYPTOPP_INSTALL OFF CACHE INTERNAL "") + set(CRYPTOPP_BUILD_TESTING OFF CACHE INTERNAL "" FORCE) + set(CRYPTOPP_INSTALL OFF CACHE INTERNAL "" FORCE) add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/cryptopp") # hack: clang on windows (NOT clang-cl) needs these to compile cryptopp @@ -39,7 +39,7 @@ endif() # minizip-ng if(NOT TARGET MINIZIP::minizip) - set(MZ_COMPAT OFF CACHE INTERNAL "") + set(MZ_COMPAT OFF CACHE INTERNAL "" FORCE) set(MZ_ZLIB ON CACHE INTERNAL "") set(MZ_BZIP2 ON CACHE INTERNAL "") set(MZ_LZMA ON CACHE INTERNAL "") @@ -50,7 +50,7 @@ if(NOT TARGET MINIZIP::minizip) set(MZ_OPENSSL OFF CACHE INTERNAL "") set(MZ_FETCH_LIBS ON CACHE INTERNAL "") set(MZ_FORCE_FETCH_LIBS ON CACHE INTERNAL "") - set(SKIP_INSTALL_ALL ON CACHE INTERNAL "") + set(SKIP_INSTALL_ALL ON CACHE INTERNAL "" FORCE) add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/minizip-ng") if(SOURCEPP_BUILD_WIN7_COMPAT) @@ -63,5 +63,32 @@ if(NOT TARGET MINIZIP::minizip) endif() +# OpenCL +if(SOURCEPP_BUILD_WITH_OPENCL AND NOT TARGET OpenCL::OpenCL) + find_package(OpenCL) + if(NOT OpenCL_FOUND) + set(SOURCEPP_BUILD_WITH_OPENCL OFF CACHE INTERNAL "" FORCE) + endif() +endif() + +function(sourcepp_add_opencl TARGET) + if(SOURCEPP_BUILD_WITH_OPENCL) + target_compile_definitions(${TARGET} PRIVATE SOURCEPP_BUILD_WITH_OPENCL) + target_link_libraries(${TARGET} PRIVATE OpenCL::OpenCL) + endif() +endfunction() + + # stb add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/stb") + + +# TBB +function(sourcepp_add_tbb TARGET) + if(SOURCEPP_BUILD_WITH_TBB) + target_compile_definitions(${TARGET} PRIVATE SOURCEPP_BUILD_WITH_TBB) + if(NOT MSVC) + target_link_libraries(${TARGET} PRIVATE tbb) + endif() + endif() +endfunction() diff --git a/include/sourcepp/math/Float.h b/include/sourcepp/math/Float.h index bf7bd971f..c2ae7d891 100644 --- a/include/sourcepp/math/Float.h +++ b/include/sourcepp/math/Float.h @@ -7,11 +7,11 @@ namespace sourcepp::math { // https://stackoverflow.com/a/60047308 class FloatCompressed16 { public: - explicit FloatCompressed16(uint16_t in) { - this->data = in; - } + explicit FloatCompressed16(uint16_t in) + : data(in) {} - explicit FloatCompressed16(float in) { // IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits + // NOLINTNEXTLINE(*-explicit-constructor) + FloatCompressed16(float in) { // IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits const auto b = *reinterpret_cast(&in) + 0x00001000; // round-to-nearest-even: add last bit after truncated mantissa const auto e = (b & 0x7F800000) >> 23; // exponent const auto m = b & 0x007FFFFF; // mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding diff --git a/include/vtfpp/vtfpp.h b/include/vtfpp/vtfpp.h index 50ca62326..a65fcdfc0 100644 --- a/include/vtfpp/vtfpp.h +++ b/include/vtfpp/vtfpp.h @@ -164,7 +164,7 @@ class VTF { uint8_t compressionLevel = 6; }; - /// This value is only valid when passed to VTFWriter::create through CreationOptions or VTF::setFormat + /// This value is only valid when passed to VTF::create through CreationOptions or VTF::setFormat static constexpr ImageFormat FORMAT_DEFAULT = static_cast(-1); static constexpr int32_t MAX_RESOURCES = 32; diff --git a/test/bench/_bench.cpp b/test/bench/_bench.cpp new file mode 100644 index 000000000..71fefa047 --- /dev/null +++ b/test/bench/_bench.cpp @@ -0,0 +1,3 @@ +#include + +BENCHMARK_MAIN(); diff --git a/test/bench/vtfpp.cmake b/test/bench/vtfpp.cmake new file mode 100644 index 000000000..e69d93913 --- /dev/null +++ b/test/bench/vtfpp.cmake @@ -0,0 +1,10 @@ +FetchContent_Declare( + vtflib + GIT_REPOSITORY https://github.com/StrataSource/VTFLib.git + GIT_TAG origin/master) +FetchContent_MakeAvailable(vtflib) +set_target_properties(vtflib PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + +list(APPEND ${SOURCEPP_BENCH_NAME}_DEPS vtflib) diff --git a/test/bench/vtfpp.cpp b/test/bench/vtfpp.cpp new file mode 100644 index 000000000..38ce9bf80 --- /dev/null +++ b/test/bench/vtfpp.cpp @@ -0,0 +1,94 @@ +#include + +#include +#include +#include +#include + +using namespace sourcepp; +using namespace vtfpp; + +// GCC +#pragma GCC optimize("O0") + +// Clang +#pragma clang optimize off + +// MSVC +#pragma optimize("", off) + +namespace { + +#define BM_FORMAT(Format, VTFLibFormat) \ + void BM_vtfpp_convert_256x256_##Format##toRGBA8888(benchmark::State& state) { \ + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt/" #Format ".vtf")}; \ + for ([[maybe_unused]] auto _: state) { \ + auto data = vtf.getImageDataAsRGBA8888(); \ + } \ + } \ + BENCHMARK(BM_vtfpp_convert_256x256_##Format##toRGBA8888); \ + void BM_vtflib_convert_256x256_##Format##toRGBA8888(benchmark::State& state) { \ + VTFLib::CVTFFile vtf; \ + vtf.Load(ASSET_ROOT "vtfpp/fmt/" #Format ".vtf"); \ + for ([[maybe_unused]] auto _: state) { \ + std::vector data(vtf.GetWidth() * vtf.GetHeight() * sizeof(ImagePixel::RGBA8888)); \ + VTFLib::CVTFFile::ConvertToRGBA8888(vtf.GetData(0, 0, 0, 0), reinterpret_cast(data.data()), vtf.GetWidth(), vtf.GetHeight(), IMAGE_FORMAT_##VTFLibFormat); \ + } \ + } \ + BENCHMARK(BM_vtflib_convert_256x256_##Format##toRGBA8888) + +BM_FORMAT(A8, A8); +BM_FORMAT(ABGR8888, ABGR8888); +BM_FORMAT(ARGB8888, ARGB8888); +BM_FORMAT(BGR565, BGR565); +BM_FORMAT(BGR888, BGR888); +BM_FORMAT(BGR888_BLUESCREEN, BGR888_BLUESCREEN); +BM_FORMAT(BGRA4444, BGRA4444); +BM_FORMAT(BGRA5551, BGRA5551); +BM_FORMAT(BGRA8888, BGRA8888); +BM_FORMAT(BGRX5551, BGRX5551); +BM_FORMAT(BGRX8888, BGRX8888); +BM_FORMAT(DXT1, DXT1); +BM_FORMAT(DXT1_ONE_BIT_ALPHA, DXT1_ONEBITALPHA); +BM_FORMAT(DXT3, DXT3); +BM_FORMAT(DXT5, DXT5); +BM_FORMAT(I8, I8); +BM_FORMAT(IA88, IA88); +BM_FORMAT(RGB565, RGB565); +BM_FORMAT(RGB888, RGB888); +BM_FORMAT(RGB888_BLUESCREEN, RGB888_BLUESCREEN); +BM_FORMAT(RGBA8888, RGBA8888); +BM_FORMAT(UV88, UV88); +BM_FORMAT(UVLX8888, UVLX8888); +BM_FORMAT(UVWQ8888, UVWQ8888); + +void BM_vtfpp_createVTF(benchmark::State& state) { + VTF image{ASSET_ROOT "vtfpp/fmt/RGBA8888.vtf"}; + auto imageData = image.getImageDataAsRGBA8888(); + + for ([[maybe_unused]] auto _: state) { + auto data = VTF::create(imageData, ImageFormat::RGBA8888, 2048, 2048, {}); + } +} +BENCHMARK(BM_vtfpp_createVTF); + +void BM_vtflib_createVTF(benchmark::State& state) { + VTF image{ASSET_ROOT "vtfpp/fmt/RGBA8888.vtf"}; + auto imageData = image.getImageDataAsRGBA8888(); + + for ([[maybe_unused]] auto _: state) { + VTFLib::CVTFFile vtf; + vtf.Create(2048, 2048, reinterpret_cast(imageData.data()), { + .uiVersion = {7, 4}, + .ImageFormat = IMAGE_FORMAT_DXT1, + .bMipmaps = true, + .bThumbnail = true, + .bReflectivity = true, + }); + std::vector data(vtf.GetSize()); + vtf.Save(data.data()); + } +} +BENCHMARK(BM_vtflib_createVTF); + +} // namespace diff --git a/test/res/vtfpp/fmt_a8.vtf b/test/res/vtfpp/fmt/A8.vtf similarity index 100% rename from test/res/vtfpp/fmt_a8.vtf rename to test/res/vtfpp/fmt/A8.vtf diff --git a/test/res/vtfpp/fmt_abgr8888.vtf b/test/res/vtfpp/fmt/ABGR8888.vtf similarity index 100% rename from test/res/vtfpp/fmt_abgr8888.vtf rename to test/res/vtfpp/fmt/ABGR8888.vtf diff --git a/test/res/vtfpp/fmt_argb8888.vtf b/test/res/vtfpp/fmt/ARGB8888.vtf similarity index 100% rename from test/res/vtfpp/fmt_argb8888.vtf rename to test/res/vtfpp/fmt/ARGB8888.vtf diff --git a/test/res/vtfpp/fmt_bgr565.vtf b/test/res/vtfpp/fmt/BGR565.vtf similarity index 100% rename from test/res/vtfpp/fmt_bgr565.vtf rename to test/res/vtfpp/fmt/BGR565.vtf diff --git a/test/res/vtfpp/fmt_bgr888.vtf b/test/res/vtfpp/fmt/BGR888.vtf similarity index 100% rename from test/res/vtfpp/fmt_bgr888.vtf rename to test/res/vtfpp/fmt/BGR888.vtf diff --git a/test/res/vtfpp/fmt_bgr888_bluescreen.vtf b/test/res/vtfpp/fmt/BGR888_BLUESCREEN.vtf similarity index 100% rename from test/res/vtfpp/fmt_bgr888_bluescreen.vtf rename to test/res/vtfpp/fmt/BGR888_BLUESCREEN.vtf diff --git a/test/res/vtfpp/fmt_bgra4444.vtf b/test/res/vtfpp/fmt/BGRA4444.vtf similarity index 100% rename from test/res/vtfpp/fmt_bgra4444.vtf rename to test/res/vtfpp/fmt/BGRA4444.vtf diff --git a/test/res/vtfpp/fmt_bgra5551.vtf b/test/res/vtfpp/fmt/BGRA5551.vtf similarity index 99% rename from test/res/vtfpp/fmt_bgra5551.vtf rename to test/res/vtfpp/fmt/BGRA5551.vtf index 7e53c4ffe196e93f1156c8b00909937aa5a5c95d..9366768fde37b217ad508a76bffa6e204c08652c 100644 GIT binary patch delta 19 acmcc7z;UC2V}dB7aHCkO7~@niCMN(yX$71B delta 19 acmcc7z;UC2V}d9nbE8{ delta 14 Vcmcc7z;vU5X@V#t^F}eJ1^_Ao1m^$% diff --git a/test/res/vtfpp/fmt_dxt3.vtf b/test/res/vtfpp/fmt/DXT3.vtf similarity index 100% rename from test/res/vtfpp/fmt_dxt3.vtf rename to test/res/vtfpp/fmt/DXT3.vtf diff --git a/test/res/vtfpp/fmt_dxt5.vtf b/test/res/vtfpp/fmt/DXT5.vtf similarity index 100% rename from test/res/vtfpp/fmt_dxt5.vtf rename to test/res/vtfpp/fmt/DXT5.vtf diff --git a/test/res/vtfpp/fmt_i8.vtf b/test/res/vtfpp/fmt/I8.vtf similarity index 100% rename from test/res/vtfpp/fmt_i8.vtf rename to test/res/vtfpp/fmt/I8.vtf diff --git a/test/res/vtfpp/fmt_ia88.vtf b/test/res/vtfpp/fmt/IA88.vtf similarity index 100% rename from test/res/vtfpp/fmt_ia88.vtf rename to test/res/vtfpp/fmt/IA88.vtf diff --git a/test/res/vtfpp/fmt_rgb565.vtf b/test/res/vtfpp/fmt/RGB565.vtf similarity index 100% rename from test/res/vtfpp/fmt_rgb565.vtf rename to test/res/vtfpp/fmt/RGB565.vtf diff --git a/test/res/vtfpp/fmt_rgb888.vtf b/test/res/vtfpp/fmt/RGB888.vtf similarity index 100% rename from test/res/vtfpp/fmt_rgb888.vtf rename to test/res/vtfpp/fmt/RGB888.vtf diff --git a/test/res/vtfpp/fmt_rgb888_bluescreen.vtf b/test/res/vtfpp/fmt/RGB888_BLUESCREEN.vtf similarity index 100% rename from test/res/vtfpp/fmt_rgb888_bluescreen.vtf rename to test/res/vtfpp/fmt/RGB888_BLUESCREEN.vtf diff --git a/test/res/vtfpp/fmt_rgba8888.vtf b/test/res/vtfpp/fmt/RGBA8888.vtf similarity index 100% rename from test/res/vtfpp/fmt_rgba8888.vtf rename to test/res/vtfpp/fmt/RGBA8888.vtf diff --git a/test/res/vtfpp/fmt_uv88.vtf b/test/res/vtfpp/fmt/UV88.vtf similarity index 100% rename from test/res/vtfpp/fmt_uv88.vtf rename to test/res/vtfpp/fmt/UV88.vtf diff --git a/test/res/vtfpp/fmt_uvlx8888.vtf b/test/res/vtfpp/fmt/UVLX8888.vtf similarity index 100% rename from test/res/vtfpp/fmt_uvlx8888.vtf rename to test/res/vtfpp/fmt/UVLX8888.vtf diff --git a/test/res/vtfpp/fmt_uvwq8888.vtf b/test/res/vtfpp/fmt/UVWQ8888.vtf similarity index 100% rename from test/res/vtfpp/fmt_uvwq8888.vtf rename to test/res/vtfpp/fmt/UVWQ8888.vtf diff --git a/test/res/vtfpp/v70.vtf b/test/res/vtfpp/ver/v70.vtf similarity index 100% rename from test/res/vtfpp/v70.vtf rename to test/res/vtfpp/ver/v70.vtf diff --git a/test/res/vtfpp/v70_nomip.vtf b/test/res/vtfpp/ver/v70_nomip.vtf similarity index 100% rename from test/res/vtfpp/v70_nomip.vtf rename to test/res/vtfpp/ver/v70_nomip.vtf diff --git a/test/res/vtfpp/v70_nothumb.vtf b/test/res/vtfpp/ver/v70_nothumb.vtf similarity index 100% rename from test/res/vtfpp/v70_nothumb.vtf rename to test/res/vtfpp/ver/v70_nothumb.vtf diff --git a/test/res/vtfpp/v70_nothumb_nomip.vtf b/test/res/vtfpp/ver/v70_nothumb_nomip.vtf similarity index 100% rename from test/res/vtfpp/v70_nothumb_nomip.vtf rename to test/res/vtfpp/ver/v70_nothumb_nomip.vtf diff --git a/test/res/vtfpp/v71.vtf b/test/res/vtfpp/ver/v71.vtf similarity index 100% rename from test/res/vtfpp/v71.vtf rename to test/res/vtfpp/ver/v71.vtf diff --git a/test/res/vtfpp/v71_nomip.vtf b/test/res/vtfpp/ver/v71_nomip.vtf similarity index 100% rename from test/res/vtfpp/v71_nomip.vtf rename to test/res/vtfpp/ver/v71_nomip.vtf diff --git a/test/res/vtfpp/v71_nothumb.vtf b/test/res/vtfpp/ver/v71_nothumb.vtf similarity index 100% rename from test/res/vtfpp/v71_nothumb.vtf rename to test/res/vtfpp/ver/v71_nothumb.vtf diff --git a/test/res/vtfpp/v71_nothumb_nomip.vtf b/test/res/vtfpp/ver/v71_nothumb_nomip.vtf similarity index 100% rename from test/res/vtfpp/v71_nothumb_nomip.vtf rename to test/res/vtfpp/ver/v71_nothumb_nomip.vtf diff --git a/test/res/vtfpp/v72.vtf b/test/res/vtfpp/ver/v72.vtf similarity index 100% rename from test/res/vtfpp/v72.vtf rename to test/res/vtfpp/ver/v72.vtf diff --git a/test/res/vtfpp/v72_nomip.vtf b/test/res/vtfpp/ver/v72_nomip.vtf similarity index 100% rename from test/res/vtfpp/v72_nomip.vtf rename to test/res/vtfpp/ver/v72_nomip.vtf diff --git a/test/res/vtfpp/v72_nothumb.vtf b/test/res/vtfpp/ver/v72_nothumb.vtf similarity index 100% rename from test/res/vtfpp/v72_nothumb.vtf rename to test/res/vtfpp/ver/v72_nothumb.vtf diff --git a/test/res/vtfpp/v72_nothumb_nomip.vtf b/test/res/vtfpp/ver/v72_nothumb_nomip.vtf similarity index 100% rename from test/res/vtfpp/v72_nothumb_nomip.vtf rename to test/res/vtfpp/ver/v72_nothumb_nomip.vtf diff --git a/test/res/vtfpp/v75.vtf b/test/res/vtfpp/ver/v75.vtf similarity index 100% rename from test/res/vtfpp/v75.vtf rename to test/res/vtfpp/ver/v75.vtf diff --git a/test/res/vtfpp/v75_nomip.vtf b/test/res/vtfpp/ver/v75_nomip.vtf similarity index 100% rename from test/res/vtfpp/v75_nomip.vtf rename to test/res/vtfpp/ver/v75_nomip.vtf diff --git a/test/res/vtfpp/v75_nothumb.vtf b/test/res/vtfpp/ver/v75_nothumb.vtf similarity index 100% rename from test/res/vtfpp/v75_nothumb.vtf rename to test/res/vtfpp/ver/v75_nothumb.vtf diff --git a/test/res/vtfpp/v75_nothumb_nomip.vtf b/test/res/vtfpp/ver/v75_nothumb_nomip.vtf similarity index 100% rename from test/res/vtfpp/v75_nothumb_nomip.vtf rename to test/res/vtfpp/ver/v75_nothumb_nomip.vtf diff --git a/test/res/vtfpp/v76_c9.vtf b/test/res/vtfpp/ver/v76_c9.vtf similarity index 100% rename from test/res/vtfpp/v76_c9.vtf rename to test/res/vtfpp/ver/v76_c9.vtf diff --git a/test/res/vtfpp/v76_nomip_c9.vtf b/test/res/vtfpp/ver/v76_nomip_c9.vtf similarity index 100% rename from test/res/vtfpp/v76_nomip_c9.vtf rename to test/res/vtfpp/ver/v76_nomip_c9.vtf diff --git a/test/vtfpp.cpp b/test/vtfpp.cpp index c76d986bc..96ddc5be6 100644 --- a/test/vtfpp.cpp +++ b/test/vtfpp.cpp @@ -8,461 +8,44 @@ using namespace vtfpp; #if 0 -TEST(vtfpp, read_fmt_rgba8888) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_rgba8888.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA); - EXPECT_EQ(vtf.getFormat(), ImageFormat::RGBA8888); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_rgba8888.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_abgr8888) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_abgr8888.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA); - EXPECT_EQ(vtf.getFormat(), ImageFormat::ABGR8888); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_abgr8888.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_rgb888) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_rgb888.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NONE); - EXPECT_EQ(vtf.getFormat(), ImageFormat::RGB888); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_rgb888.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_bgr888) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_bgr888.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD); - EXPECT_EQ(vtf.getFormat(), ImageFormat::BGR888); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_bgr888.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_rgb565) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_rgb565.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD); - EXPECT_EQ(vtf.getFormat(), ImageFormat::RGB565); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_rgb565.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_i8) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_i8.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD); - EXPECT_EQ(vtf.getFormat(), ImageFormat::I8); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_i8.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_ia88) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_ia88.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA); - EXPECT_EQ(vtf.getFormat(), ImageFormat::IA88); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_ia88.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_a8) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_a8.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA); - EXPECT_EQ(vtf.getFormat(), ImageFormat::A8); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_a8.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_rgb888_bluescreen) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_rgb888_bluescreen.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD); - EXPECT_EQ(vtf.getFormat(), ImageFormat::RGB888_BLUESCREEN); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_rgb888_bluescreen.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_bgr888_bluescreen) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_bgr888_bluescreen.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD); - EXPECT_EQ(vtf.getFormat(), ImageFormat::BGR888_BLUESCREEN); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_bgr888_bluescreen.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_argb8888) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_argb8888.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA); - EXPECT_EQ(vtf.getFormat(), ImageFormat::ARGB8888); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_argb8888.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_bgra8888) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_bgra8888.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA); - EXPECT_EQ(vtf.getFormat(), ImageFormat::BGRA8888); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_bgra8888.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_dxt1) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_dxt1.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD); - EXPECT_EQ(vtf.getFormat(), ImageFormat::DXT1); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_dxt1.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_dxt3) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_dxt3.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA); - EXPECT_EQ(vtf.getFormat(), ImageFormat::DXT3); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_dxt3.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_dxt5) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_dxt5.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA); - EXPECT_EQ(vtf.getFormat(), ImageFormat::DXT5); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_dxt5.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_bgrx8888) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_bgrx8888.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD); - EXPECT_EQ(vtf.getFormat(), ImageFormat::BGRX8888); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_bgrx8888.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_bgr565) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_bgr565.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD); - EXPECT_EQ(vtf.getFormat(), ImageFormat::BGR565); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_bgr565.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_bgrx5551) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_bgrx5551.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD); - EXPECT_EQ(vtf.getFormat(), ImageFormat::BGRX5551); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_bgrx5551.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_bgra4444) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_bgra4444.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA); - EXPECT_EQ(vtf.getFormat(), ImageFormat::BGRA4444); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_bgra4444.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_dxt1_one_bit_alpha) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_dxt1_one_bit_alpha.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD); - EXPECT_EQ(vtf.getFormat(), ImageFormat::DXT1_ONE_BIT_ALPHA); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_dxt1_one_bit_alpha.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_bgra5551) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_bgra5551.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD); - EXPECT_EQ(vtf.getFormat(), ImageFormat::BGRA5551); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_bgra5551.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_uv88) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_uv88.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD); - EXPECT_EQ(vtf.getFormat(), ImageFormat::UV88); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_uv88.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_uvwq8888) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_uvwq8888.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA); - EXPECT_EQ(vtf.getFormat(), ImageFormat::UVWQ8888); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_uvwq8888.png", vtf.convertAndSaveImageDataToFile()); -} - -TEST(vtfpp, read_fmt_uvlx8888) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt_uvlx8888.vtf")}; - ASSERT_TRUE(vtf); - - // Header - EXPECT_EQ(vtf.getWidth(), 256); - EXPECT_EQ(vtf.getHeight(), 256); - EXPECT_EQ(vtf.getFlags(), VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA); - EXPECT_EQ(vtf.getFormat(), ImageFormat::UVLX8888); - - // Resources - const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); - ASSERT_TRUE(image); - EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); - - // Convert - fs::writeFileBuffer("fmt_uvlx8888.png", vtf.convertAndSaveImageDataToFile()); -} +#define TEST_WRITE_FMT(Format, Flags) \ + TEST(vtfpp, write_fmt_##Format) { \ + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/fmt/" #Format ".vtf")}; \ + ASSERT_TRUE(vtf); \ + EXPECT_EQ(vtf.getWidth(), 256); \ + EXPECT_EQ(vtf.getHeight(), 256); \ + EXPECT_EQ(vtf.getFlags(), (Flags)); \ + EXPECT_EQ(vtf.getFormat(), ImageFormat::Format); \ + const auto* image = vtf.getResource(Resource::TYPE_IMAGE_DATA); \ + ASSERT_TRUE(image); \ + EXPECT_EQ(image->data.size(), ImageFormatDetails::getDataLength(vtf.getFormat(), vtf.getMipCount(), vtf.getFrameCount(), vtf.getFaceCount(), vtf.getWidth(), vtf.getHeight(), vtf.getSliceCount())); \ + fs::writeFileBuffer("fmt_" #Format ".png", vtf.convertAndSaveImageDataToFile()); \ + } + +TEST_WRITE_FMT(RGBA8888, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA) +TEST_WRITE_FMT(ABGR8888, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA) +TEST_WRITE_FMT(RGB888, VTF::FLAG_NONE) +TEST_WRITE_FMT(BGR888, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD) +TEST_WRITE_FMT(RGB565, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD) +TEST_WRITE_FMT(I8, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD) +TEST_WRITE_FMT(IA88, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA) +TEST_WRITE_FMT(A8, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA) +TEST_WRITE_FMT(RGB888_BLUESCREEN, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD) +TEST_WRITE_FMT(BGR888_BLUESCREEN, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD) +TEST_WRITE_FMT(ARGB8888, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA) +TEST_WRITE_FMT(BGRA8888, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA) +TEST_WRITE_FMT(DXT1, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD) +TEST_WRITE_FMT(DXT3, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA) +TEST_WRITE_FMT(DXT5, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA) +TEST_WRITE_FMT(BGRX8888, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD) +TEST_WRITE_FMT(BGR565, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD) +TEST_WRITE_FMT(BGRX5551, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD) +TEST_WRITE_FMT(BGRA4444, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA) +TEST_WRITE_FMT(DXT1_ONE_BIT_ALPHA, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_ONE_BIT_ALPHA) +TEST_WRITE_FMT(BGRA5551, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_ONE_BIT_ALPHA) +TEST_WRITE_FMT(UV88, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD) +TEST_WRITE_FMT(UVWQ8888, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA) +TEST_WRITE_FMT(UVLX8888, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FLAG_MULTI_BIT_ALPHA) #endif @@ -495,7 +78,7 @@ TEST(vtfpp, write_non_po2) { } TEST(vtfpp, read_v70) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v70.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v70.vtf")}; ASSERT_TRUE(vtf); // Header @@ -560,7 +143,7 @@ TEST(vtfpp, write_v70) { } TEST(vtfpp, read_v70_nomip) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v70_nomip.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v70_nomip.vtf")}; ASSERT_TRUE(vtf); // Header @@ -598,7 +181,7 @@ TEST(vtfpp, read_v70_nomip) { } TEST(vtfpp, read_v70_nothumb) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v70_nothumb.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v70_nothumb.vtf")}; ASSERT_TRUE(vtf); // Header @@ -634,7 +217,7 @@ TEST(vtfpp, read_v70_nothumb) { } TEST(vtfpp, read_v70_nothumb_nomip) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v70_nothumb_nomip.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v70_nothumb_nomip.vtf")}; ASSERT_TRUE(vtf); // Header @@ -670,7 +253,7 @@ TEST(vtfpp, read_v70_nothumb_nomip) { } TEST(vtfpp, read_v71) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v71.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v71.vtf")}; ASSERT_TRUE(vtf); // Header @@ -735,7 +318,7 @@ TEST(vtfpp, write_v71) { } TEST(vtfpp, read_v71_nomip) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v71_nomip.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v71_nomip.vtf")}; ASSERT_TRUE(vtf); // Header @@ -773,7 +356,7 @@ TEST(vtfpp, read_v71_nomip) { } TEST(vtfpp, read_v71_nothumb) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v71_nothumb.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v71_nothumb.vtf")}; ASSERT_TRUE(vtf); // Header @@ -809,7 +392,7 @@ TEST(vtfpp, read_v71_nothumb) { } TEST(vtfpp, read_v71_nothumb_nomip) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v71_nothumb_nomip.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v71_nothumb_nomip.vtf")}; ASSERT_TRUE(vtf); // Header @@ -845,7 +428,7 @@ TEST(vtfpp, read_v71_nothumb_nomip) { } TEST(vtfpp, read_v72) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v72.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v72.vtf")}; ASSERT_TRUE(vtf); // Header @@ -910,7 +493,7 @@ TEST(vtfpp, write_v72) { } TEST(vtfpp, read_v72_nomip) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v72_nomip.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v72_nomip.vtf")}; ASSERT_TRUE(vtf); // Header @@ -948,7 +531,7 @@ TEST(vtfpp, read_v72_nomip) { } TEST(vtfpp, read_v72_nothumb) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v72_nothumb.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v72_nothumb.vtf")}; ASSERT_TRUE(vtf); // Header @@ -984,7 +567,7 @@ TEST(vtfpp, read_v72_nothumb) { } TEST(vtfpp, read_v72_nothumb_nomip) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v72_nothumb_nomip.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v72_nothumb_nomip.vtf")}; ASSERT_TRUE(vtf); // Header @@ -1020,7 +603,7 @@ TEST(vtfpp, read_v72_nothumb_nomip) { } TEST(vtfpp, read_v75) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v75.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v75.vtf")}; ASSERT_TRUE(vtf); // Header @@ -1098,7 +681,7 @@ TEST(vtfpp, write_v75) { } TEST(vtfpp, read_v75_nomip) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v75_nomip.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v75_nomip.vtf")}; ASSERT_TRUE(vtf); // Header @@ -1136,7 +719,7 @@ TEST(vtfpp, read_v75_nomip) { } TEST(vtfpp, read_v75_nothumb) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v75_nothumb.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v75_nothumb.vtf")}; ASSERT_TRUE(vtf); // Header @@ -1172,7 +755,7 @@ TEST(vtfpp, read_v75_nothumb) { } TEST(vtfpp, read_v75_nothumb_nomip) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v75_nothumb_nomip.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v75_nothumb_nomip.vtf")}; ASSERT_TRUE(vtf); // Header @@ -1208,7 +791,7 @@ TEST(vtfpp, read_v75_nothumb_nomip) { } TEST(vtfpp, read_v76_c9) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v76_c9.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v76_c9.vtf")}; ASSERT_TRUE(vtf); // Header @@ -1275,7 +858,7 @@ TEST(vtfpp, write_v76_c6) { } TEST(vtfpp, read_v76_nomip_c9) { - VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/v76_nomip_c9.vtf")}; + VTF vtf{fs::readFileBuffer(ASSET_ROOT "vtfpp/ver/v76_nomip_c9.vtf")}; ASSERT_TRUE(vtf); // Header From bc3af5cbdbd95c31b195b901ed45c9759c2bedc5 Mon Sep 17 00:00:00 2001 From: craftablescience Date: Fri, 30 Aug 2024 22:47:03 -0400 Subject: [PATCH 3/5] feat(vtfpp): accelerate conversion functions --- cmake/AddPrettyParser.cmake | 5 + include/vtfpp/ImageConversion.h | 193 +++- include/vtfpp/ImageFormats.h | 64 +- include/vtfpp/VTF.h | 398 +++++++ include/vtfpp/vtfpp.h | 398 +------ src/vtfpp/ImageConversion.cpp | 1001 +++++++++-------- src/vtfpp/{vtfpp.cpp => VTF.cpp} | 8 +- src/vtfpp/_vtfpp.cmake | 4 +- test/bench/vtfpp.cpp | 1 - .../{src_non_po2.png => src/non_po2.png} | Bin test/res/vtfpp/{src.png => src/po2.png} | Bin test/vtfpp.cpp | 12 +- 12 files changed, 1219 insertions(+), 865 deletions(-) create mode 100644 include/vtfpp/VTF.h rename src/vtfpp/{vtfpp.cpp => VTF.cpp} (99%) rename test/res/vtfpp/{src_non_po2.png => src/non_po2.png} (100%) rename test/res/vtfpp/{src.png => src/po2.png} (100%) diff --git a/cmake/AddPrettyParser.cmake b/cmake/AddPrettyParser.cmake index d8a081f6e..1f6ab7cfe 100644 --- a/cmake/AddPrettyParser.cmake +++ b/cmake/AddPrettyParser.cmake @@ -23,6 +23,11 @@ function(add_pretty_parser TARGET) # Define DEBUG macro target_compile_definitions(${TARGET} PRIVATE "$<$:DEBUG>") + # MSVC on its bullshit again + if(MSVC) + target_compile_options(${TARGET} PRIVATE "/Zc:preprocessor") + endif() + # Set optimization flags if(CMAKE_BUILD_TYPE MATCHES "Debug") # Build with debug friendly optimizations and debug symbols (MSVC defaults are fine) diff --git a/include/vtfpp/ImageConversion.h b/include/vtfpp/ImageConversion.h index b0fec05da..74b42e194 100644 --- a/include/vtfpp/ImageConversion.h +++ b/include/vtfpp/ImageConversion.h @@ -4,9 +4,196 @@ #include #include +#include + #include "ImageFormats.h" -namespace vtfpp::ImageConversion { +namespace vtfpp { + +namespace ImagePixel { + +struct RGBA8888 { + static constexpr auto FORMAT = ImageFormat::RGBA8888; + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +}; + +struct ABGR8888 { + static constexpr auto FORMAT = ImageFormat::ABGR8888; + uint8_t a; + uint8_t b; + uint8_t g; + uint8_t r; +}; + +struct RGB888 { + static constexpr auto FORMAT = ImageFormat::RGB888; + uint8_t r; + uint8_t g; + uint8_t b; +}; + +struct RGB888_BLUESCREEN : public RGB888 { + static constexpr auto FORMAT = ImageFormat::RGB888_BLUESCREEN; +}; + +struct BGR888 { + static constexpr auto FORMAT = ImageFormat::BGR888; + uint8_t b; + uint8_t g; + uint8_t r; +}; + +struct BGR888_BLUESCREEN : public BGR888 { + static constexpr auto FORMAT = ImageFormat::BGR888_BLUESCREEN; +}; + +struct RGB565 { + static constexpr auto FORMAT = ImageFormat::RGB565; + uint16_t r : 5; + uint16_t g : 6; + uint16_t b : 5; +}; + +struct I8 { + static constexpr auto FORMAT = ImageFormat::I8; + uint8_t i; +}; + +struct IA88 { + static constexpr auto FORMAT = ImageFormat::IA88; + uint8_t i; + uint8_t a; +}; + +struct P8 { + static constexpr auto FORMAT = ImageFormat::P8; + uint8_t p; +}; + +struct A8 { + static constexpr auto FORMAT = ImageFormat::A8; + uint8_t a; +}; + +struct ARGB8888 { + static constexpr auto FORMAT = ImageFormat::ARGB8888; + uint8_t a; + uint8_t r; + uint8_t g; + uint8_t b; +}; + +struct BGRA8888 { + static constexpr auto FORMAT = ImageFormat::BGRA8888; + uint8_t b; + uint8_t g; + uint8_t r; + uint8_t a; +}; + +struct BGRX8888 { + static constexpr auto FORMAT = ImageFormat::BGRX8888; + uint8_t b; + uint8_t g; + uint8_t r; + uint8_t x; +}; + +struct BGR565 { + static constexpr auto FORMAT = ImageFormat::BGR565; + uint16_t b : 5; + uint16_t g : 6; + uint16_t r : 5; +}; + +struct BGRX5551 { + static constexpr auto FORMAT = ImageFormat::BGRX5551; + uint16_t b : 5; + uint16_t g : 5; + uint16_t r : 5; + uint16_t x : 1; +}; + +struct BGRA4444 { + static constexpr auto FORMAT = ImageFormat::BGRA4444; + uint16_t b : 4; + uint16_t g : 4; + uint16_t r : 4; + uint16_t a : 4; +}; + +struct BGRA5551 { + static constexpr auto FORMAT = ImageFormat::BGRA5551; + uint16_t b : 5; + uint16_t g : 5; + uint16_t r : 5; + uint16_t a : 1; +}; + +struct UV88 { + static constexpr auto FORMAT = ImageFormat::UV88; + uint8_t u; + uint8_t v; +}; + +struct UVWQ8888 { + static constexpr auto FORMAT = ImageFormat::UVWQ8888; + uint8_t u; + uint8_t v; + uint8_t w; + uint8_t q; +}; + +struct RGBA16161616F { + static constexpr auto FORMAT = ImageFormat::RGBA16161616F; + sourcepp::math::FloatCompressed16 r; + sourcepp::math::FloatCompressed16 g; + sourcepp::math::FloatCompressed16 b; + sourcepp::math::FloatCompressed16 a; +}; + +struct RGBA16161616 { + static constexpr auto FORMAT = ImageFormat::RGBA16161616; + uint16_t r; + uint16_t g; + uint16_t b; + uint16_t a; +}; + +struct UVLX8888 { + static constexpr auto FORMAT = ImageFormat::UVLX8888; + uint8_t u; + uint8_t v; + uint8_t l; + uint8_t x; +}; + +struct R32F { + static constexpr auto FORMAT = ImageFormat::R32F; + float r; +}; + +struct RGB323232F { + static constexpr auto FORMAT = ImageFormat::R32F; + float r; + float g; + float b; +}; + +struct RGBA32323232F { + static constexpr auto FORMAT = ImageFormat::RGBA32323232F; + float r; + float g; + float b; + float a; +}; + +} // namespace ImagePixel + +namespace ImageConversion { /// Converts an image from one format to another. [[nodiscard]] std::vector convertImageDataToFormat(std::span imageData, ImageFormat oldFormat, ImageFormat newFormat, uint16_t width, uint16_t height); @@ -56,4 +243,6 @@ void setResizedDims(uint16_t& width, ResizeMethod widthResize, uint16_t& height, /// Resize given image data to the new dimensions, where the new width and height are governed by the resize methods [[nodiscard]] std::vector resizeImageDataStrict(std::span imageData, ImageFormat format, uint16_t width, uint16_t newWidth, uint16_t& widthOut, ResizeMethod widthResize, uint16_t height, uint16_t newHeight, uint16_t& heightOut, ResizeMethod heightResize, bool srgb, ResizeFilter filter, ResizeEdge edge = ResizeEdge::CLAMP); -} // namespace vtfpp::ImageConversion +} // namespace ImageConversion + +} // namespace vtfpp diff --git a/include/vtfpp/ImageFormats.h b/include/vtfpp/ImageFormats.h index bcc11ec0c..a37b2b2ca 100644 --- a/include/vtfpp/ImageFormats.h +++ b/include/vtfpp/ImageFormats.h @@ -386,6 +386,10 @@ namespace ImageFormatDetails { return red(format) > 8 || bpp(format) > 32; } +[[nodiscard]] constexpr bool decimal(ImageFormat format) { + return large(format) && format != ImageFormat::RGBA16161616; +} + [[nodiscard]] constexpr bool transparent(ImageFormat format) { const auto a = alpha(format); if (a < 0) { @@ -403,15 +407,71 @@ namespace ImageFormatDetails { break; } return false; - } else { - return a != 0; } + switch (format) { + using enum ImageFormat; + case RGB888_BLUESCREEN: + case BGR888_BLUESCREEN: + return true; + case BGRX8888: + case BGRX5551: + return false; + default: + break; + } + return a != 0; } [[nodiscard]] constexpr bool opaque(ImageFormat format) { return !transparent(format); } +[[nodiscard]] constexpr ImageFormat containerFormat(ImageFormat format) { + switch (format) { + using enum ImageFormat; + case R32F: + case RGB323232F: + case RGBA16161616F: + case RGBA32323232F: + case BC6H: + return RGBA32323232F; + case RGBA16161616: + return RGBA16161616; + case RGBA8888: + case ABGR8888: + case RGB888: + case BGR888: + case RGB888_BLUESCREEN: + case BGR888_BLUESCREEN: + case ARGB8888: + case BGRA8888: + case BGRX8888: + case UVWQ8888: + case UVLX8888: + case RGB565: + case BGR565: + case BGRX5551: + case BGRA5551: + case BGRA4444: + case I8: + case IA88: + case P8: + case UV88: + case A8: + case DXT1: + case DXT3: + case DXT5: + case DXT1_ONE_BIT_ALPHA: + case ATI2N: + case ATI1N: + case BC7: + return RGBA8888; + case EMPTY: + break; + } + return ImageFormat::EMPTY; +} + } // namespace ImageFormatDetails namespace ImageDimensions { diff --git a/include/vtfpp/VTF.h b/include/vtfpp/VTF.h new file mode 100644 index 000000000..8563e702e --- /dev/null +++ b/include/vtfpp/VTF.h @@ -0,0 +1,398 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ImageConversion.h" + +namespace vtfpp { + +constexpr uint32_t VTF_SIGNATURE = sourcepp::parser::binary::makeFourCC("VTF\0"); + +struct Resource { + enum Type : uint32_t { + TYPE_UNKNOWN = 0, // Unknown + TYPE_THUMBNAIL_DATA = sourcepp::parser::binary::makeFourCC("\x01\0\0\0"), + TYPE_IMAGE_DATA = sourcepp::parser::binary::makeFourCC("\x30\0\0\0"), + TYPE_PARTICLE_SHEET_DATA = sourcepp::parser::binary::makeFourCC("\x10\0\0\0"), + TYPE_CRC = sourcepp::parser::binary::makeFourCC("CRC\0"), + TYPE_LOD_CONTROL_INFO = sourcepp::parser::binary::makeFourCC("LOD\0"), + TYPE_EXTENDED_FLAGS = sourcepp::parser::binary::makeFourCC("TSO\0"), + TYPE_KEYVALUES_DATA = sourcepp::parser::binary::makeFourCC("KVD\0"), + TYPE_AUX_COMPRESSION = sourcepp::parser::binary::makeFourCC("AXC\0"), + }; + static constexpr std::array TYPE_ARRAY_ORDER{ + // These don't really matter + Resource::TYPE_CRC, Resource::TYPE_EXTENDED_FLAGS, Resource::TYPE_LOD_CONTROL_INFO, Resource::TYPE_KEYVALUES_DATA, Resource::TYPE_PARTICLE_SHEET_DATA, + // These matter + Resource::TYPE_THUMBNAIL_DATA, Resource::TYPE_AUX_COMPRESSION, Resource::TYPE_IMAGE_DATA, + }; + + enum Flags : uint8_t { + FLAG_NONE = 0, + FLAG_LOCAL_DATA = 1 << 1, + }; + + Type type = TYPE_UNKNOWN; + Flags flags = FLAG_NONE; + std::span data; + + using ConvertedData = std::variant< + std::monostate, // Anything that would be equivalent to just returning data directly, or used as an error + uint32_t, // CRC, TSO + std::pair, // LOD + std::string, // KVD + std::span // AXC + >; + [[nodiscard]] ConvertedData convertData() const; + + [[nodiscard]] uint32_t getDataAsCRC() const { + return std::get(this->convertData()); + } + + [[nodiscard]] uint32_t getDataAsExtendedFlags() const { + return std::get(this->convertData()); + } + + [[nodiscard]] std::pair getDataAsLODControlInfo() const { + return std::get>(this->convertData()); + } + + [[nodiscard]] std::string getDataAsKeyValuesData() const { + return std::get(this->convertData()); + } + + [[nodiscard]] int32_t getDataAsAuxCompressionLevel() const { + return static_cast(std::get>(this->convertData())[1]); + } + + [[nodiscard]] uint32_t getDataAsAuxCompressionLength(uint8_t mip, uint8_t mipCount, uint16_t frame, uint16_t frameCount, uint16_t face, uint16_t faceCount) const { + return std::get>(this->convertData())[((mipCount - 1 - mip) * frameCount * faceCount + frame * faceCount + face) + 2]; + } +}; +SOURCEPP_BITFLAGS_ENUM(Resource::Flags) + +/* + * === EASY DIFFICULTY WRITER API === + * + * Use a static helper function to create a VTF in one function call - VTF::create + * + * === MEDIUM DIFFICULTY WRITER API === + * + * Constructing a VTF instance from existing VTF data will let you modify that data. + * + * This class has methods that should be called in a particular order + * when creating a VTF from scratch, or your output VTF may look incorrect: + * + * 0. Construct an empty VTF instance + * 1. Set the version (7.4 is the default) - VTF::setVersion + * 2. Set FLAG_SRGB (optional, read for image resizing) - VTF::setFlags + * 3. Set the image resize methods (optional) - VTF::setImageResizeMethods + * 4. Set the base image (mip 0, frame 0, face 0, slice 0) - VTF::setImage + * 5. Set the output format (optional) - VTF::setFormat + * 6. Compute mips (optional) - VTF::computeMips + * + * After these methods are called, the other writer methods in the class should work as expected. + */ +class VTF { +public: + enum Flags : int32_t { + FLAG_NONE = 0, + FLAG_POINT_SAMPLE = 1 << 0, + FLAG_TRILINEAR = 1 << 1, + FLAG_CLAMP_S = 1 << 2, + FLAG_CLAMP_T = 1 << 3, + FLAG_ANISOTROPIC = 1 << 4, + FLAG_HINT_DXT5 = 1 << 5, + FLAG_SRGB = 1 << 6, + FLAG_NO_COMPRESS = FLAG_SRGB, // Internal to vtex, removed + FLAG_NORMAL = 1 << 7, + FLAG_NO_MIP = 1 << 8, // Added at VTF creation time + FLAG_NO_LOD = 1 << 9, // Added at VTF creation time + FLAG_MIN_MIP = 1 << 10, + FLAG_PROCEDURAL = 1 << 11, + FLAG_ONE_BIT_ALPHA = 1 << 12, // Added at VTF creation time + FLAG_MULTI_BIT_ALPHA = 1 << 13, // Added at VTF creation time + FLAG_ENVMAP = 1 << 14, // Added at VTF creation time + FLAG_RENDERTARGET = 1 << 15, + FLAG_DEPTH_RENDERTARGET = 1 << 16, + FLAG_NO_DEBUG_OVERRIDE = 1 << 17, + FLAG_SINGLE_COPY = 1 << 18, + FLAG_ONE_OVER_MIP_LEVEL_IN_ALPHA = 1 << 19, // Internal to vtex, removed + FLAG_PREMULTIPLY_COLOR_BY_ONE_OVER_MIP_LEVEL = 1 << 20, // Internal to vtex, removed + FLAG_NORMAL_TO_DUDV = 1 << 21, // Internal to vtex, removed + FLAG_ALPHA_TEST_MIP_GENERATION = 1 << 22, + FLAG_NO_DEPTH_BUFFER = 1 << 23, + FLAG_NICE_FILTERED = 1 << 24, // Internal to vtex, removed + FLAG_CLAMP_U = 1 << 25, + FLAG_VERTEX_TEXTURE = 1 << 26, + FLAG_SSBUMP = 1 << 27, + FLAG_UNFILTERABLE_OK = 1 << 28, // Removed + FLAG_BORDER = 1 << 29, + FLAG_SPECVAR_RED = 1 << 30, // Removed + FLAG_SPECVAR_ALPHA = 1 << 31, // Removed + }; + static constexpr std::underlying_type_t FLAG_MASK_GENERATED = FLAG_NO_MIP | FLAG_NO_LOD | FLAG_ONE_BIT_ALPHA | FLAG_MULTI_BIT_ALPHA | FLAG_ENVMAP; + + struct CreationOptions { + uint32_t majorVersion = 7; + uint32_t minorVersion = 4; + ImageFormat outputFormat = FORMAT_DEFAULT; + ImageConversion::ResizeMethod widthResizeMethod = ImageConversion::ResizeMethod::POWER_OF_TWO_BIGGER; + ImageConversion::ResizeMethod heightResizeMethod = ImageConversion::ResizeMethod::POWER_OF_TWO_BIGGER; + ImageConversion::ResizeFilter filter = ImageConversion::ResizeFilter::BILINEAR; + uint16_t initialFrameCount = 1; + uint16_t initialSliceCount = 1; + bool isCubeMap = false; + bool createMips = true; + bool createThumbnail = true; + bool createReflectivity = true; + Flags flags = FLAG_NONE; + float bumpMapScale = 1.f; + uint16_t startFrame = 0; + uint8_t compressionLevel = 6; + }; + + /// This value is only valid when passed to VTF::create through CreationOptions or VTF::setFormat + static constexpr ImageFormat FORMAT_DEFAULT = static_cast(-1); + + static constexpr int32_t MAX_RESOURCES = 32; + + static constexpr uint16_t SPHEREMAP_START_FRAME = 0xffff; + + VTF(); + + explicit VTF(std::vector&& vtfData, bool parseHeaderOnly = false); + + explicit VTF(std::span vtfData, bool parseHeaderOnly = false); + + explicit VTF(const std::string& vtfPath, bool parseHeaderOnly = false); + + VTF(const VTF&) = delete; + VTF& operator=(const VTF&) = delete; + + VTF(VTF&&) noexcept = default; + VTF& operator=(VTF&&) noexcept = default; + + [[nodiscard]] explicit operator bool() const; + + static void create(std::span imageData, ImageFormat format, uint16_t width, uint16_t height, const std::string& vtfPath, CreationOptions options); + + [[nodiscard]] static VTF create(std::span imageData, ImageFormat format, uint16_t width, uint16_t height, CreationOptions options); + + static void create(const std::string& imagePath, const std::string& vtfPath, CreationOptions options); + + [[nodiscard]] static VTF create(const std::string& imagePath, CreationOptions options); + + [[nodiscard]] uint32_t getMajorVersion() const; + + [[nodiscard]] uint32_t getMinorVersion() const; + + void setVersion(uint32_t newMajorVersion, uint32_t newMinorVersion); + + void setMajorVersion(uint32_t newMajorVersion); + + void setMinorVersion(uint32_t newMinorVersion); + + [[nodiscard]] ImageConversion::ResizeMethod getImageWidthResizeMethod() const; + + [[nodiscard]] ImageConversion::ResizeMethod getImageHeightResizeMethod() const; + + void setImageResizeMethods(ImageConversion::ResizeMethod imageWidthResizeMethod_, ImageConversion::ResizeMethod imageHeightResizeMethod_); + + [[nodiscard]] uint16_t getWidth(uint8_t mip = 0) const; + + [[nodiscard]] uint16_t getHeight(uint8_t mip = 0) const; + + void setSize(uint16_t newWidth, uint16_t newHeight, ImageConversion::ResizeFilter filter); + + [[nodiscard]] Flags getFlags() const; + + void setFlags(Flags flags_); + + void addFlags(Flags flags_); + + void removeFlags(Flags flags_); + + [[nodiscard]] ImageFormat getFormat() const; + + void setFormat(ImageFormat newFormat, ImageConversion::ResizeFilter filter = ImageConversion::ResizeFilter::BILINEAR); + + [[nodiscard]] uint8_t getMipCount() const; + + bool setMipCount(uint8_t newMipCount); + + bool setRecommendedMipCount(); + + void computeMips(ImageConversion::ResizeFilter filter = ImageConversion::ResizeFilter::BILINEAR); + + [[nodiscard]] uint16_t getFrameCount() const; + + bool setFrameCount(uint16_t newFrameCount); + + [[nodiscard]] uint8_t getFaceCount() const; + + bool setFaceCount(bool hasMultipleFaces, bool hasSphereMap = false); + + //bool computeSphereMap(); + + [[nodiscard]] uint16_t getSliceCount() const; + + bool setSliceCount(uint16_t newSliceCount); + + [[nodiscard]] uint16_t getStartFrame() const; + + bool setStartFrame(uint16_t newStartFrame); + + [[nodiscard]] sourcepp::math::Vec3f getReflectivity() const; + + void setReflectivity(sourcepp::math::Vec3f newReflectivity); + + void computeReflectivity(); + + [[nodiscard]] float getBumpMapScale() const; + + void setBumpMapScale(float newBumpMapScale); + + [[nodiscard]] ImageFormat getThumbnailFormat() const; + + [[nodiscard]] uint8_t getThumbnailWidth() const; + + [[nodiscard]] uint8_t getThumbnailHeight() const; + + [[nodiscard]] const std::vector& getResources() const; + + [[nodiscard]] const Resource* getResource(Resource::Type type) const; + + void setParticleSheetResource(std::span value); + + void removeParticleSheetResource(); + + void setCRCResource(uint32_t value); + + void removeCRCResource(); + + void setLODResource(uint8_t u, uint8_t v); + + void removeLODResource(); + + void setExtendedFlagsResource(uint32_t value); + + void removeExtendedFlagsResource(); + + void setKeyValuesData(const std::string& value); + + void removeKeyValuesData(); + + [[nodiscard]] uint8_t getCompressionLevel() const; + + void setCompressionLevel(uint8_t newCompressionLevel); + + [[nodiscard]] bool hasImageData() const; + + [[nodiscard]] bool imageDataIsSRGB() const; + + [[nodiscard]] std::span getImageDataRaw(uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0, uint16_t slice = 0) const; + + [[nodiscard]] std::vector getImageDataAs(ImageFormat newFormat, uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0, uint16_t slice = 0) const; + + [[nodiscard]] std::vector getImageDataAsRGBA8888(uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0, uint16_t slice = 0) const; + + bool setImage(std::span imageData_, ImageFormat format_, uint16_t width_, uint16_t height_, ImageConversion::ResizeFilter filter = ImageConversion::ResizeFilter::BILINEAR, uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0, uint16_t slice = 0); + + bool setImage(const std::string& imagePath, ImageConversion::ResizeFilter filter = ImageConversion::ResizeFilter::BILINEAR, uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0, uint16_t slice = 0); + + [[nodiscard]] std::vector convertAndSaveImageDataToFile(uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0, uint16_t slice = 0) const; + + void convertAndSaveImageDataToFile(const std::string& imagePath, uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0, uint16_t slice = 0) const; + + [[nodiscard]] bool hasThumbnailData() const; + + [[nodiscard]] std::span getThumbnailDataRaw() const; + + [[nodiscard]] std::vector getThumbnailDataAs(ImageFormat newFormat) const; + + [[nodiscard]] std::vector getThumbnailDataAsRGBA8888() const; + + void computeThumbnail(ImageConversion::ResizeFilter filter = ImageConversion::ResizeFilter::BILINEAR); + + void removeThumbnail(); + + [[nodiscard]] std::vector convertAndSaveThumbnailDataToFile() const; + + void convertAndSaveThumbnailDataToFile(const std::string& imagePath) const; + + [[nodiscard]] std::vector bake(); + + bool bake(const std::string& vtfPath); + +protected: + [[nodiscard]] ImageFormat getDefaultFormat() const; + + [[nodiscard]] static uint8_t getFaceCountFor(uint16_t width, uint16_t height, uint32_t majorVersion, uint32_t minorVersion, Flags flags, uint16_t startFrame); + + [[nodiscard]] Resource* getResourceInternal(Resource::Type type); + + void setResourceInternal(Resource::Type type, std::span data_); + + void removeResourceInternal(Resource::Type type); + + void regenerateImageData(ImageFormat newFormat, uint16_t newWidth, uint16_t newHeight, uint8_t newMipCount, uint16_t newFrameCount, uint8_t newFaceCount, uint16_t newSliceCount, ImageConversion::ResizeFilter filter = ImageConversion::ResizeFilter::BILINEAR); + + bool opened = false; + + std::vector data; + + //uint32_t signature; + uint32_t majorVersion{}; + uint32_t minorVersion{}; + + uint32_t headerSize{}; + + uint16_t width{}; + uint16_t height{}; + Flags flags{}; + + uint16_t frameCount = 1; + uint16_t startFrame{}; + + //uint8_t _padding0[4]; + sourcepp::math::Vec3f reflectivity{}; + //uint8_t _padding1[4]; + + float bumpMapScale{}; + ImageFormat format{}; + uint8_t mipCount = 1; + + ImageFormat thumbnailFormat{}; + uint8_t thumbnailWidth{}; + uint8_t thumbnailHeight{}; + + // 1 for v7.1 and lower + uint16_t sliceCount = 1; + //uint8_t _padding2[3]; + + // Technically added in v7.3, but we use it to store image and thumbnail data in v7.2 and lower anyway + //uint32_t resourceCount; + std::vector resources; + //uint8_t _padding3[4]; + + // These aren't in the header, these are for VTF modification + uint8_t compressionLevel = 0; + ImageConversion::ResizeMethod imageWidthResizeMethod = ImageConversion::ResizeMethod::POWER_OF_TWO_BIGGER; + ImageConversion::ResizeMethod imageHeightResizeMethod = ImageConversion::ResizeMethod::POWER_OF_TWO_BIGGER; +}; +SOURCEPP_BITFLAGS_ENUM(VTF::Flags) + +} // namespace vtfpp diff --git a/include/vtfpp/vtfpp.h b/include/vtfpp/vtfpp.h index a65fcdfc0..772520651 100644 --- a/include/vtfpp/vtfpp.h +++ b/include/vtfpp/vtfpp.h @@ -1,398 +1,10 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "ImageConversion.h" - -namespace vtfpp { - -constexpr uint32_t VTF_SIGNATURE = sourcepp::parser::binary::makeFourCC("VTF\0"); - -struct Resource { - enum Type : uint32_t { - TYPE_UNKNOWN = 0, // Unknown - TYPE_THUMBNAIL_DATA = sourcepp::parser::binary::makeFourCC("\x01\0\0\0"), - TYPE_IMAGE_DATA = sourcepp::parser::binary::makeFourCC("\x30\0\0\0"), - TYPE_PARTICLE_SHEET_DATA = sourcepp::parser::binary::makeFourCC("\x10\0\0\0"), - TYPE_CRC = sourcepp::parser::binary::makeFourCC("CRC\0"), - TYPE_LOD_CONTROL_INFO = sourcepp::parser::binary::makeFourCC("LOD\0"), - TYPE_EXTENDED_FLAGS = sourcepp::parser::binary::makeFourCC("TSO\0"), - TYPE_KEYVALUES_DATA = sourcepp::parser::binary::makeFourCC("KVD\0"), - TYPE_AUX_COMPRESSION = sourcepp::parser::binary::makeFourCC("AXC\0"), - }; - static constexpr std::array TYPE_ARRAY_ORDER{ - // These don't really matter - Resource::TYPE_CRC, Resource::TYPE_EXTENDED_FLAGS, Resource::TYPE_LOD_CONTROL_INFO, Resource::TYPE_KEYVALUES_DATA, Resource::TYPE_PARTICLE_SHEET_DATA, - // These matter - Resource::TYPE_THUMBNAIL_DATA, Resource::TYPE_AUX_COMPRESSION, Resource::TYPE_IMAGE_DATA, - }; - - enum Flags : uint8_t { - FLAG_NONE = 0, - FLAG_LOCAL_DATA = 1 << 1, - }; - - Type type = TYPE_UNKNOWN; - Flags flags = FLAG_NONE; - std::span data; - - using ConvertedData = std::variant< - std::monostate, // Anything that would be equivalent to just returning data directly, or used as an error - uint32_t, // CRC, TSO - std::pair, // LOD - std::string, // KVD - std::span // AXC - >; - [[nodiscard]] ConvertedData convertData() const; - - [[nodiscard]] uint32_t getDataAsCRC() const { - return std::get(this->convertData()); - } - - [[nodiscard]] uint32_t getDataAsExtendedFlags() const { - return std::get(this->convertData()); - } - - [[nodiscard]] std::pair getDataAsLODControlInfo() const { - return std::get>(this->convertData()); - } - - [[nodiscard]] std::string getDataAsKeyValuesData() const { - return std::get(this->convertData()); - } - - [[nodiscard]] int32_t getDataAsAuxCompressionLevel() const { - return static_cast(std::get>(this->convertData())[1]); - } - - [[nodiscard]] uint32_t getDataAsAuxCompressionLength(uint8_t mip, uint8_t mipCount, uint16_t frame, uint16_t frameCount, uint16_t face, uint16_t faceCount) const { - return std::get>(this->convertData())[((mipCount - 1 - mip) * frameCount * faceCount + frame * faceCount + face) + 2]; - } -}; -SOURCEPP_BITFLAGS_ENUM(Resource::Flags) - /* - * === EASY DIFFICULTY WRITER API === - * - * Use a static helper function to create a VTF in one function call - VTF::create - * - * === MEDIUM DIFFICULTY WRITER API === - * - * Constructing a VTF instance from existing VTF data will let you modify that data. - * - * This class has methods that should be called in a particular order - * when creating a VTF from scratch, or your output VTF may look incorrect: - * - * 0. Construct an empty VTF instance - * 1. Set the version (7.4 is the default) - VTF::setVersion - * 2. Set FLAG_SRGB (optional, read for image resizing) - VTF::setFlags - * 3. Set the image resize methods (optional) - VTF::setImageResizeMethods - * 4. Set the base image (mip 0, frame 0, face 0, slice 0) - VTF::setImage - * 5. Set the output format (optional) - VTF::setFormat - * 6. Compute mips (optional) - VTF::computeMips - * - * After these methods are called, the other writer methods in the class should work as expected. + * This header is just included so consumers of this library can + * include it the same way as any of the other SourcePP libraries. */ -class VTF { -public: - enum Flags : int32_t { - FLAG_NONE = 0, - FLAG_POINT_SAMPLE = 1 << 0, - FLAG_TRILINEAR = 1 << 1, - FLAG_CLAMP_S = 1 << 2, - FLAG_CLAMP_T = 1 << 3, - FLAG_ANISOTROPIC = 1 << 4, - FLAG_HINT_DXT5 = 1 << 5, - FLAG_SRGB = 1 << 6, - FLAG_NO_COMPRESS = FLAG_SRGB, // Internal to vtex, removed - FLAG_NORMAL = 1 << 7, - FLAG_NO_MIP = 1 << 8, // Added at VTF creation time - FLAG_NO_LOD = 1 << 9, // Added at VTF creation time - FLAG_MIN_MIP = 1 << 10, - FLAG_PROCEDURAL = 1 << 11, - FLAG_ONE_BIT_ALPHA = 1 << 12, // Added at VTF creation time - FLAG_MULTI_BIT_ALPHA = 1 << 13, // Added at VTF creation time - FLAG_ENVMAP = 1 << 14, // Added at VTF creation time - FLAG_RENDERTARGET = 1 << 15, - FLAG_DEPTH_RENDERTARGET = 1 << 16, - FLAG_NO_DEBUG_OVERRIDE = 1 << 17, - FLAG_SINGLE_COPY = 1 << 18, - FLAG_ONE_OVER_MIP_LEVEL_IN_ALPHA = 1 << 19, // Internal to vtex, removed - FLAG_PREMULTIPLY_COLOR_BY_ONE_OVER_MIP_LEVEL = 1 << 20, // Internal to vtex, removed - FLAG_NORMAL_TO_DUDV = 1 << 21, // Internal to vtex, removed - FLAG_ALPHA_TEST_MIP_GENERATION = 1 << 22, - FLAG_NO_DEPTH_BUFFER = 1 << 23, - FLAG_NICE_FILTERED = 1 << 24, // Internal to vtex, removed - FLAG_CLAMP_U = 1 << 25, - FLAG_VERTEX_TEXTURE = 1 << 26, - FLAG_SSBUMP = 1 << 27, - FLAG_UNFILTERABLE_OK = 1 << 28, // Removed - FLAG_BORDER = 1 << 29, - FLAG_SPECVAR_RED = 1 << 30, // Removed - FLAG_SPECVAR_ALPHA = 1 << 31, // Removed - }; - static constexpr std::underlying_type_t FLAG_MASK_GENERATED = FLAG_NO_MIP | FLAG_NO_LOD | FLAG_ONE_BIT_ALPHA | FLAG_MULTI_BIT_ALPHA | FLAG_ENVMAP; - - struct CreationOptions { - uint32_t majorVersion = 7; - uint32_t minorVersion = 4; - ImageFormat outputFormat = FORMAT_DEFAULT; - ImageConversion::ResizeMethod widthResizeMethod = ImageConversion::ResizeMethod::POWER_OF_TWO_BIGGER; - ImageConversion::ResizeMethod heightResizeMethod = ImageConversion::ResizeMethod::POWER_OF_TWO_BIGGER; - ImageConversion::ResizeFilter filter = ImageConversion::ResizeFilter::BILINEAR; - uint16_t initialFrameCount = 1; - uint16_t initialSliceCount = 1; - bool isCubeMap = false; - bool createMips = true; - bool createThumbnail = true; - bool createReflectivity = true; - Flags flags = FLAG_NONE; - float bumpMapScale = 1.f; - uint16_t startFrame = 0; - uint8_t compressionLevel = 6; - }; - - /// This value is only valid when passed to VTF::create through CreationOptions or VTF::setFormat - static constexpr ImageFormat FORMAT_DEFAULT = static_cast(-1); - - static constexpr int32_t MAX_RESOURCES = 32; - - static constexpr uint16_t SPHEREMAP_START_FRAME = 0xffff; - - VTF(); - - explicit VTF(std::vector&& vtfData, bool parseHeaderOnly = false); - - explicit VTF(std::span vtfData, bool parseHeaderOnly = false); - - explicit VTF(const std::string& vtfPath, bool parseHeaderOnly = false); - - VTF(const VTF&) = delete; - VTF& operator=(const VTF&) = delete; - - VTF(VTF&&) noexcept = default; - VTF& operator=(VTF&&) noexcept = default; - - [[nodiscard]] explicit operator bool() const; - - static void create(std::span imageData, ImageFormat format, uint16_t width, uint16_t height, const std::string& vtfPath, CreationOptions options); - - [[nodiscard]] static VTF create(std::span imageData, ImageFormat format, uint16_t width, uint16_t height, CreationOptions options); - - static void create(const std::string& imagePath, const std::string& vtfPath, CreationOptions options); - - [[nodiscard]] static VTF create(const std::string& imagePath, CreationOptions options); - - [[nodiscard]] uint32_t getMajorVersion() const; - - [[nodiscard]] uint32_t getMinorVersion() const; - - void setVersion(uint32_t newMajorVersion, uint32_t newMinorVersion); - - void setMajorVersion(uint32_t newMajorVersion); - - void setMinorVersion(uint32_t newMinorVersion); - - [[nodiscard]] ImageConversion::ResizeMethod getImageWidthResizeMethod() const; - - [[nodiscard]] ImageConversion::ResizeMethod getImageHeightResizeMethod() const; - - void setImageResizeMethods(ImageConversion::ResizeMethod imageWidthResizeMethod_, ImageConversion::ResizeMethod imageHeightResizeMethod_); - - [[nodiscard]] uint16_t getWidth(uint8_t mip = 0) const; - - [[nodiscard]] uint16_t getHeight(uint8_t mip = 0) const; - - void setSize(uint16_t newWidth, uint16_t newHeight, ImageConversion::ResizeFilter filter); - - [[nodiscard]] Flags getFlags() const; - - void setFlags(Flags flags_); - - void addFlags(Flags flags_); - - void removeFlags(Flags flags_); - - [[nodiscard]] ImageFormat getFormat() const; - - void setFormat(ImageFormat newFormat, ImageConversion::ResizeFilter filter = ImageConversion::ResizeFilter::BILINEAR); - - [[nodiscard]] uint8_t getMipCount() const; - - bool setMipCount(uint8_t newMipCount); - - bool setRecommendedMipCount(); - - void computeMips(ImageConversion::ResizeFilter filter = ImageConversion::ResizeFilter::BILINEAR); - - [[nodiscard]] uint16_t getFrameCount() const; - - bool setFrameCount(uint16_t newFrameCount); - - [[nodiscard]] uint8_t getFaceCount() const; - - bool setFaceCount(bool hasMultipleFaces, bool hasSphereMap = false); - - //bool computeSphereMap(); - - [[nodiscard]] uint16_t getSliceCount() const; - - bool setSliceCount(uint16_t newSliceCount); - - [[nodiscard]] uint16_t getStartFrame() const; - - bool setStartFrame(uint16_t newStartFrame); - [[nodiscard]] sourcepp::math::Vec3f getReflectivity() const; - - void setReflectivity(sourcepp::math::Vec3f newReflectivity); - - void computeReflectivity(); - - [[nodiscard]] float getBumpMapScale() const; - - void setBumpMapScale(float newBumpMapScale); - - [[nodiscard]] ImageFormat getThumbnailFormat() const; - - [[nodiscard]] uint8_t getThumbnailWidth() const; - - [[nodiscard]] uint8_t getThumbnailHeight() const; - - [[nodiscard]] const std::vector& getResources() const; - - [[nodiscard]] const Resource* getResource(Resource::Type type) const; - - void setParticleSheetResource(std::span value); - - void removeParticleSheetResource(); - - void setCRCResource(uint32_t value); - - void removeCRCResource(); - - void setLODResource(uint8_t u, uint8_t v); - - void removeLODResource(); - - void setExtendedFlagsResource(uint32_t value); - - void removeExtendedFlagsResource(); - - void setKeyValuesData(const std::string& value); - - void removeKeyValuesData(); - - [[nodiscard]] uint8_t getCompressionLevel() const; - - void setCompressionLevel(uint8_t newCompressionLevel); - - [[nodiscard]] bool hasImageData() const; - - [[nodiscard]] bool imageDataIsSRGB() const; - - [[nodiscard]] std::span getImageDataRaw(uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0, uint16_t slice = 0) const; - - [[nodiscard]] std::vector getImageDataAs(ImageFormat newFormat, uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0, uint16_t slice = 0) const; - - [[nodiscard]] std::vector getImageDataAsRGBA8888(uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0, uint16_t slice = 0) const; - - bool setImage(std::span imageData_, ImageFormat format_, uint16_t width_, uint16_t height_, ImageConversion::ResizeFilter filter = ImageConversion::ResizeFilter::BILINEAR, uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0, uint16_t slice = 0); - - bool setImage(const std::string& imagePath, ImageConversion::ResizeFilter filter = ImageConversion::ResizeFilter::BILINEAR, uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0, uint16_t slice = 0); - - [[nodiscard]] std::vector convertAndSaveImageDataToFile(uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0, uint16_t slice = 0) const; - - void convertAndSaveImageDataToFile(const std::string& imagePath, uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0, uint16_t slice = 0) const; - - [[nodiscard]] bool hasThumbnailData() const; - - [[nodiscard]] std::span getThumbnailDataRaw() const; - - [[nodiscard]] std::vector getThumbnailDataAs(ImageFormat newFormat) const; - - [[nodiscard]] std::vector getThumbnailDataAsRGBA8888() const; - - void computeThumbnail(ImageConversion::ResizeFilter filter = ImageConversion::ResizeFilter::BILINEAR); - - void removeThumbnail(); - - [[nodiscard]] std::vector convertAndSaveThumbnailDataToFile() const; - - void convertAndSaveThumbnailDataToFile(const std::string& imagePath) const; - - [[nodiscard]] std::vector bake(); - - void bake(const std::string& vtfPath); - -protected: - [[nodiscard]] ImageFormat getDefaultFormat() const; - - [[nodiscard]] static uint8_t getFaceCountFor(uint16_t width, uint16_t height, uint32_t majorVersion, uint32_t minorVersion, Flags flags, uint16_t startFrame); - - [[nodiscard]] Resource* getResourceInternal(Resource::Type type); - - void setResourceInternal(Resource::Type type, std::span data_); - - void removeResourceInternal(Resource::Type type); - - void regenerateImageData(ImageFormat newFormat, uint16_t newWidth, uint16_t newHeight, uint8_t newMipCount, uint16_t newFrameCount, uint8_t newFaceCount, uint16_t newSliceCount, ImageConversion::ResizeFilter filter = ImageConversion::ResizeFilter::BILINEAR); - - bool opened = false; - - std::vector data; - - //uint32_t signature; - uint32_t majorVersion{}; - uint32_t minorVersion{}; - - uint32_t headerSize{}; - - uint16_t width{}; - uint16_t height{}; - Flags flags{}; - - uint16_t frameCount = 1; - uint16_t startFrame{}; - - //uint8_t _padding0[4]; - sourcepp::math::Vec3f reflectivity{}; - //uint8_t _padding1[4]; - - float bumpMapScale{}; - ImageFormat format{}; - uint8_t mipCount = 1; - - ImageFormat thumbnailFormat{}; - uint8_t thumbnailWidth{}; - uint8_t thumbnailHeight{}; - - // 1 for v7.1 and lower - uint16_t sliceCount = 1; - //uint8_t _padding2[3]; - - // Technically added in v7.3, but we use it to store image and thumbnail data in v7.2 and lower anyway - //uint32_t resourceCount; - std::vector resources; - //uint8_t _padding3[4]; - - // These aren't in the header, these are for VTF modification - uint8_t compressionLevel = 0; - ImageConversion::ResizeMethod imageWidthResizeMethod = ImageConversion::ResizeMethod::POWER_OF_TWO_BIGGER; - ImageConversion::ResizeMethod imageHeightResizeMethod = ImageConversion::ResizeMethod::POWER_OF_TWO_BIGGER; -}; -SOURCEPP_BITFLAGS_ENUM(VTF::Flags) - -} // namespace vtfpp +#include "ImageConversion.h" +#include "ImageFormats.h" +#include "VTF.h" diff --git a/src/vtfpp/ImageConversion.cpp b/src/vtfpp/ImageConversion.cpp index a81baa676..4bca85332 100644 --- a/src/vtfpp/ImageConversion.cpp +++ b/src/vtfpp/ImageConversion.cpp @@ -3,6 +3,11 @@ #include #include #include +#include + +#ifdef SOURCEPP_BUILD_WITH_TBB +#include +#endif #include #include @@ -216,339 +221,307 @@ namespace { } [[nodiscard]] std::vector convertImageDataToRGBA8888(std::span imageData, ImageFormat format) { + using namespace ImageConversion; + if (imageData.empty()) { return {}; } + if (format == ImageFormat::RGBA8888 || format == ImageFormat::UVWQ8888 || format == ImageFormat::UVLX8888) { + return {imageData.begin(), imageData.end()}; + } + std::vector newData; - newData.reserve(imageData.size() / (ImageFormatDetails::bpp(format) / 8) * (ImageFormatDetails::bpp(ImageFormat::RGBA8888) / 8)); + newData.resize(imageData.size() / (ImageFormatDetails::bpp(format) / 8) * (ImageFormatDetails::bpp(ImageFormat::RGBA8888) / 8)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA8888)}; + + #define VTFPP_REMAP_TO_8(value, shift) math::remap((value), (1 << (shift)) - 1, (1 << 8) - 1) + + #define VTFPP_CONVERT_DETAIL(InputType, r, g, b, a, ...) \ + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::InputType)}; \ + std::transform(__VA_ARGS__ __VA_OPT__(,) imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::InputType pixel) -> ImagePixel::RGBA8888 { \ + return {(r), (g), (b), (a)}; \ + }) +#ifdef SOURCEPP_BUILD_WITH_TBB + #define VTFPP_CONVERT(InputType, r, g, b, a) VTFPP_CONVERT_DETAIL(InputType, r, g, b, a, std::execution::unseq) +#else + #define VTFPP_CONVERT(InputType, r, g, b, a) VTFPP_CONVERT_DETAIL(InputType, r, g, b, a) +#endif + #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, r, g, b, a) \ + case InputType: { VTFPP_CONVERT(InputType, r, g, b, a); } break + + #define VTFPP_CONVERT_REMAP(InputType, r, g, b, a) \ + do { \ + if constexpr (ImageFormatDetails::alpha(ImageFormat::InputType) > 1) { \ + VTFPP_CONVERT(InputType, \ + static_cast(VTFPP_REMAP_TO_8((r), ImageFormatDetails::red(ImageFormat::InputType))), \ + static_cast(VTFPP_REMAP_TO_8((g), ImageFormatDetails::green(ImageFormat::InputType))), \ + static_cast(VTFPP_REMAP_TO_8((b), ImageFormatDetails::blue(ImageFormat::InputType))), \ + static_cast(VTFPP_REMAP_TO_8((a), ImageFormatDetails::alpha(ImageFormat::InputType)))); \ + } else if constexpr (ImageFormatDetails::alpha(ImageFormat::InputType) == 1) { \ + VTFPP_CONVERT(InputType, \ + static_cast(VTFPP_REMAP_TO_8((r), ImageFormatDetails::red(ImageFormat::InputType))), \ + static_cast(VTFPP_REMAP_TO_8((g), ImageFormatDetails::green(ImageFormat::InputType))), \ + static_cast(VTFPP_REMAP_TO_8((b), ImageFormatDetails::blue(ImageFormat::InputType))), \ + static_cast((a) * 0xff)); \ + } else { \ + VTFPP_CONVERT(InputType, \ + static_cast(VTFPP_REMAP_TO_8((r), ImageFormatDetails::red(ImageFormat::InputType))), \ + static_cast(VTFPP_REMAP_TO_8((g), ImageFormatDetails::green(ImageFormat::InputType))), \ + static_cast(VTFPP_REMAP_TO_8((b), ImageFormatDetails::blue(ImageFormat::InputType))), \ + static_cast(a)); \ + } \ + } while (false) + #define VTFPP_CASE_CONVERT_REMAP_AND_BREAK(InputType, r, g, b, a) \ + case InputType: { VTFPP_CONVERT_REMAP(InputType, r, g, b, a); } \ + break + switch (format) { using enum ImageFormat; - case RGBA8888: - case UVWQ8888: - case UVLX8888: - newData = {imageData.begin(), imageData.end()}; - break; - case ABGR8888: - for (int i = 0; i < imageData.size(); i += 4) { - newData.push_back(imageData[i + 3]); - newData.push_back(imageData[i + 2]); - newData.push_back(imageData[i + 1]); - newData.push_back(imageData[i]); - } - break; - case RGB888: - for (int i = 0; i < imageData.size(); i += 3) { - newData.push_back(imageData[i]); - newData.push_back(imageData[i + 1]); - newData.push_back(imageData[i + 2]); - newData.push_back(std::byte{0xff}); - } - break; - case RGB888_BLUESCREEN: - for (int i = 0; i < imageData.size(); i += 3) { - newData.push_back(imageData[i]); - newData.push_back(imageData[i + 1]); - newData.push_back(imageData[i + 2]); - if (static_cast(imageData[i]) == 0 && static_cast(imageData[i + 1]) == 0 && static_cast(imageData[i + 2]) == 0xff) { - newData.push_back(std::byte{0}); - } else { - newData.push_back(std::byte{0xff}); - } - } - break; - case BGR888: - for (int i = 0; i < imageData.size(); i += 3) { - newData.push_back(imageData[i + 2]); - newData.push_back(imageData[i + 1]); - newData.push_back(imageData[i]); - newData.push_back(std::byte{0xff}); - } - break; - case BGR888_BLUESCREEN: - for (int i = 0; i < imageData.size(); i += 3) { - newData.push_back(imageData[i + 2]); - newData.push_back(imageData[i + 1]); - newData.push_back(imageData[i]); - if (static_cast(imageData[i + 2]) == 0 && static_cast(imageData[i + 1]) == 0 && static_cast(imageData[i]) == 0xff) { - newData.push_back(std::byte{0}); - } else { - newData.push_back(std::byte{0xff}); - } - } - break; - case RGB565: - for (int i = 0; i < imageData.size(); i += 2) { - auto pixel = *reinterpret_cast(&imageData[i]); - newData.push_back(static_cast(((( pixel & 0x1f) * 527) + 23) >> 6)); - newData.push_back(static_cast(((((pixel >> 5) & 0x3f) * 259) + 33) >> 6)); - newData.push_back(static_cast(((((pixel >> 11) & 0x1f) * 527) + 23) >> 6)); - newData.push_back(std::byte{0xff}); - } - break; - case P8: - case I8: - for (int i = 0; i < imageData.size(); i += 1) { - newData.push_back(imageData[i]); - newData.push_back(imageData[i]); - newData.push_back(imageData[i]); - newData.push_back(std::byte{0xff}); - } - break; - case IA88: - for (int i = 0; i < imageData.size(); i += 2) { - newData.push_back(imageData[i]); - newData.push_back(imageData[i]); - newData.push_back(imageData[i]); - newData.push_back(imageData[i + 1]); - } - break; - case A8: - for (int i = 0; i < imageData.size(); i += 1) { - newData.push_back(std::byte{0}); - newData.push_back(std::byte{0}); - newData.push_back(std::byte{0}); - newData.push_back(imageData[i]); - } - break; - case ARGB8888: - for (int i = 0; i < imageData.size(); i += 4) { - newData.push_back(imageData[i + 3]); - newData.push_back(imageData[i]); - newData.push_back(imageData[i + 1]); - newData.push_back(imageData[i + 2]); - } - break; - case BGRA8888: - for (int i = 0; i < imageData.size(); i += 4) { - newData.push_back(imageData[i + 2]); - newData.push_back(imageData[i + 1]); - newData.push_back(imageData[i]); - newData.push_back(imageData[i + 3]); - } - break; - case BGRX8888: - for (int i = 0; i < imageData.size(); i += 4) { - newData.push_back(imageData[i + 2]); - newData.push_back(imageData[i + 1]); - newData.push_back(imageData[i]); - newData.push_back(std::byte{0xff}); - } - break; - case BGR565: - for (int i = 0; i < imageData.size(); i += 2) { - auto pixel = *reinterpret_cast(&imageData[i]); - newData.push_back(static_cast(((((pixel >> 11) & 0x1f) * 527) + 23) >> 6)); - newData.push_back(static_cast(((((pixel >> 5) & 0x3f) * 259) + 33) >> 6)); - newData.push_back(static_cast(((( pixel & 0x1f) * 527) + 23) >> 6)); - newData.push_back(std::byte{0xff}); - } - break; - case BGRA5551: - for (int i = 0; i < imageData.size(); i += 2) { - auto pixel = *reinterpret_cast(&imageData[i]); - newData.push_back(static_cast((((pixel & 0x7c00) >> 10) * 255 + 15) / 31)); - newData.push_back(static_cast((((pixel & 0x03e0) >> 5) * 255 + 15) / 31)); - newData.push_back(static_cast((( pixel & 0x001f ) * 255 + 15) / 31)); - newData.push_back(static_cast((((pixel & 0x8000) >> 15) * 255))); - } - break; - case BGRX5551: - for (int i = 0; i < imageData.size(); i += 2) { - auto pixel = *reinterpret_cast(&imageData[i]); - newData.push_back(static_cast((((pixel & 0x7c00) >> 10) * 255 + 15) / 31)); - newData.push_back(static_cast((((pixel & 0x03e0) >> 5) * 255 + 15) / 31)); - newData.push_back(static_cast((( pixel & 0x001f ) * 255 + 15) / 31)); - newData.push_back(std::byte{255}); - } - break; - case BGRA4444: - for (int i = 0; i < imageData.size(); i += 2) { - newData.push_back(static_cast(((static_cast(imageData[i + 1]) & 0x0f) * 255 + 7) / 15)); - newData.push_back(static_cast(((static_cast(imageData[i]) >> 4) * 255 + 7) / 15)); - newData.push_back(static_cast(((static_cast(imageData[i]) & 0x0f) * 255 + 7) / 15)); - newData.push_back(static_cast(((static_cast(imageData[i + 1]) >> 4) * 255 + 7) / 15)); - } - break; - case UV88: - for (int i = 0; i < imageData.size(); i += 2) { - newData.push_back(imageData[i]); - newData.push_back(imageData[i + 1]); - newData.push_back(std::byte{0}); - newData.push_back(std::byte{0xff}); - } - break; - default: - break; + VTFPP_CASE_CONVERT_AND_BREAK( ABGR8888, pixel.r, pixel.g, pixel.b, pixel.a); + VTFPP_CASE_CONVERT_AND_BREAK( RGB888, pixel.r, pixel.g, pixel.b, 0xff); + VTFPP_CASE_CONVERT_AND_BREAK( RGB888_BLUESCREEN, pixel.r, pixel.g, pixel.b, static_cast((pixel.r == 0 && pixel.g == 0 && pixel.b == 0xff) ? 0 : 0xff)); + VTFPP_CASE_CONVERT_AND_BREAK( BGR888, pixel.r, pixel.g, pixel.b, 0xff); + VTFPP_CASE_CONVERT_AND_BREAK( BGR888_BLUESCREEN, pixel.r, pixel.g, pixel.b, static_cast((pixel.r == 0 && pixel.g == 0 && pixel.b == 0xff) ? 0 : 0xff)); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(RGB565, pixel.r, pixel.g, pixel.b, 0xff); + VTFPP_CASE_CONVERT_AND_BREAK( P8, pixel.p, pixel.p, pixel.p, 0xff); + VTFPP_CASE_CONVERT_AND_BREAK( I8, pixel.i, pixel.i, pixel.i, 0xff); + VTFPP_CASE_CONVERT_AND_BREAK( IA88, pixel.i, pixel.i, pixel.i, pixel.a); + VTFPP_CASE_CONVERT_AND_BREAK( A8, 0, 0, 0, pixel.a); + VTFPP_CASE_CONVERT_AND_BREAK( ARGB8888, pixel.r, pixel.g, pixel.b, pixel.a); + VTFPP_CASE_CONVERT_AND_BREAK( BGRA8888, pixel.r, pixel.g, pixel.b, pixel.a); + VTFPP_CASE_CONVERT_AND_BREAK( BGRX8888, pixel.r, pixel.g, pixel.b, 0xff); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(BGR565, pixel.r, pixel.g, pixel.b, 0xff); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(BGRA5551, pixel.r, pixel.g, pixel.b, pixel.a); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(BGRX5551, pixel.r, pixel.g, pixel.b, 0x1); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(BGRA4444, pixel.r, pixel.g, pixel.b, pixel.a); + VTFPP_CASE_CONVERT_AND_BREAK( UV88, pixel.u, pixel.v, 0, 0xff); + default: break; } + + #undef VTFPP_CASE_CONVERT_REMAP_AND_BREAK + #undef VTFPP_CONVERT_REMAP + #undef VTFPP_CASE_CONVERT_AND_BREAK + #undef VTFPP_CONVERT + #undef VTFPP_CONVERT_DETAIL + #undef VTFPP_REMAP_TO_8 + return newData; } [[nodiscard]] std::vector convertImageDataFromRGBA8888(std::span imageData, ImageFormat format) { + using namespace ImageConversion; + if (imageData.empty()) { return {}; } + if (format == ImageFormat::RGBA8888) { + return {imageData.begin(), imageData.end()}; + } + + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA8888)}; std::vector newData; - newData.reserve(imageData.size() / (ImageFormatDetails::bpp(ImageFormat::RGBA8888) / 8) * (ImageFormatDetails::bpp(format) / 8)); + newData.resize(imageData.size() / (ImageFormatDetails::bpp(ImageFormat::RGBA8888) / 8) * (ImageFormatDetails::bpp(format) / 8)); + + #define VTFPP_REMAP_FROM_8(value, shift) math::remap((value), (1 << 8) - 1, (1 << (shift)) - 1) + +#ifdef SOURCEPP_BUILD_WITH_TBB + #define VTFPP_CONVERT(InputType, ...) \ + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::InputType)}; \ + std::transform(std::execution::unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA8888 pixel) -> ImagePixel::InputType { \ + return __VA_ARGS__; \ + }) +#else + #define VTFPP_CONVERT(InputType, ...) \ + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::InputType)}; \ + std::transform(imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA8888 pixel) -> ImagePixel::InputType { \ + return __VA_ARGS__; \ + }) +#endif + #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, ...) \ + case InputType: { VTFPP_CONVERT(InputType, __VA_ARGS__); } break + switch (format) { using enum ImageFormat; - case RGBA8888: - case UVWQ8888: - case UVLX8888: - newData = {imageData.begin(), imageData.end()}; - break; - case ABGR8888: - for (int i = 0; i < imageData.size(); i += 4) { - newData.push_back(imageData[i + 3]); - newData.push_back(imageData[i + 2]); - newData.push_back(imageData[i + 1]); - newData.push_back(imageData[i]); - } - break; - case RGB888: - for (int i = 0; i < imageData.size(); i += 4) { - newData.push_back(imageData[i]); - newData.push_back(imageData[i + 1]); - newData.push_back(imageData[i + 2]); - } - break; - case RGB888_BLUESCREEN: - for (int i = 0; i < imageData.size(); i += 4) { - if (static_cast(imageData[i + 3]) == 0) { - newData.push_back(std::byte{0}); - newData.push_back(std::byte{0}); - newData.push_back(std::byte{0xff}); - } else { - newData.push_back(imageData[i]); - newData.push_back(imageData[i + 1]); - newData.push_back(imageData[i + 2]); - } - } - break; - case BGR888: - for (int i = 0; i < imageData.size(); i += 4) { - newData.push_back(imageData[i + 2]); - newData.push_back(imageData[i + 1]); - newData.push_back(imageData[i]); - } - break; - case BGR888_BLUESCREEN: - for (int i = 0; i < imageData.size(); i += 4) { - if (static_cast(imageData[i + 3]) == 0) { - newData.push_back(std::byte{0xff}); - newData.push_back(std::byte{0}); - newData.push_back(std::byte{0}); - } else { - newData.push_back(imageData[i + 2]); - newData.push_back(imageData[i + 1]); - newData.push_back(imageData[i]); - } - } - break; - case RGB565: - for (int i = 0; i < imageData.size(); i += 4) { - uint16_t rgb565 = ((static_cast(imageData[i + 2]) >> 3) << 11) | ((static_cast(imageData[i + 1]) >> 2) << 5) | (static_cast(imageData[i]) >> 3); - newData.push_back({}); - newData.push_back({}); - *reinterpret_cast(&newData[newData.size() - 2]) = rgb565; - } - break; - case P8: - case I8: - for (int i = 0; i < imageData.size(); i += 4) { - newData.push_back(imageData[i]); - } - break; - case IA88: - for (int i = 0; i < imageData.size(); i += 4) { - newData.push_back(imageData[i]); - newData.push_back(imageData[i + 3]); - } - break; - case A8: - for (int i = 0; i < imageData.size(); i += 4) { - newData.push_back(imageData[i + 3]); - } - break; - case ARGB8888: - for (int i = 0; i < imageData.size(); i += 4) { - newData.push_back(imageData[i + 1]); - newData.push_back(imageData[i + 2]); - newData.push_back(imageData[i + 3]); - newData.push_back(imageData[i]); - } - break; - case BGRA8888: - for (int i = 0; i < imageData.size(); i += 4) { - newData.push_back(imageData[i + 2]); - newData.push_back(imageData[i + 1]); - newData.push_back(imageData[i]); - newData.push_back(imageData[i + 3]); - } - break; - case BGRX8888: - for (int i = 0; i < imageData.size(); i += 4) { - newData.push_back(imageData[i + 2]); - newData.push_back(imageData[i + 1]); - newData.push_back(imageData[i]); - newData.push_back(std::byte{0}); - } - break; - case BGR565: - for (int i = 0; i < imageData.size(); i += 4) { - uint16_t bgr565 = ((static_cast(imageData[i]) >> 3) << 11) | ((static_cast(imageData[i + 1]) >> 2) << 5) | (static_cast(imageData[i + 2]) >> 3); - newData.push_back({}); - newData.push_back({}); - *reinterpret_cast(&newData[newData.size() - 2]) = bgr565; - } - break; - case BGRA5551: - for (int i = 0; i < imageData.size(); i += 4) { - uint16_t bgra5551 = ((static_cast(imageData[i + 3]) > 0 ? uint16_t{1} : uint16_t{0}) << 15) | ((static_cast(imageData[i]) >> 3) << 10) | ((static_cast(imageData[i + 1]) >> 3) << 5) | ((static_cast(imageData[i + 2]) >> 3)); - newData.push_back({}); - newData.push_back({}); - *reinterpret_cast(&newData[newData.size() - 2]) = bgra5551; - } - break; - case BGRX5551: - for (int i = 0; i < imageData.size(); i += 4) { - uint16_t bgra5551 = ((static_cast(imageData[i]) >> 3) << 10) | ((static_cast(imageData[i + 1]) >> 3) << 5) | ((static_cast(imageData[i + 2]) >> 3)); - newData.push_back({}); - newData.push_back({}); - *reinterpret_cast(&newData[newData.size() - 2]) = bgra5551; - } - break; - case BGRA4444: - for (int i = 0; i < imageData.size(); i += 4) { - auto r = static_cast(imageData[i] >> 4); - auto g = static_cast(imageData[i + 1] >> 4); - auto b = static_cast(imageData[i + 2] >> 4); - auto a = static_cast(imageData[i + 3] >> 4); - uint16_t bgra4444 = (a << 12) | (r << 8) | (g << 4) | b; - newData.push_back({}); - newData.push_back({}); - *reinterpret_cast(&newData[newData.size() - 2]) = bgra4444; - } - break; - case UV88: - for (int i = 0; i < imageData.size(); i += 4) { - newData.push_back(imageData[i]); - newData.push_back(imageData[i + 1]); - } - break; - default: - break; + VTFPP_CASE_CONVERT_AND_BREAK(ABGR8888, {pixel.a, pixel.b, pixel.g, pixel.r}); + VTFPP_CASE_CONVERT_AND_BREAK(RGB888, {pixel.r, pixel.g, pixel.b}); + VTFPP_CASE_CONVERT_AND_BREAK(RGB888_BLUESCREEN, pixel.a < 0xff ? ImagePixel::RGB888_BLUESCREEN{pixel.r, pixel.g, pixel.b} : ImagePixel::RGB888_BLUESCREEN{0, 0, 0xff}); + VTFPP_CASE_CONVERT_AND_BREAK(BGR888, {pixel.b, pixel.g, pixel.r}); + VTFPP_CASE_CONVERT_AND_BREAK(BGR888_BLUESCREEN, pixel.a < 0xff ? ImagePixel::BGR888_BLUESCREEN{pixel.b, pixel.g, pixel.r} : ImagePixel::BGR888_BLUESCREEN{0xff, 0, 0}); + VTFPP_CASE_CONVERT_AND_BREAK(RGB565, {VTFPP_REMAP_FROM_8(pixel.r, 5), VTFPP_REMAP_FROM_8(pixel.g, 6), VTFPP_REMAP_FROM_8(pixel.b, 5)}); + VTFPP_CASE_CONVERT_AND_BREAK(P8, {pixel.r}); + VTFPP_CASE_CONVERT_AND_BREAK(I8, {pixel.r}); + VTFPP_CASE_CONVERT_AND_BREAK(IA88, {pixel.r, pixel.a}); + VTFPP_CASE_CONVERT_AND_BREAK(A8, {pixel.a}); + VTFPP_CASE_CONVERT_AND_BREAK(ARGB8888, {pixel.a, pixel.r, pixel.g, pixel.b}); + VTFPP_CASE_CONVERT_AND_BREAK(BGRA8888, {pixel.b, pixel.g, pixel.r, pixel.a}); + VTFPP_CASE_CONVERT_AND_BREAK(BGRX8888, {pixel.b, pixel.g, pixel.r, 0xff}); + VTFPP_CASE_CONVERT_AND_BREAK(BGR565, {VTFPP_REMAP_FROM_8(pixel.b, 5), VTFPP_REMAP_FROM_8(pixel.g, 6), VTFPP_REMAP_FROM_8(pixel.r, 5)}); + VTFPP_CASE_CONVERT_AND_BREAK(BGRA5551, {VTFPP_REMAP_FROM_8(pixel.b, 5), VTFPP_REMAP_FROM_8(pixel.g, 5), VTFPP_REMAP_FROM_8(pixel.r, 5), static_cast(pixel.a < 0xff ? 1 : 0)}); + VTFPP_CASE_CONVERT_AND_BREAK(BGRX5551, {VTFPP_REMAP_FROM_8(pixel.b, 5), VTFPP_REMAP_FROM_8(pixel.g, 5), VTFPP_REMAP_FROM_8(pixel.r, 5), 0x1}); + VTFPP_CASE_CONVERT_AND_BREAK(BGRA4444, {VTFPP_REMAP_FROM_8(pixel.b, 4), VTFPP_REMAP_FROM_8(pixel.g, 4), VTFPP_REMAP_FROM_8(pixel.r, 4), VTFPP_REMAP_FROM_8(pixel.a, 4)}); + VTFPP_CASE_CONVERT_AND_BREAK(UV88, {pixel.r, pixel.g}); + default: break; } + + #undef VTFPP_CASE_CONVERT_AND_BREAK + #undef VTFPP_CONVERT + #undef VTFPP_REMAP_FROM_8 + return newData; } -[[nodiscard]] std::vector decodeImageDataToRGBA8888(std::span imageData, ImageFormat format, uint16_t width, uint16_t height) { - return ::convertImageDataUsingCompressonator(imageData, format, ImageFormat::RGBA8888, width, height); +[[nodiscard]] std::vector convertImageDataToRGBA16161616(std::span imageData, ImageFormat format) { + using namespace ImageConversion; + + if (imageData.empty()) { + return {}; + } + + if (format == ImageFormat::RGBA16161616) { + return {imageData.begin(), imageData.end()}; + } + + std::vector newData; + newData.resize(imageData.size() / (ImageFormatDetails::bpp(format) / 8) * (ImageFormatDetails::bpp(ImageFormat::RGBA16161616) / 8)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA16161616)}; + + #define VTFPP_REMAP_TO_16(value, shift) math::remap((value), (1 << (shift)) - 1, (1 << 16) - 1) + + #define VTFPP_CONVERT_DETAIL(InputType, r, g, b, a, ...) \ + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::InputType)}; \ + std::transform(__VA_ARGS__ __VA_OPT__(,) imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::InputType pixel) -> ImagePixel::RGBA16161616 { \ + return { static_cast(r), static_cast(g), static_cast(b), static_cast(a) }; \ + }) +#ifdef SOURCEPP_BUILD_WITH_TBB + #define VTFPP_CONVERT(InputType, r, g, b, a) VTFPP_CONVERT_DETAIL(InputType, r, g, b, a, std::execution::unseq) +#else + #define VTFPP_CONVERT(InputType, r, g, b, a) VTFPP_CONVERT_DETAIL(InputType, r, g, b, a) +#endif + #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, r, g, b, a) \ + case InputType: { VTFPP_CONVERT(InputType, r, g, b, a); } break + + #define VTFPP_CONVERT_REMAP(InputType, r, g, b, a) \ + do { \ + if constexpr (ImageFormatDetails::alpha(ImageFormat::InputType) > 1) { \ + VTFPP_CONVERT(InputType, \ + VTFPP_REMAP_TO_16((r), ImageFormatDetails::red(ImageFormat::InputType)), \ + VTFPP_REMAP_TO_16((g), ImageFormatDetails::green(ImageFormat::InputType)), \ + VTFPP_REMAP_TO_16((b), ImageFormatDetails::blue(ImageFormat::InputType)), \ + VTFPP_REMAP_TO_16((a), ImageFormatDetails::alpha(ImageFormat::InputType))); \ + } else if constexpr (ImageFormatDetails::alpha(ImageFormat::InputType) == 1) { \ + VTFPP_CONVERT(InputType, \ + VTFPP_REMAP_TO_16((r), ImageFormatDetails::red(ImageFormat::InputType)), \ + VTFPP_REMAP_TO_16((g), ImageFormatDetails::green(ImageFormat::InputType)), \ + VTFPP_REMAP_TO_16((b), ImageFormatDetails::blue(ImageFormat::InputType)), \ + (a) * 0xff); \ + } else { \ + VTFPP_CONVERT(InputType, \ + VTFPP_REMAP_TO_16((r), ImageFormatDetails::red(ImageFormat::InputType)), \ + VTFPP_REMAP_TO_16((g), ImageFormatDetails::green(ImageFormat::InputType)), \ + VTFPP_REMAP_TO_16((b), ImageFormatDetails::blue(ImageFormat::InputType)), \ + (a)); \ + } \ + } while (false) + #define VTFPP_CASE_CONVERT_REMAP_AND_BREAK(InputType, r, g, b, a) \ + case InputType: { VTFPP_CONVERT_REMAP(InputType, r, g, b, a); } \ + break + + switch (format) { + using enum ImageFormat; + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(ABGR8888, pixel.r, pixel.g, pixel.b, pixel.a); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(RGB888, pixel.r, pixel.g, pixel.b, 0xff); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(RGB888_BLUESCREEN, pixel.r, pixel.g, pixel.b, (pixel.r == 0 && pixel.g == 0 && pixel.b == 0xff) ? 0 : 0xff); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(BGR888, pixel.r, pixel.g, pixel.b, 0xff); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(BGR888_BLUESCREEN, pixel.r, pixel.g, pixel.b, (pixel.r == 0 && pixel.g == 0 && pixel.b == 0xff) ? 0 : 0xff); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(RGB565, pixel.r, pixel.g, pixel.b, 0xff); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(P8, pixel.p, pixel.p, pixel.p, 0xff); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(I8, pixel.i, pixel.i, pixel.i, 0xff); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(IA88, pixel.i, pixel.i, pixel.i, pixel.a); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(A8, 0, 0, 0, pixel.a); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(ARGB8888, pixel.r, pixel.g, pixel.b, pixel.a); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(BGRA8888, pixel.r, pixel.g, pixel.b, pixel.a); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(BGRX8888, pixel.r, pixel.g, pixel.b, 0xff); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(BGR565, pixel.r, pixel.g, pixel.b, 0xff); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(BGRA5551, pixel.r, pixel.g, pixel.b, pixel.a); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(BGRX5551, pixel.r, pixel.g, pixel.b, 1); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(BGRA4444, pixel.r, pixel.g, pixel.b, pixel.a); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(UV88, pixel.u, pixel.v, 0, 0xff); + default: break; + } + + #undef VTFPP_CASE_CONVERT_REMAP_AND_BREAK + #undef VTFPP_CONVERT_REMAP + #undef VTFPP_CASE_CONVERT_AND_BREAK + #undef VTFPP_CONVERT + #undef VTFPP_CONVERT_DETAIL + #undef VTFPP_REMAP_TO_16 + + return newData; } -[[nodiscard]] std::vector encodeImageDataFromRGBA8888(std::span imageData, ImageFormat format, uint16_t width, uint16_t height) { - return ::convertImageDataUsingCompressonator(imageData, ImageFormat::RGBA8888, format, width, height); +[[nodiscard]] std::vector convertImageDataFromRGBA16161616(std::span imageData, ImageFormat format) { + using namespace ImageConversion; + + if (imageData.empty()) { + return {}; + } + + if (format == ImageFormat::RGBA16161616) { + return {imageData.begin(), imageData.end()}; + } + + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA16161616)}; + std::vector newData; + newData.resize(imageData.size() / (ImageFormatDetails::bpp(ImageFormat::RGBA16161616) / 8) * (ImageFormatDetails::bpp(format) / 8)); + + #define VTFPP_REMAP_FROM_16(value, shift) static_cast(math::remap((value), (1 << 16) - 1, (1 << (shift)) - 1)) + +#ifdef SOURCEPP_BUILD_WITH_TBB + #define VTFPP_CONVERT(InputType, ...) \ + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::InputType)}; \ + std::transform(std::execution::unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA16161616 pixel) -> ImagePixel::InputType { \ + return __VA_ARGS__; \ + }) +#else + #define VTFPP_CONVERT(InputType, ...) \ + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::InputType)}; \ + std::transform(imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA16161616 pixel) -> ImagePixel::InputType { \ + return __VA_ARGS__; \ + }) +#endif + #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, ...) \ + case InputType: { VTFPP_CONVERT(InputType, __VA_ARGS__); } break + + switch (format) { + using enum ImageFormat; + VTFPP_CASE_CONVERT_AND_BREAK(ABGR8888, {VTFPP_REMAP_FROM_16(pixel.a, 8), VTFPP_REMAP_FROM_16(pixel.b, 8), VTFPP_REMAP_FROM_16(pixel.g, 8), VTFPP_REMAP_FROM_16(pixel.r, 8)}); + VTFPP_CASE_CONVERT_AND_BREAK(RGB888, {VTFPP_REMAP_FROM_16(pixel.r, 8), VTFPP_REMAP_FROM_16(pixel.g, 8), VTFPP_REMAP_FROM_16(pixel.b, 8)}); + VTFPP_CASE_CONVERT_AND_BREAK(RGB888_BLUESCREEN, pixel.a < 0xffff ? ImagePixel::RGB888_BLUESCREEN{VTFPP_REMAP_FROM_16(pixel.r, 8), VTFPP_REMAP_FROM_16(pixel.g, 8), VTFPP_REMAP_FROM_16(pixel.b, 8)} : ImagePixel::RGB888_BLUESCREEN{0, 0, 0xff}); + VTFPP_CASE_CONVERT_AND_BREAK(BGR888, {VTFPP_REMAP_FROM_16(pixel.b, 8), VTFPP_REMAP_FROM_16(pixel.g, 8), VTFPP_REMAP_FROM_16(pixel.r, 8)}); + VTFPP_CASE_CONVERT_AND_BREAK(BGR888_BLUESCREEN, pixel.a < 0xffff ? ImagePixel::BGR888_BLUESCREEN{VTFPP_REMAP_FROM_16(pixel.b, 8), VTFPP_REMAP_FROM_16(pixel.g, 8), VTFPP_REMAP_FROM_16(pixel.r, 8)} : ImagePixel::BGR888_BLUESCREEN{0xff, 0, 0}); + VTFPP_CASE_CONVERT_AND_BREAK(RGB565, {VTFPP_REMAP_FROM_16(pixel.r, 5), VTFPP_REMAP_FROM_16(pixel.g, 6), VTFPP_REMAP_FROM_16(pixel.b, 5)}); + VTFPP_CASE_CONVERT_AND_BREAK(P8, {VTFPP_REMAP_FROM_16(pixel.r, 8)}); + VTFPP_CASE_CONVERT_AND_BREAK(I8, {VTFPP_REMAP_FROM_16(pixel.r, 8)}); + VTFPP_CASE_CONVERT_AND_BREAK(IA88, {VTFPP_REMAP_FROM_16(pixel.r, 8), VTFPP_REMAP_FROM_16(pixel.a, 8)}); + VTFPP_CASE_CONVERT_AND_BREAK(A8, {VTFPP_REMAP_FROM_16(pixel.a, 8)}); + VTFPP_CASE_CONVERT_AND_BREAK(ARGB8888, {VTFPP_REMAP_FROM_16(pixel.a, 8), VTFPP_REMAP_FROM_16(pixel.r, 8), VTFPP_REMAP_FROM_16(pixel.g, 8), VTFPP_REMAP_FROM_16(pixel.b, 8)}); + VTFPP_CASE_CONVERT_AND_BREAK(BGRA8888, {VTFPP_REMAP_FROM_16(pixel.b, 8), VTFPP_REMAP_FROM_16(pixel.g, 8), VTFPP_REMAP_FROM_16(pixel.r, 8), VTFPP_REMAP_FROM_16(pixel.a, 8)}); + VTFPP_CASE_CONVERT_AND_BREAK(BGRX8888, {VTFPP_REMAP_FROM_16(pixel.b, 8), VTFPP_REMAP_FROM_16(pixel.g, 8), VTFPP_REMAP_FROM_16(pixel.r, 8), 0xff}); + VTFPP_CASE_CONVERT_AND_BREAK(BGR565, {VTFPP_REMAP_FROM_16(pixel.b, 5), VTFPP_REMAP_FROM_16(pixel.g, 6), VTFPP_REMAP_FROM_16(pixel.r, 5)}); + VTFPP_CASE_CONVERT_AND_BREAK(BGRA5551, {VTFPP_REMAP_FROM_16(pixel.b, 5), VTFPP_REMAP_FROM_16(pixel.g, 5), VTFPP_REMAP_FROM_16(pixel.r, 5), static_cast(pixel.a < 0xffff ? 1 : 0)}); + VTFPP_CASE_CONVERT_AND_BREAK(BGRX5551, {VTFPP_REMAP_FROM_16(pixel.b, 5), VTFPP_REMAP_FROM_16(pixel.g, 5), VTFPP_REMAP_FROM_16(pixel.r, 5), 0x1}); + VTFPP_CASE_CONVERT_AND_BREAK(BGRA4444, {VTFPP_REMAP_FROM_16(pixel.b, 4), VTFPP_REMAP_FROM_16(pixel.g, 4), VTFPP_REMAP_FROM_16(pixel.r, 4), VTFPP_REMAP_FROM_16(pixel.a, 4)}); + VTFPP_CASE_CONVERT_AND_BREAK(UV88, {VTFPP_REMAP_FROM_16(pixel.r, 8), VTFPP_REMAP_FROM_16(pixel.g, 8)}); + default: break; + } + + #undef VTFPP_CASE_CONVERT_AND_BREAK + #undef VTFPP_CONVERT + #undef VTFPP_REMAP_FROM_16 + + return newData; } [[nodiscard]] std::vector convertImageDataToRGBA32323232F(std::span imageData, ImageFormat format) { @@ -556,118 +529,83 @@ namespace { return {}; } + if (format == ImageFormat::RGBA32323232F) { + return {imageData.begin(), imageData.end()}; + } + std::vector newData; - newData.reserve(imageData.size() / (ImageFormatDetails::bpp(format) / 8) * (ImageFormatDetails::bpp(ImageFormat::RGBA32323232F) / 8)); + newData.resize(imageData.size() / (ImageFormatDetails::bpp(format) / 8) * (ImageFormatDetails::bpp(ImageFormat::RGBA32323232F) / 8)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA32323232F)}; + + #define VTFPP_CONVERT_DETAIL(InputType, r, g, b, a, ...) \ + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::InputType)}; \ + std::transform(__VA_ARGS__ __VA_OPT__(,) imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::InputType pixel) -> ImagePixel::RGBA32323232F { return {(r), (g), (b), (a)}; }) +#ifdef SOURCEPP_BUILD_WITH_TBB + #define VTFPP_CONVERT(InputType, r, g, b, a) VTFPP_CONVERT_DETAIL(InputType, r, g, b, a, std::execution::unseq) +#else + #define VTFPP_CONVERT(InputType, r, g, b, a) VTFPP_CONVERT_DETAIL(InputType, r, g, b, a) +#endif + #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, r, g, b, a) \ + case InputType: { VTFPP_CONVERT(InputType, r, g, b, a); } break + switch (format) { using enum ImageFormat; - case RGBA32323232F: - newData = {imageData.begin(), imageData.end()}; - break; - case RGB323232F: - for (int i = 0; i < imageData.size(); i += 12) { - for (int j = 0; j < 16; j++) { - newData.push_back({}); - } - *reinterpret_cast(&newData[newData.size() - 16]) = *reinterpret_cast(&imageData[i]); - *reinterpret_cast(&newData[newData.size() - 12]) = *reinterpret_cast(&imageData[i + 4]); - *reinterpret_cast(&newData[newData.size() - 8]) = *reinterpret_cast(&imageData[i + 8]); - *reinterpret_cast(&newData[newData.size() - 4]) = 1.f; - } - break; - case R32F: - for (int i = 0; i < imageData.size(); i += 4) { - for (int j = 0; j < 16; j++) { - newData.push_back({}); - } - *reinterpret_cast(&newData[newData.size() - 16]) = *reinterpret_cast(&imageData[i]); - *reinterpret_cast(&newData[newData.size() - 12]) = 0.f; - *reinterpret_cast(&newData[newData.size() - 8]) = 0.f; - *reinterpret_cast(&newData[newData.size() - 4]) = 1.f; - } - break; - case RGBA16161616F: - for (int i = 0; i < imageData.size(); i += 8) { - for (int j = 0; j < 16; j++) { - newData.push_back({}); - } - *reinterpret_cast(&newData[newData.size() - 16]) = *math::FloatCompressed16{*reinterpret_cast(&imageData[i])}; - *reinterpret_cast(&newData[newData.size() - 12]) = *math::FloatCompressed16{*reinterpret_cast(&imageData[i + 2])}; - *reinterpret_cast(&newData[newData.size() - 8]) = *math::FloatCompressed16{*reinterpret_cast(&imageData[i + 4])}; - *reinterpret_cast(&newData[newData.size() - 4]) = *math::FloatCompressed16{*reinterpret_cast(&imageData[i + 6])}; - } - break; - case RGBA16161616: - for (int i = 0; i < imageData.size(); i += 8) { - for (int j = 0; j < 16; j++) { - newData.push_back({}); - } - *reinterpret_cast(&newData[newData.size() - 16]) = static_cast(*reinterpret_cast(&imageData[i])) / 65535.f; - *reinterpret_cast(&newData[newData.size() - 12]) = static_cast(*reinterpret_cast(&imageData[i + 2])) / 65535.f; - *reinterpret_cast(&newData[newData.size() - 8]) = static_cast(*reinterpret_cast(&imageData[i + 4])) / 65535.f; - *reinterpret_cast(&newData[newData.size() - 4]) = static_cast(*reinterpret_cast(&imageData[i + 6])) / 65535.f; - } - break; - default: - break; + VTFPP_CASE_CONVERT_AND_BREAK(RGB323232F, pixel.r, pixel.g, pixel.b, 1.f); + VTFPP_CASE_CONVERT_AND_BREAK(R32F, pixel.r, pixel.r, pixel.r, 1.f); + VTFPP_CASE_CONVERT_AND_BREAK(RGBA16161616F, pixel.r.toFloat32(), pixel.g.toFloat32(), pixel.b.toFloat32(), pixel.a.toFloat32()); + VTFPP_CASE_CONVERT_AND_BREAK(RGBA16161616, pixel.r / 65535.f, pixel.g / 65535.f, pixel.b / 65535.f, pixel.a / 65535.f); + default: break; } + + #undef VTFPP_CASE_CONVERT_AND_BREAK + #undef VTFPP_CONVERT + #undef VTFPP_CONVERT_DETAIL + return newData; } [[nodiscard]] std::vector convertImageDataFromRGBA32323232F(std::span imageData, ImageFormat format) { + using namespace ImageConversion; + if (imageData.empty()) { return {}; } + if (format == ImageFormat::RGBA32323232F) { + return {imageData.begin(), imageData.end()}; + } + + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA32323232F)}; std::vector newData; - newData.reserve(imageData.size() / (ImageFormatDetails::bpp(ImageFormat::RGBA32323232F) / 8) * (ImageFormatDetails::bpp(format) / 8)); + newData.resize(imageData.size() / (ImageFormatDetails::bpp(ImageFormat::RGBA32323232F) / 8) * (ImageFormatDetails::bpp(format) / 8)); + +#ifdef SOURCEPP_BUILD_WITH_TBB + #define VTFPP_CONVERT(InputType, ...) \ + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::InputType)}; \ + std::transform(std::execution::unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA32323232F pixel) -> ImagePixel::InputType { \ + return __VA_ARGS__; \ + }) +#else + #define VTFPP_CONVERT(InputType, ...) \ + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::InputType)}; \ + std::transform(imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA32323232F pixel) -> ImagePixel::InputType { \ + return __VA_ARGS__; \ + }) +#endif + #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, ...) \ + case InputType: { VTFPP_CONVERT(InputType, __VA_ARGS__); } break + switch (format) { using enum ImageFormat; - case RGBA32323232F: - newData = {imageData.begin(), imageData.end()}; - break; - case RGB323232F: - for (int i = 0; i < imageData.size(); i += 16) { - for (int j = 0; j < 12; j++) { - newData.push_back({}); - } - *reinterpret_cast(&newData[newData.size() - 12]) = *reinterpret_cast(&imageData[i]); - *reinterpret_cast(&newData[newData.size() - 8]) = *reinterpret_cast(&imageData[i + 4]); - *reinterpret_cast(&newData[newData.size() - 4]) = *reinterpret_cast(&imageData[i + 8]); - } - break; - case R32F: - for (int i = 0; i < imageData.size(); i += 16) { - for (int j = 0; j < 4; j++) { - newData.push_back({}); - } - *reinterpret_cast(&newData[newData.size() - 4]) = *reinterpret_cast(&imageData[i]); - } - break; - case RGBA16161616F: - for (int i = 0; i < imageData.size(); i += 16) { - for (int j = 0; j < 8; j++) { - newData.push_back({}); - } - *reinterpret_cast(&newData[newData.size() - 8]) = math::FloatCompressed16{*reinterpret_cast(&imageData[i])}.toFloat16(); - *reinterpret_cast(&newData[newData.size() - 6]) = math::FloatCompressed16{*reinterpret_cast(&imageData[i + 4])}.toFloat16(); - *reinterpret_cast(&newData[newData.size() - 4]) = math::FloatCompressed16{*reinterpret_cast(&imageData[i + 8])}.toFloat16(); - *reinterpret_cast(&newData[newData.size() - 2]) = math::FloatCompressed16{*reinterpret_cast(&imageData[i + 12])}.toFloat16(); - } - break; - case RGBA16161616: - for (int i = 0; i < imageData.size(); i += 16) { - for (int j = 0; j < 8; j++) { - newData.push_back({}); - } - *reinterpret_cast(&newData[newData.size() - 8]) = static_cast(std::clamp(*reinterpret_cast(&imageData[i]), 0.f, 1.f) * 65535.f); - *reinterpret_cast(&newData[newData.size() - 6]) = static_cast(std::clamp(*reinterpret_cast(&imageData[i + 4]), 0.f, 1.f) * 65535.f); - *reinterpret_cast(&newData[newData.size() - 4]) = static_cast(std::clamp(*reinterpret_cast(&imageData[i + 8]), 0.f, 1.f) * 65535.f); - *reinterpret_cast(&newData[newData.size() - 2]) = static_cast(std::clamp(*reinterpret_cast(&imageData[i + 12]), 0.f, 1.f) * 65535.f); - } - break; - default: - break; + VTFPP_CASE_CONVERT_AND_BREAK(RGB323232F, {pixel.r, pixel.g, pixel.b}); + VTFPP_CASE_CONVERT_AND_BREAK(R32F, {pixel.r}); + VTFPP_CASE_CONVERT_AND_BREAK(RGBA16161616F, {math::FloatCompressed16{pixel.r}, math::FloatCompressed16{pixel.g}, math::FloatCompressed16{pixel.b}, math::FloatCompressed16{pixel.a}}); + default: break; } + + #undef VTFPP_CASE_CONVERT_AND_BREAK + #undef VTFPP_CONVERT + return newData; } @@ -677,16 +615,23 @@ namespace { } std::vector newData; - newData.reserve(imageData.size() / (ImageFormatDetails::bpp(ImageFormat::RGBA8888) / 8) * (ImageFormatDetails::bpp(ImageFormat::RGBA32323232F) / 8)); - for (int i = 0; i < imageData.size(); i += 4) { - for (int j = 0; j < 16; j++) { - newData.push_back({}); - } - *reinterpret_cast(&newData[newData.size() - 16]) = static_cast(*reinterpret_cast(&imageData[i])) / 255.f; - *reinterpret_cast(&newData[newData.size() - 12]) = static_cast(*reinterpret_cast(&imageData[i + 1])) / 255.f; - *reinterpret_cast(&newData[newData.size() - 8]) = static_cast(*reinterpret_cast(&imageData[i + 2])) / 255.f; - *reinterpret_cast(&newData[newData.size() - 4]) = static_cast(*reinterpret_cast(&imageData[i + 3])) / 255.f; - } + newData.resize(imageData.size() / (ImageFormatDetails::bpp(ImageFormat::RGBA8888) / 8) * (ImageFormatDetails::bpp(ImageFormat::RGBA32323232F) / 8)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA32323232F)}; + + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA8888)}; \ + std::transform( +#ifdef SOURCEPP_BUILD_WITH_TBB + std::execution::unseq, +#endif + imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA8888 pixel) -> ImagePixel::RGBA32323232F { + return { + static_cast(pixel.r) / static_cast((1 << 8) - 1), + static_cast(pixel.g) / static_cast((1 << 8) - 1), + static_cast(pixel.b) / static_cast((1 << 8) - 1), + static_cast(pixel.a) / static_cast((1 << 8) - 1), + }; + }); + return newData; } @@ -696,20 +641,134 @@ namespace { } std::vector newData; - newData.reserve(imageData.size() / (ImageFormatDetails::bpp(ImageFormat::RGBA32323232F) / 8) * (ImageFormatDetails::bpp(ImageFormat::RGBA8888) / 8)); - for (int i = 0; i < imageData.size(); i += 16) { - newData.push_back(static_cast(std::clamp(*reinterpret_cast(&imageData[i]), 0.f, 1.f) * 255)); - newData.push_back(static_cast(std::clamp(*reinterpret_cast(&imageData[i + 4]), 0.f, 1.f) * 255)); - newData.push_back(static_cast(std::clamp(*reinterpret_cast(&imageData[i + 8]), 0.f, 1.f) * 255)); - newData.push_back(static_cast(std::clamp(*reinterpret_cast(&imageData[i + 12]), 0.f, 1.f) * 255)); + newData.resize(imageData.size() / (ImageFormatDetails::bpp(ImageFormat::RGBA32323232F) / 8) * (ImageFormatDetails::bpp(ImageFormat::RGBA8888) / 8)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA8888)}; + + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA32323232F)}; \ + std::transform( +#ifdef SOURCEPP_BUILD_WITH_TBB + std::execution::unseq, +#endif + imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA32323232F pixel) -> ImagePixel::RGBA8888 { + return { + static_cast(std::clamp(pixel.r, 0.f, 1.f) * ((1 << 8) - 1)), + static_cast(std::clamp(pixel.g, 0.f, 1.f) * ((1 << 8) - 1)), + static_cast(std::clamp(pixel.b, 0.f, 1.f) * ((1 << 8) - 1)), + static_cast(std::clamp(pixel.a, 0.f, 1.f) * ((1 << 8) - 1)), + }; + }); + + return newData; +} + +[[nodiscard]] std::vector convertImageDataFromRGBA8888ToRGBA16161616(std::span imageData) { + if (imageData.empty()) { + return {}; } + + std::vector newData; + newData.resize(imageData.size() / (ImageFormatDetails::bpp(ImageFormat::RGBA8888) / 8) * (ImageFormatDetails::bpp(ImageFormat::RGBA16161616) / 8)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA16161616)}; + + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA8888)}; \ + std::transform( +#ifdef SOURCEPP_BUILD_WITH_TBB + std::execution::unseq, +#endif + imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA8888 pixel) -> ImagePixel::RGBA16161616 { + return { + math::remap(pixel.r, (1 << 8) - 1, (1 << 16) - 1), + math::remap(pixel.g, (1 << 8) - 1, (1 << 16) - 1), + math::remap(pixel.b, (1 << 8) - 1, (1 << 16) - 1), + math::remap(pixel.a, (1 << 8) - 1, (1 << 16) - 1), + }; + }); + + return newData; +} + +[[nodiscard]] std::vector convertImageDataFromRGBA16161616ToRGBA8888(std::span imageData) { + if (imageData.empty()) { + return {}; + } + + std::vector newData; + newData.resize(imageData.size() / (ImageFormatDetails::bpp(ImageFormat::RGBA16161616) / 8) * (ImageFormatDetails::bpp(ImageFormat::RGBA8888) / 8)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA8888)}; + + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA16161616)}; \ + std::transform( +#ifdef SOURCEPP_BUILD_WITH_TBB + std::execution::unseq, +#endif + imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA16161616 pixel) -> ImagePixel::RGBA8888 { + return { + static_cast(math::remap(pixel.r, (1 << 16) - 1, (1 << 8) - 1)), + static_cast(math::remap(pixel.g, (1 << 16) - 1, (1 << 8) - 1)), + static_cast(math::remap(pixel.b, (1 << 16) - 1, (1 << 8) - 1)), + static_cast(math::remap(pixel.a, (1 << 16) - 1, (1 << 8) - 1)), + }; + }); + + return newData; +} + +[[nodiscard]] std::vector convertImageDataFromRGBA32323232FToRGBA16161616(std::span imageData) { + if (imageData.empty()) { + return {}; + } + + std::vector newData; + newData.resize(imageData.size() / (ImageFormatDetails::bpp(ImageFormat::RGBA32323232F) / 8) * (ImageFormatDetails::bpp(ImageFormat::RGBA16161616) / 8)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA16161616)}; + + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA32323232F)}; \ + std::transform( +#ifdef SOURCEPP_BUILD_WITH_TBB + std::execution::unseq, +#endif + imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA32323232F pixel) -> ImagePixel::RGBA16161616 { + return { + static_cast(std::clamp(pixel.r, 0.f, 1.f) * ((1 << 16) - 1)), + static_cast(std::clamp(pixel.g, 0.f, 1.f) * ((1 << 16) - 1)), + static_cast(std::clamp(pixel.b, 0.f, 1.f) * ((1 << 16) - 1)), + static_cast(std::clamp(pixel.a, 0.f, 1.f) * ((1 << 16) - 1)), + }; + }); + + return newData; +} + +[[nodiscard]] std::vector convertImageDataFromRGBA16161616ToRGBA32323232F(std::span imageData) { + if (imageData.empty()) { + return {}; + } + + std::vector newData; + newData.resize(imageData.size() / (ImageFormatDetails::bpp(ImageFormat::RGBA16161616) / 8) * (ImageFormatDetails::bpp(ImageFormat::RGBA32323232F) / 8)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA32323232F)}; + + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA16161616)}; \ + std::transform( +#ifdef SOURCEPP_BUILD_WITH_TBB + std::execution::unseq, +#endif + imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA16161616 pixel) -> ImagePixel::RGBA32323232F { + return { + static_cast(pixel.r) / static_cast((1 << 16) - 1), + static_cast(pixel.g) / static_cast((1 << 16) - 1), + static_cast(pixel.b) / static_cast((1 << 16) - 1), + static_cast(pixel.a) / static_cast((1 << 16) - 1), + }; + }); + return newData; } } // namespace std::vector ImageConversion::convertImageDataToFormat(std::span imageData, ImageFormat oldFormat, ImageFormat newFormat, uint16_t width, uint16_t height) { - if (imageData.empty()) { + if (imageData.empty() || oldFormat == ImageFormat::EMPTY) { return {}; } @@ -717,46 +776,77 @@ std::vector ImageConversion::convertImageDataToFormat(std::span intermediaryData; + std::vector newData; + + ImageFormat intermediaryOldFormat = ImageFormatDetails::containerFormat(oldFormat); if (ImageFormatDetails::compressed(oldFormat)) { - intermediaryFormat = ImageFormat::RGBA8888; - intermediaryData = ::decodeImageDataToRGBA8888(imageData, oldFormat, width, height); - } else if (ImageFormatDetails::large(oldFormat)) { - intermediaryFormat = ImageFormat::RGBA32323232F; - intermediaryData = ::convertImageDataToRGBA32323232F(imageData, oldFormat); - if (!ImageFormatDetails::large(newFormat)) { - intermediaryFormat = ImageFormat::RGBA8888; - intermediaryData = ::convertImageDataFromRGBA32323232FToRGBA8888(intermediaryData); - } + newData = ::convertImageDataUsingCompressonator(imageData, oldFormat, intermediaryOldFormat, width, height); + } else if (intermediaryOldFormat == ImageFormat::RGBA8888) { + newData = ::convertImageDataToRGBA8888(imageData, oldFormat); + } else if (intermediaryOldFormat == ImageFormat::RGBA16161616) { + newData = ::convertImageDataToRGBA16161616(imageData, oldFormat); + } else if (intermediaryOldFormat == ImageFormat::RGBA32323232F) { + newData = ::convertImageDataToRGBA32323232F(imageData, oldFormat); } else { - intermediaryFormat = ImageFormat::RGBA8888; - intermediaryData = ::convertImageDataToRGBA8888(imageData, oldFormat); - if (ImageFormatDetails::large(newFormat)) { - intermediaryFormat = ImageFormat::RGBA32323232F; - intermediaryData = ::convertImageDataFromRGBA8888ToRGBA32323232F(intermediaryData); - } + return {}; } - if (intermediaryFormat == newFormat) { - return intermediaryData; + if (intermediaryOldFormat == newFormat) { + return newData; } - std::vector finalData; - if (intermediaryFormat == ImageFormat::RGBA8888) { - if (ImageFormatDetails::compressed(newFormat)) { - finalData = ::encodeImageDataFromRGBA8888(intermediaryData, newFormat, width, height); + ImageFormat intermediaryNewFormat = ImageFormatDetails::containerFormat(newFormat); + if (intermediaryOldFormat != intermediaryNewFormat) { + if (intermediaryOldFormat == ImageFormat::RGBA8888) { + if (intermediaryNewFormat == ImageFormat::RGBA16161616) { + newData = ::convertImageDataFromRGBA8888ToRGBA16161616(newData); + } else if (intermediaryNewFormat == ImageFormat::RGBA32323232F) { + newData = ::convertImageDataFromRGBA8888ToRGBA32323232F(newData); + } else { + return {}; + } + } else if (intermediaryOldFormat == ImageFormat::RGBA16161616) { + if (intermediaryNewFormat == ImageFormat::RGBA8888) { + newData = ::convertImageDataFromRGBA16161616ToRGBA8888(newData); + } else if (intermediaryNewFormat == ImageFormat::RGBA32323232F) { + newData = ::convertImageDataFromRGBA16161616ToRGBA32323232F(newData); + } else { + return {}; + } + } else if (intermediaryOldFormat == ImageFormat::RGBA32323232F) { + if (intermediaryNewFormat == ImageFormat::RGBA8888) { + newData = ::convertImageDataFromRGBA32323232FToRGBA8888(newData); + } else if (intermediaryNewFormat == ImageFormat::RGBA16161616) { + newData = ::convertImageDataFromRGBA32323232FToRGBA16161616(newData); + } else { + return {}; + } } else { - finalData = ::convertImageDataFromRGBA8888(intermediaryData, newFormat); + return {}; } + } + + if (intermediaryNewFormat == newFormat) { + return newData; + } + + if (ImageFormatDetails::compressed(newFormat)) { + newData = ::convertImageDataUsingCompressonator(newData, intermediaryNewFormat, newFormat, width, height); + } else if (intermediaryNewFormat == ImageFormat::RGBA8888) { + newData = ::convertImageDataFromRGBA8888(newData, newFormat); + } else if (intermediaryNewFormat == ImageFormat::RGBA16161616) { + newData = ::convertImageDataFromRGBA16161616(newData, newFormat); + } else if (intermediaryNewFormat == ImageFormat::RGBA32323232F) { + newData = ::convertImageDataFromRGBA32323232F(newData, newFormat); } else { - finalData = ::convertImageDataFromRGBA32323232F(intermediaryData, newFormat); + return {}; } - return finalData; + + return newData; } std::vector ImageConversion::convertSeveralImageDataToFormat(std::span imageData, ImageFormat oldFormat, ImageFormat newFormat, uint8_t mipCount, uint16_t frameCount, uint16_t faceCount, uint16_t width, uint16_t height, uint16_t sliceCount) { - if (imageData.empty()) { + if (imageData.empty() || oldFormat == ImageFormat::EMPTY) { return {}; } @@ -784,21 +874,20 @@ std::vector ImageConversion::convertSeveralImageDataToFormat(std::spa } std::vector ImageConversion::convertImageDataToFile(std::span imageData, ImageFormat format, uint16_t width, uint16_t height) { - if (imageData.empty()) { + if (imageData.empty() || format == ImageFormat::EMPTY) { return {}; } std::vector out; auto stbWriteFunc = [](void* out, void* data, int size) { std::copy(reinterpret_cast(data), reinterpret_cast(data) + size, std::back_inserter(*reinterpret_cast*>(out))); }; - if (format == ImageFormat::RGBA16161616) { - // Larger than RGBA8888! - stbi_write_png_to_func(stbWriteFunc, &out, width, height, ImageFormatDetails::bpp(ImageFormat::RGBA16161616) / 8, imageData.data(), 0); - } else if (ImageFormatDetails::large(format)) { + if (ImageFormatDetails::decimal(format)) { auto hdr = convertImageDataToFormat(imageData, format, ImageFormat::RGBA32323232F, width, height); stbi_write_hdr_to_func(stbWriteFunc, &out, width, height, ImageFormatDetails::bpp(ImageFormat::RGBA32323232F) / 8, reinterpret_cast(hdr.data())); + } else if (ImageFormatDetails::large(format)) { + auto rgba = convertImageDataToFormat(imageData, format, ImageFormat::RGBA16161616, width, height); + stbi_write_png_to_func(stbWriteFunc, &out, width, height, ImageFormatDetails::bpp(ImageFormat::RGBA16161616) / 8, rgba.data(), 0); } else { - // This works for compressed formats too auto rgba = convertImageDataToFormat(imageData, format, ImageFormat::RGBA8888, width, height); stbi_write_png_to_func(stbWriteFunc, &out, width, height, ImageFormatDetails::bpp(ImageFormat::RGBA8888) / 8, rgba.data(), 0); } @@ -821,7 +910,7 @@ void ImageConversion::setResizedDims(uint16_t& width, ResizeMethod widthResize, } std::vector ImageConversion::resizeImageData(std::span imageData, ImageFormat format, uint16_t width, uint16_t newWidth, uint16_t height, uint16_t newHeight, bool srgb, ResizeFilter filter, ResizeEdge edge) { - if (imageData.empty()) { + if (imageData.empty() || format == ImageFormat::EMPTY) { return {}; } const auto pixelLayout = ::imageFormatToSTBIRPixelLayout(format); @@ -837,7 +926,7 @@ std::vector ImageConversion::resizeImageData(std::span ImageConversion::resizeImageDataStrict(std::span imageData, ImageFormat format, uint16_t width, uint16_t newWidth, uint16_t& widthOut, ResizeMethod widthResize, uint16_t height, uint16_t newHeight, uint16_t& heightOut, ResizeMethod heightResize, bool srgb, ResizeFilter filter, ResizeEdge edge) { - if (imageData.empty()) { + if (imageData.empty() || format == ImageFormat::EMPTY) { return {}; } widthOut = getResizedDim(newWidth, widthResize); diff --git a/src/vtfpp/vtfpp.cpp b/src/vtfpp/VTF.cpp similarity index 99% rename from src/vtfpp/vtfpp.cpp rename to src/vtfpp/VTF.cpp index bab8c4b1d..6f1f38da5 100644 --- a/src/vtfpp/vtfpp.cpp +++ b/src/vtfpp/VTF.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -19,7 +19,7 @@ std::vector compressData(std::span data, int level) mz_ulong compressedSize = mz_compressBound(data.size()); std::vector out(compressedSize); - int status; + int status = MZ_OK; while ((status = mz_compress2(reinterpret_cast(out.data()), &compressedSize, reinterpret_cast(data.data()), data.size(), level)) == MZ_BUF_ERROR) { compressedSize *= 2; out.resize(compressedSize); @@ -1236,6 +1236,6 @@ std::vector VTF::bake() { return out; } -void VTF::bake(const std::string& vtfPath) { - fs::writeFileBuffer(vtfPath, this->bake()); +bool VTF::bake(const std::string& vtfPath) { + return fs::writeFileBuffer(vtfPath, this->bake()); } diff --git a/src/vtfpp/_vtfpp.cmake b/src/vtfpp/_vtfpp.cmake index 261f3b760..79a688612 100644 --- a/src/vtfpp/_vtfpp.cmake +++ b/src/vtfpp/_vtfpp.cmake @@ -3,9 +3,11 @@ add_pretty_parser(vtfpp PRECOMPILED_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/ImageConversion.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/ImageFormats.h" + "${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/VTF.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/vtfpp.h" SOURCES "${CMAKE_CURRENT_LIST_DIR}/ImageConversion.cpp" - "${CMAKE_CURRENT_LIST_DIR}/vtfpp.cpp") + "${CMAKE_CURRENT_LIST_DIR}/VTF.cpp") +sourcepp_add_tbb(vtfpp) target_link_compressonator(vtfpp) diff --git a/test/bench/vtfpp.cpp b/test/bench/vtfpp.cpp index 38ce9bf80..af70b426b 100644 --- a/test/bench/vtfpp.cpp +++ b/test/bench/vtfpp.cpp @@ -2,7 +2,6 @@ #include #include -#include #include using namespace sourcepp; diff --git a/test/res/vtfpp/src_non_po2.png b/test/res/vtfpp/src/non_po2.png similarity index 100% rename from test/res/vtfpp/src_non_po2.png rename to test/res/vtfpp/src/non_po2.png diff --git a/test/res/vtfpp/src.png b/test/res/vtfpp/src/po2.png similarity index 100% rename from test/res/vtfpp/src.png rename to test/res/vtfpp/src/po2.png diff --git a/test/vtfpp.cpp b/test/vtfpp.cpp index 96ddc5be6..f6cc76708 100644 --- a/test/vtfpp.cpp +++ b/test/vtfpp.cpp @@ -50,7 +50,7 @@ TEST_WRITE_FMT(UVLX8888, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FL #endif TEST(vtfpp, write_non_po2) { - VTF vtf = VTF::create(ASSET_ROOT "vtfpp/src_non_po2.png", { + VTF vtf = VTF::create(ASSET_ROOT "vtfpp/src/non_po2.png", { .widthResizeMethod = ImageConversion::ResizeMethod::NONE, .heightResizeMethod = ImageConversion::ResizeMethod::NONE, }); @@ -116,7 +116,7 @@ TEST(vtfpp, read_v70) { } TEST(vtfpp, write_v70) { - VTF vtf = VTF::create(ASSET_ROOT "vtfpp/src.png", { + VTF vtf = VTF::create(ASSET_ROOT "vtfpp/src/po2.png", { .minorVersion = 0, }); ASSERT_TRUE(vtf); @@ -291,7 +291,7 @@ TEST(vtfpp, read_v71) { } TEST(vtfpp, write_v71) { - VTF vtf = VTF::create(ASSET_ROOT "vtfpp/src.png", { + VTF vtf = VTF::create(ASSET_ROOT "vtfpp/src/po2.png", { .minorVersion = 1, }); ASSERT_TRUE(vtf); @@ -466,7 +466,7 @@ TEST(vtfpp, read_v72) { } TEST(vtfpp, write_v72) { - VTF vtf = VTF::create(ASSET_ROOT "vtfpp/src.png", { + VTF vtf = VTF::create(ASSET_ROOT "vtfpp/src/po2.png", { .minorVersion = 2, }); ASSERT_TRUE(vtf); @@ -654,7 +654,7 @@ TEST(vtfpp, read_v75) { } TEST(vtfpp, write_v75) { - VTF vtf = VTF::create(ASSET_ROOT "vtfpp/src.png", { + VTF vtf = VTF::create(ASSET_ROOT "vtfpp/src/po2.png", { .minorVersion = 5, }); ASSERT_TRUE(vtf); @@ -830,7 +830,7 @@ TEST(vtfpp, read_v76_c9) { } TEST(vtfpp, write_v76_c6) { - VTF vtf = VTF::create(ASSET_ROOT "vtfpp/src.png", { + VTF vtf = VTF::create(ASSET_ROOT "vtfpp/src/po2.png", { .minorVersion = 6, }); ASSERT_TRUE(vtf); From d575147106d085e862f5f7d8e71ed8878bfdac28 Mon Sep 17 00:00:00 2001 From: craftablescience Date: Tue, 10 Sep 2024 13:33:46 -0400 Subject: [PATCH 4/5] fix(toolpp): in FGDWriter return bool from bake on successful file write --- include/toolpp/FGD.h | 2 +- src/toolpp/FGD.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/toolpp/FGD.h b/include/toolpp/FGD.h index ac39ecb68..0ee139ad2 100644 --- a/include/toolpp/FGD.h +++ b/include/toolpp/FGD.h @@ -191,7 +191,7 @@ class FGDWriter { [[nodiscard]] std::string bake(); - void bake(const std::string& fgdPath); + bool bake(const std::string& fgdPath); protected: FGDWriter(); diff --git a/src/toolpp/FGD.cpp b/src/toolpp/FGD.cpp index 3d2a3e098..594c5029a 100644 --- a/src/toolpp/FGD.cpp +++ b/src/toolpp/FGD.cpp @@ -837,6 +837,6 @@ std::string FGDWriter::bake() { return this->backingData; } -void FGDWriter::bake(const std::string& fgdPath) { - fs::writeFileText(fgdPath, this->bake()); +bool FGDWriter::bake(const std::string& fgdPath) { + return fs::writeFileText(fgdPath, this->bake()); } From 73dff009cd892e4f3307faff3036651729e278af Mon Sep 17 00:00:00 2001 From: craftablescience Date: Tue, 10 Sep 2024 13:34:29 -0400 Subject: [PATCH 5/5] feat(vpkpp): add an iterator over entries in a particular directory --- include/vpkpp/PackFile.h | 5 ++++ src/vpkpp/PackFile.cpp | 58 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/include/vpkpp/PackFile.h b/include/vpkpp/PackFile.h index c104b832f..c2e7c449a 100644 --- a/include/vpkpp/PackFile.h +++ b/include/vpkpp/PackFile.h @@ -154,6 +154,9 @@ class PackFile { /// Run a callback for each entry in the pack file void runForAllEntries(const EntryCallback& operation, bool includeUnbaked = true) const; + /// Run a callback for each entry in the pack file under the parent directory + void runForAllEntries(const std::string& parentDir, const EntryCallback& operation, bool recursive = true, bool includeUnbaked = true) const; + /// /home/user/pak01_dir.vpk [[nodiscard]] std::string_view getFilepath() const; @@ -186,6 +189,8 @@ class PackFile { void runForAllEntriesInternal(const std::function& operation, bool includeUnbaked = true); + void runForAllEntriesInternal(const std::string& parentDir, const std::function& operation, bool recursive = true, bool includeUnbaked = true); + [[nodiscard]] std::vector verifyEntryChecksumsUsingCRC32() const; virtual void addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options) = 0; diff --git a/src/vpkpp/PackFile.cpp b/src/vpkpp/PackFile.cpp index 4fd66259b..a93c23109 100644 --- a/src/vpkpp/PackFile.cpp +++ b/src/vpkpp/PackFile.cpp @@ -413,7 +413,7 @@ bool PackFile::extractAll(const std::string& outputDir, bool createUnderPackFile } bool noneFailed = true; this->runForAllEntries([this, &outputDirPath, &noneFailed](const std::string& path, const Entry& entry) { - std::string entryPath = path; + std::string entryPath = path; // NOLINT(*-unnecessary-copy-initialization) #ifdef _WIN32 ::fixFilePathForWindows(entryPath); #endif @@ -520,6 +520,34 @@ void PackFile::runForAllEntries(const EntryCallback& operation, bool includeUnba } } +void PackFile::runForAllEntries(const std::string& parentDir, const EntryCallback& operation, bool recursive, bool includeUnbaked) const { + auto dir = this->cleanEntryPath(parentDir) + '/'; + + std::string key; + for (auto [entry, end] = this->entries.equal_prefix_range(dir); entry != end; ++entry) { + entry.key(key); + if (!recursive) { + auto keyView = std::string_view{key}.substr(dir.length()); + if (std::find(keyView.begin(), keyView.end(), '/') != keyView.end()) { + continue; + } + } + operation(key, entry.value()); + } + if (includeUnbaked) { + for (auto [entry, end] = this->unbakedEntries.equal_prefix_range(dir); entry != end; ++entry) { + entry.key(key); + if (!recursive) { + auto keyView = std::string_view{key}.substr(dir.length()); + if (std::find(keyView.begin(), keyView.end(), '/') != keyView.end()) { + continue; + } + } + operation(key, entry.value()); + } + } +} + void PackFile::runForAllEntriesInternal(const std::function& operation, bool includeUnbaked) { std::string key; for (auto entry = this->entries.begin(); entry != this->entries.end(); ++entry) { @@ -534,6 +562,34 @@ void PackFile::runForAllEntriesInternal(const std::function& operation, bool recursive, bool includeUnbaked) { + auto dir = this->cleanEntryPath(parentDir) + '/'; + + std::string key; + for (auto [entry, end] = this->entries.equal_prefix_range(dir); entry != end; ++entry) { + entry.key(key); + if (!recursive) { + auto keyView = std::string_view{key}.substr(dir.length()); + if (std::find(keyView.begin(), keyView.end(), '/') != keyView.end()) { + continue; + } + } + operation(key, entry.value()); + } + if (includeUnbaked) { + for (auto [entry, end] = this->unbakedEntries.equal_prefix_range(dir); entry != end; ++entry) { + entry.key(key); + if (!recursive) { + auto keyView = std::string_view{key}.substr(dir.length()); + if (std::find(keyView.begin(), keyView.end(), '/') != keyView.end()) { + continue; + } + } + operation(key, entry.value()); + } + } +} + std::string_view PackFile::getFilepath() const { return this->fullFilePath; }