From 68905dcaca7a9e931c6270c1d01da9ae9acf54d3 Mon Sep 17 00:00:00 2001 From: yuuko Date: Mon, 8 Sep 2025 17:58:54 -0700 Subject: [PATCH 01/32] vtfpp: maintain byte order of underlying buffers --- include/vtfpp/ImageConversion.h | 78 ++++++++++++++++++++++++--------- src/vtfpp/ImageConversion.cpp | 16 +++---- src/vtfpp/VTF.cpp | 4 +- 3 files changed, 69 insertions(+), 29 deletions(-) diff --git a/include/vtfpp/ImageConversion.h b/include/vtfpp/ImageConversion.h index fea6cb3a1..9a3a4fd49 100644 --- a/include/vtfpp/ImageConversion.h +++ b/include/vtfpp/ImageConversion.h @@ -5,14 +5,52 @@ #include #include #include +#include #include #include +#include + +using sourcepp::math::Arithmetic; #include "ImageFormats.h" namespace vtfpp { +template +class LERep : public std::array { + using uint_according = typename std::conditional::type>::type>::type; +public: + constexpr operator A() { + uint_according ret = 0; + for (size_t offs = 0; auto &b : *this) { + ret |= (static_cast(b) << offs) & (uint_according(0xFFu) << offs); + offs += 8; + } + return *reinterpret_cast(&ret); + } + constexpr LERep &operator=(const A &v) { + auto in = *reinterpret_cast(&v); + for (size_t offs = 0; auto &b : *this) { + b = static_cast((in >> offs) & uint_according(0xFFu)); + offs += 8; + } + return *this; + } + template requires (std::convertible_to || std::is_same_v) + constexpr LERep(const B &u) { *this = static_cast(u); } + template requires std::convertible_to + constexpr LERep(const LERep &u) { *this = static_cast(u); } + template requires std::convertible_to + constexpr operator B() const { return static_cast(static_cast(*this)); } +}; + namespace ImagePixel { #define VTFPP_CHECK_SIZE(format) \ @@ -157,18 +195,18 @@ struct UVWQ8888 { struct RGBA16161616F { static constexpr auto FORMAT = ImageFormat::RGBA16161616F; - half r; - half g; - half b; - half a; + LERep r; + LERep g; + LERep b; + LERep a; }; VTFPP_CHECK_SIZE(RGBA16161616F); struct RGBA16161616 { static constexpr auto FORMAT = ImageFormat::RGBA16161616; - uint16_t r; - uint16_t g; - uint16_t b; - uint16_t a; + LERep r; + LERep g; + LERep b; + LERep a; }; VTFPP_CHECK_SIZE(RGBA16161616); struct UVLX8888 { @@ -181,34 +219,34 @@ struct UVLX8888 { struct R32F { static constexpr auto FORMAT = ImageFormat::R32F; - float r; + LERep r; }; VTFPP_CHECK_SIZE(R32F); struct RGB323232F { static constexpr auto FORMAT = ImageFormat::R32F; - float r; - float g; - float b; + LERep r; + LERep g; + LERep b; }; VTFPP_CHECK_SIZE(RGB323232F); struct RGBA32323232F { static constexpr auto FORMAT = ImageFormat::RGBA32323232F; - float r; - float g; - float b; - float a; + LERep r; + LERep g; + LERep b; + LERep a; }; VTFPP_CHECK_SIZE(RGBA32323232F); struct RG1616F { static constexpr auto FORMAT = ImageFormat::RG1616F; - half r; - half g; + LERep r; + LERep g; }; VTFPP_CHECK_SIZE(RG1616F); struct RG3232F { static constexpr auto FORMAT = ImageFormat::RG3232F; - float r; - float g; + LERep r; + LERep g; }; VTFPP_CHECK_SIZE(RG3232F); struct RGBX8888 { diff --git a/src/vtfpp/ImageConversion.cpp b/src/vtfpp/ImageConversion.cpp index a666a759b..bc9f1945a 100644 --- a/src/vtfpp/ImageConversion.cpp +++ b/src/vtfpp/ImageConversion.cpp @@ -730,10 +730,10 @@ namespace { #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)), + 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)), }; }); @@ -808,10 +808,10 @@ namespace { #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)), + 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)), }; }); diff --git a/src/vtfpp/VTF.cpp b/src/vtfpp/VTF.cpp index bc6a6925c..3d1a71f9c 100644 --- a/src/vtfpp/VTF.cpp +++ b/src/vtfpp/VTF.cpp @@ -1681,7 +1681,9 @@ std::vector VTF::bake() const { .write(this->frameCount) .write(this->startFrame) .write(0) // padding - .write(this->reflectivity) + .write(this->reflectivity[0]) + .write(this->reflectivity[1]) + .write(this->reflectivity[2]) .write(0) // padding .write(this->bumpMapScale) .write(bakeFormat) From 07aaa98f45378605adcde5537a4c54e6b29d73a2 Mon Sep 17 00:00:00 2001 From: yuuko Date: Wed, 10 Sep 2025 14:21:49 -0700 Subject: [PATCH 02/32] vtfpp: ImageConversion: disambiguate LERep conversions --- include/vtfpp/ImageConversion.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/vtfpp/ImageConversion.h b/include/vtfpp/ImageConversion.h index 9a3a4fd49..169fb575b 100644 --- a/include/vtfpp/ImageConversion.h +++ b/include/vtfpp/ImageConversion.h @@ -27,7 +27,7 @@ class LERep : public std::array { uint16_t, uint8_t>::type>::type>::type; public: - constexpr operator A() { + constexpr operator A() const { uint_according ret = 0; for (size_t offs = 0; auto &b : *this) { ret |= (static_cast(b) << offs) & (uint_according(0xFFu) << offs); @@ -44,11 +44,13 @@ class LERep : public std::array { return *this; } template requires (std::convertible_to || std::is_same_v) - constexpr LERep(const B &u) { *this = static_cast(u); } + constexpr LERep(const B &u) { this->operator=(static_cast(u)); } + template requires std::convertible_to - constexpr LERep(const LERep &u) { *this = static_cast(u); } + constexpr LERep(const LERep &u) { this->operator=(u.operator A()); } + template requires std::convertible_to - constexpr operator B() const { return static_cast(static_cast(*this)); } + constexpr operator B() const { return static_cast(this->operator A()); } }; namespace ImagePixel { From 91bcd67101445a35f945d087aad736dcbea31152 Mon Sep 17 00:00:00 2001 From: yuuko Date: Thu, 11 Sep 2025 15:59:34 -0700 Subject: [PATCH 03/32] sourcepp: adopt LERep from vtfpp --- include/sourcepp/Bits.h | 45 +++++++++++++++++++++++++++++++++ include/vtfpp/ImageConversion.h | 43 +++---------------------------- 2 files changed, 48 insertions(+), 40 deletions(-) create mode 100644 include/sourcepp/Bits.h diff --git a/include/sourcepp/Bits.h b/include/sourcepp/Bits.h new file mode 100644 index 000000000..353bd6a20 --- /dev/null +++ b/include/sourcepp/Bits.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +#include + +namespace sourcepp::bits { + +template +class LERep : public std::array { + using uint_according = typename std::conditional::type>::type>::type; +public: + constexpr operator A() const { + uint_according ret = 0; + for (size_t offs = 0; auto &b : *this) { + ret |= (static_cast(b) << offs) & (uint_according(0xFFu) << offs); + offs += 8; + } + return *reinterpret_cast(&ret); + } + constexpr LERep &operator=(const A &v) { + auto in = *reinterpret_cast(&v); + for (size_t offs = 0; auto &b : *this) { + b = static_cast((in >> offs) & uint_according(0xFFu)); + offs += 8; + } + return *this; + } + template requires (std::convertible_to || std::is_same_v) + constexpr LERep(const B &u) { this->operator=(static_cast(u)); } + + template requires std::convertible_to + constexpr LERep(const LERep &u) { this->operator=(u.operator A()); } + + template requires std::convertible_to + constexpr operator B() const { return static_cast(this->operator A()); } +}; + +} // namespace sourcepp::bits diff --git a/include/vtfpp/ImageConversion.h b/include/vtfpp/ImageConversion.h index 169fb575b..cfff115f4 100644 --- a/include/vtfpp/ImageConversion.h +++ b/include/vtfpp/ImageConversion.h @@ -5,53 +5,16 @@ #include #include #include -#include #include +#include #include -#include - -using sourcepp::math::Arithmetic; #include "ImageFormats.h" -namespace vtfpp { - -template -class LERep : public std::array { - using uint_according = typename std::conditional::type>::type>::type; -public: - constexpr operator A() const { - uint_according ret = 0; - for (size_t offs = 0; auto &b : *this) { - ret |= (static_cast(b) << offs) & (uint_according(0xFFu) << offs); - offs += 8; - } - return *reinterpret_cast(&ret); - } - constexpr LERep &operator=(const A &v) { - auto in = *reinterpret_cast(&v); - for (size_t offs = 0; auto &b : *this) { - b = static_cast((in >> offs) & uint_according(0xFFu)); - offs += 8; - } - return *this; - } - template requires (std::convertible_to || std::is_same_v) - constexpr LERep(const B &u) { this->operator=(static_cast(u)); } - - template requires std::convertible_to - constexpr LERep(const LERep &u) { this->operator=(u.operator A()); } +using sourcepp::bits::LERep; - template requires std::convertible_to - constexpr operator B() const { return static_cast(this->operator A()); } -}; +namespace vtfpp { namespace ImagePixel { From 986a5aa5962a464f8a00e0885060193c9892881d Mon Sep 17 00:00:00 2001 From: yuuko Date: Thu, 11 Sep 2025 17:41:27 -0700 Subject: [PATCH 04/32] sourcepp: Bits: ergonomics+docs --- include/sourcepp/Bits.h | 33 +++++++++++++++++++++++++ include/vtfpp/ImageConversion.h | 44 +++++++++++++++++---------------- 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/include/sourcepp/Bits.h b/include/sourcepp/Bits.h index 353bd6a20..5baae6bc1 100644 --- a/include/sourcepp/Bits.h +++ b/include/sourcepp/Bits.h @@ -6,6 +6,13 @@ namespace sourcepp::bits { +/// A view of a given numeric type `A` that is always little-endian in memory. +/// Typically becomes no-op at `-O2` on LE targets. +/// +/// Any instance of `*reinterpret_cast` etc., or use of numeric types +/// in structures destined for I/O buffers is suspect, and, in either case, should likely +/// be replaced by some invocation of this class. This can be syntactically cumbersome, +/// so convenient aliases are provided with f32le, etc. template class LERep : public std::array { using uint_according = typename std::conditional { uint16_t, uint8_t>::type>::type>::type; public: + /// Conversion to native byte order. + /// `LERep` ⇒ `A`. constexpr operator A() const { uint_according ret = 0; for (size_t offs = 0; auto &b : *this) { @@ -24,6 +33,9 @@ class LERep : public std::array { } return *reinterpret_cast(&ret); } + + /// Conversion from native byte order. + /// `A` ⇒ `LERep`. constexpr LERep &operator=(const A &v) { auto in = *reinterpret_cast(&v); for (size_t offs = 0; auto &b : *this) { @@ -32,14 +44,35 @@ class LERep : public std::array { } return *this; } + + /// Convenience for arithmetic conversion from some B. + /// (`B` ⇒ `A`) ⇒ (`B` ⇒ `LERep`). template requires (std::convertible_to || std::is_same_v) constexpr LERep(const B &u) { this->operator=(static_cast(u)); } + /// Convenience for arithmetic conversion between LERep of distinct but arithmetically convertible types. + /// (`B` ⇒ `A`) ⇒ (`LERep` ⇒ `LERep`). template requires std::convertible_to constexpr LERep(const LERep &u) { this->operator=(u.operator A()); } + /// Convenience for arithmetic conversion to some B. + /// (`LERep` ⇒ `A`) ∧ (`A` ⇒ `B`) ⇒ (`LERep` ⇒ `B`). template requires std::convertible_to constexpr operator B() const { return static_cast(this->operator A()); } }; +#define SOURCEPP_LEREP_DEFINE(N, V)\ + using N##le = LERep +#define SOURCEPP_LEREP_DEFINE_P(PFX, N16, N32, N64) \ + SOURCEPP_LEREP_DEFINE(PFX##16, N16); \ + SOURCEPP_LEREP_DEFINE(PFX##32, N32); \ + SOURCEPP_LEREP_DEFINE(PFX##64, N64) + +SOURCEPP_LEREP_DEFINE_P(i, int16_t, int32_t, int64_t); +SOURCEPP_LEREP_DEFINE_P(ui, uint16_t, uint32_t, uint64_t); +SOURCEPP_LEREP_DEFINE_P(f, half, float, double); + +#undef SOURCEPP_LEREP_DEFINE_P +#undef SOURCEPP_LEREP_DEFINE + } // namespace sourcepp::bits diff --git a/include/vtfpp/ImageConversion.h b/include/vtfpp/ImageConversion.h index cfff115f4..5d069bce0 100644 --- a/include/vtfpp/ImageConversion.h +++ b/include/vtfpp/ImageConversion.h @@ -12,7 +12,9 @@ #include "ImageFormats.h" -using sourcepp::bits::LERep; +using sourcepp::bits::f16le; +using sourcepp::bits::f32le; +using sourcepp::bits::ui16le; namespace vtfpp { @@ -160,18 +162,18 @@ struct UVWQ8888 { struct RGBA16161616F { static constexpr auto FORMAT = ImageFormat::RGBA16161616F; - LERep r; - LERep g; - LERep b; - LERep a; + f16le r; + f16le g; + f16le b; + f16le a; }; VTFPP_CHECK_SIZE(RGBA16161616F); struct RGBA16161616 { static constexpr auto FORMAT = ImageFormat::RGBA16161616; - LERep r; - LERep g; - LERep b; - LERep a; + ui16le r; + ui16le g; + ui16le b; + ui16le a; }; VTFPP_CHECK_SIZE(RGBA16161616); struct UVLX8888 { @@ -184,34 +186,34 @@ struct UVLX8888 { struct R32F { static constexpr auto FORMAT = ImageFormat::R32F; - LERep r; + f32le r; }; VTFPP_CHECK_SIZE(R32F); struct RGB323232F { static constexpr auto FORMAT = ImageFormat::R32F; - LERep r; - LERep g; - LERep b; + f32le r; + f32le g; + f32le b; }; VTFPP_CHECK_SIZE(RGB323232F); struct RGBA32323232F { static constexpr auto FORMAT = ImageFormat::RGBA32323232F; - LERep r; - LERep g; - LERep b; - LERep a; + f32le r; + f32le g; + f32le b; + f32le a; }; VTFPP_CHECK_SIZE(RGBA32323232F); struct RG1616F { static constexpr auto FORMAT = ImageFormat::RG1616F; - LERep r; - LERep g; + f16le r; + f16le g; }; VTFPP_CHECK_SIZE(RG1616F); struct RG3232F { static constexpr auto FORMAT = ImageFormat::RG3232F; - LERep r; - LERep g; + f32le r; + f32le g; }; VTFPP_CHECK_SIZE(RG3232F); struct RGBX8888 { From ae0e6195478d91e5d69d8bc785af3c1eb39727e5 Mon Sep 17 00:00:00 2001 From: yuuko Date: Thu, 11 Sep 2025 17:42:35 -0700 Subject: [PATCH 05/32] docs: let doxygen know about some alias-declaring macros --- Doxyfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doxyfile b/Doxyfile index e8f283383..8e652cae7 100644 --- a/Doxyfile +++ b/Doxyfile @@ -2471,6 +2471,10 @@ PREDEFINED = # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. EXPAND_AS_DEFINED = +EXPAND_AS_DEFINED += SOURCEPP_LEREP_DEFINE +EXPAND_AS_DEFINED += SOURCEPP_LEREP_DEFINE_P +EXPAND_AS_DEFINED += SOURCEPP_MAT_DEFINE +EXPAND_AS_DEFINED += SOURCEPP_VEC_DEFINE # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will # remove all references to function-like macros that are alone on a line, have From 798731ea67e3b19039b408573ef85f1ff6e314a1 Mon Sep 17 00:00:00 2001 From: yuuko Date: Thu, 11 Sep 2025 17:45:21 -0700 Subject: [PATCH 06/32] feat(ext): allow exotic architectures to provide their own compressonator builds on linux --- ext/compressonator/CMakeLists.txt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ext/compressonator/CMakeLists.txt b/ext/compressonator/CMakeLists.txt index 22cc64f15..092bb3843 100644 --- a/ext/compressonator/CMakeLists.txt +++ b/ext/compressonator/CMakeLists.txt @@ -26,11 +26,14 @@ function(target_link_compressonator TARGET) "${COMPRESSONATOR_DIR}/lib/macOS_arm64/libCMP_Core$<$:d>.a") elseif(UNIX) target_link_libraries(${TARGET} PRIVATE - "${COMPRESSONATOR_DIR}/lib/linux_x86_64/libCompressonator$<$:d>.a" - "${COMPRESSONATOR_DIR}/lib/linux_x86_64/libCMP_Core$<$:d>.a" - "${COMPRESSONATOR_DIR}/lib/linux_x86_64/libCMP_Core_AVX$<$:d>.a" - "${COMPRESSONATOR_DIR}/lib/linux_x86_64/libCMP_Core_AVX512$<$:d>.a" - "${COMPRESSONATOR_DIR}/lib/linux_x86_64/libCMP_Core_SSE$<$:d>.a") + "${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCompressonator$<$:d>.a" + "${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCMP_Core$<$:d>.a") + if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + target_link_libraries(${TARGET} PRIVATE + "${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCMP_Core_AVX$<$:d>.a" + "${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCMP_Core_AVX512$<$:d>.a" + "${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCMP_Core_SSE$<$:d>.a") + endif() else() message(FATAL_ERROR "Unable to link to Compressonator library!") endif() From 7ab8776c32b06b6e684788467c6a373a4c9f8405 Mon Sep 17 00:00:00 2001 From: yuuko Date: Thu, 11 Sep 2025 18:10:08 -0700 Subject: [PATCH 07/32] sourcepp: more LERep constructors + convenient read --- include/sourcepp/Bits.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/sourcepp/Bits.h b/include/sourcepp/Bits.h index 5baae6bc1..08e72babd 100644 --- a/include/sourcepp/Bits.h +++ b/include/sourcepp/Bits.h @@ -13,6 +13,8 @@ namespace sourcepp::bits { /// in structures destined for I/O buffers is suspect, and, in either case, should likely /// be replaced by some invocation of this class. This can be syntactically cumbersome, /// so convenient aliases are provided with f32le, etc. +/// +/// Special provisions for arithmetic outside possible implicit conversions are intentionally omitted. template class LERep : public std::array { using uint_according = typename std::conditional { /// (`LERep` ⇒ `A`) ∧ (`A` ⇒ `B`) ⇒ (`LERep` ⇒ `B`). template requires std::convertible_to constexpr operator B() const { return static_cast(this->operator A()); } + + /// Permits uninitialized LERep, for parity with the associated A. + constexpr LERep() {} + + /// Construct a value from a byte buffer. + constexpr explicit LERep(std::span arr) { + std::memcpy(this, arr.data(), sizeof(A)); + } }; +/// Read a given value of type A from a pointer into arbitrary data presumed to hold a little-endian representation of A. +/// Subject to the usual litany of cautions about +/// +/// *reinterpret_cast(p) +/// +/// , such invocations, where spuriously dependent upon host byte order, may be replaced with: +/// +/// reinterpret_le(p) +template +[[nodiscard]] constexpr A reinterpret_le(const void *p) { + return LERep(std::span(static_cast(p), sizeof(A))).operator A(); +} + #define SOURCEPP_LEREP_DEFINE(N, V)\ using N##le = LERep #define SOURCEPP_LEREP_DEFINE_P(PFX, N16, N32, N64) \ From 6206f0dc60cde82a05ebc25f8fe572d79a2e7cd3 Mon Sep 17 00:00:00 2001 From: yuuko Date: Thu, 11 Sep 2025 22:34:53 -0700 Subject: [PATCH 08/32] vtfpp: start using LERep where applicable milestone: entire vtfpp test suite passes! --- include/vtfpp/VTF.h | 9 +++++---- src/vtfpp/VTF.cpp | 20 +++++++++++--------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/include/vtfpp/VTF.h b/include/vtfpp/VTF.h index de8ed2c32..f4aae386a 100644 --- a/include/vtfpp/VTF.h +++ b/include/vtfpp/VTF.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -76,7 +77,7 @@ struct Resource { std::tuple, // LOD std::string, // KVD HOT, // Hotspot data - std::span // AXC + std::span // AXC >; [[nodiscard]] ConvertedData convertData() const; @@ -105,11 +106,11 @@ struct Resource { } [[nodiscard]] int16_t getDataAsAuxCompressionLevel() const { - return static_cast(std::get>(this->convertData())[1] & 0xffff); + return static_cast(std::get>(this->convertData())[1].operator uint32_t() & 0xffff); } [[nodiscard]] CompressionMethod getDataAsAuxCompressionMethod() const { - auto method = static_cast((std::get>(this->convertData())[1] & 0xffff0000) >> 16); + auto method = static_cast((std::get>(this->convertData())[1].operator uint32_t() & 0xffff0000) >> 16); if (method <= 0) { return CompressionMethod::DEFLATE; } @@ -117,7 +118,7 @@ struct Resource { } [[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]; + return std::get>(this->convertData())[((mipCount - 1 - mip) * frameCount * faceCount + frame * faceCount + face) + 2]; } }; SOURCEPP_BITFLAGS_ENUM(Resource::Flags) diff --git a/src/vtfpp/VTF.cpp b/src/vtfpp/VTF.cpp index 3d1a71f9c..4e3ce50af 100644 --- a/src/vtfpp/VTF.cpp +++ b/src/vtfpp/VTF.cpp @@ -18,10 +18,12 @@ #include #include +#include #include #include using namespace sourcepp; +using namespace sourcepp::bits; using namespace vtfpp; namespace { @@ -180,13 +182,13 @@ Resource::ConvertedData Resource::convertData() const { if (this->data.size() <= sizeof(uint32_t)) { return {}; } - return SHT{{reinterpret_cast(this->data.data()) + sizeof(uint32_t), *reinterpret_cast(this->data.data())}}; + return SHT{{reinterpret_cast(this->data.data()) + sizeof(uint32_t), reinterpret_le(this->data.data())}}; case TYPE_CRC: case TYPE_EXTENDED_FLAGS: if (this->data.size() != sizeof(uint32_t)) { return {}; } - return *reinterpret_cast(this->data.data()); + return reinterpret_le(this->data.data()); case TYPE_LOD_CONTROL_INFO: if (this->data.size() != sizeof(uint32_t)) { return {}; @@ -200,17 +202,17 @@ Resource::ConvertedData Resource::convertData() const { if (this->data.size() <= sizeof(uint32_t)) { return ""; } - return std::string(reinterpret_cast(this->data.data()) + sizeof(uint32_t), *reinterpret_cast(this->data.data())); + return std::string(reinterpret_cast(this->data.data()) + sizeof(uint32_t), reinterpret_le(this->data.data())); case TYPE_HOTSPOT_DATA: if (this->data.size() <= sizeof(uint32_t)) { return {}; } - return HOT{{reinterpret_cast(this->data.data()) + sizeof(uint32_t), *reinterpret_cast(this->data.data())}}; + return HOT{{reinterpret_cast(this->data.data()) + sizeof(uint32_t), reinterpret_le(this->data.data())}}; case TYPE_AUX_COMPRESSION: if (this->data.size() <= sizeof(uint32_t) || this->data.size() % sizeof(uint32_t) != 0) { return {}; } - return std::span{reinterpret_cast(this->data.data()), this->data.size() / 4}; + return std::span{reinterpret_cast(this->data.data()), this->data.size() / 4}; default: break; } @@ -303,7 +305,7 @@ VTF::VTF(std::vector&& vtfData, bool parseHeaderOnly) if (!(lhs.flags & Resource::FLAG_LOCAL_DATA) && (rhs.flags & Resource::FLAG_LOCAL_DATA)) { return false; } - return *reinterpret_cast(lhs.data.data()) < *reinterpret_cast(rhs.data.data()); + return reinterpret_le(lhs.data.data()) < reinterpret_le(rhs.data.data()); }); // Fix up data spans to point to the actual data @@ -311,8 +313,8 @@ VTF::VTF(std::vector&& vtfData, bool parseHeaderOnly) for (auto& resource : this->resources) { if (!(resource.flags & Resource::FLAG_LOCAL_DATA)) { if (lastResource) { - const auto lastOffset = *reinterpret_cast(lastResource->data.data()); - const auto currentOffset = *reinterpret_cast(resource.data.data()); + const auto lastOffset = reinterpret_le(lastResource->data.data()); + const auto currentOffset = reinterpret_le(resource.data.data()); const auto curPos = stream.tell(); stream.seek(lastOffset); lastResource->data = stream.read_span(currentOffset - lastOffset); @@ -322,7 +324,7 @@ VTF::VTF(std::vector&& vtfData, bool parseHeaderOnly) } } if (lastResource) { - auto offset = *reinterpret_cast(lastResource->data.data()); + auto offset = reinterpret_le(lastResource->data.data()); auto curPos = stream.tell(); stream.seek(offset); lastResource->data = stream.read_span(stream.size() - offset); From db8764c3fa7c83c66902023064d79950cbe682cc Mon Sep 17 00:00:00 2001 From: yuuko Date: Sat, 13 Sep 2025 19:03:13 -0700 Subject: [PATCH 09/32] vtfpp: ImagePixelV2 --- include/sourcepp/Macros.h | 61 +++++++++++++ include/vtfpp/ImageConversion.h | 156 ++++++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+) diff --git a/include/sourcepp/Macros.h b/include/sourcepp/Macros.h index 7d90bcf3b..a8fc37484 100644 --- a/include/sourcepp/Macros.h +++ b/include/sourcepp/Macros.h @@ -4,6 +4,7 @@ // Helpers #define SOURCEPP_CONCAT_DETAIL(a, b) a##b +/// Token pasting outside a macro context. #define SOURCEPP_CONCAT(a, b) SOURCEPP_CONCAT_DETAIL(a, b) /// Create a breakpoint in debug @@ -70,3 +71,63 @@ static_cast>(lhs) ^ \ static_cast>(rhs)); \ } + +/// Indirected `()`. +#define SOURCEPP_UNIT () + +#define SOURCEPP_EXPAND8(...) SOURCEPP_EXPAND7(SOURCEPP_EXPAND7(SOURCEPP_EXPAND7(SOURCEPP_EXPAND7(__VA_ARGS__)))) +#define SOURCEPP_EXPAND7(...) SOURCEPP_EXPAND6(SOURCEPP_EXPAND6(SOURCEPP_EXPAND6(SOURCEPP_EXPAND6(__VA_ARGS__)))) +#define SOURCEPP_EXPAND6(...) SOURCEPP_EXPAND5(SOURCEPP_EXPAND5(SOURCEPP_EXPAND5(SOURCEPP_EXPAND5(__VA_ARGS__)))) +#define SOURCEPP_EXPAND5(...) SOURCEPP_EXPAND4(SOURCEPP_EXPAND4(SOURCEPP_EXPAND4(SOURCEPP_EXPAND4(__VA_ARGS__)))) +#define SOURCEPP_EXPAND4(...) SOURCEPP_EXPAND3(SOURCEPP_EXPAND3(SOURCEPP_EXPAND3(SOURCEPP_EXPAND3(__VA_ARGS__)))) +#define SOURCEPP_EXPAND3(...) SOURCEPP_EXPAND2(SOURCEPP_EXPAND2(SOURCEPP_EXPAND2(SOURCEPP_EXPAND2(__VA_ARGS__)))) +#define SOURCEPP_EXPAND2(...) SOURCEPP_EXPAND1(SOURCEPP_EXPAND1(SOURCEPP_EXPAND1(SOURCEPP_EXPAND1(__VA_ARGS__)))) +#define SOURCEPP_EXPAND1(...) __VA_ARGS__ + +#define SOURCEPP_ID(...) __VA_ARGS__ + +/// Apply a unary macro to each of `__VA_ARGS__`. +/// \param sep Nullary function-like macro expected to expand to a separator. For rationale, see \ref SOURCEPP_THUNK_COMMA. +/// \param macro Unary macro. +/// \param ... List of first arguments per call to `macro`. +#define SOURCEPP_FOREACH0_SEP(sep, macro, ...) \ + __VA_OPT__(SOURCEPP_EXPAND5(SOURCEPP_FOREACH0_SEP_HELPER(sep, macro, __VA_ARGS__))) +#define SOURCEPP_FOREACH0_SEP_HELPER(sep, macro, x, ...) \ + macro(x) \ + __VA_OPT__(sep SOURCEPP_UNIT SOURCEPP_DEFER_FOREACH0_SEP_HELPER SOURCEPP_UNIT (sep, macro, __VA_ARGS__)) +#define SOURCEPP_DEFER_FOREACH0_SEP_HELPER() SOURCEPP_FOREACH0_SEP_HELPER + +/// Apply a binary macro to each of `__VA_ARGS__` with a set first argument. +/// \param sep Nullary function-like macro expected to expand to a separator. For rationale, see \ref SOURCEPP_THUNK_COMMA. +/// \param macro Binary macro. +/// \param a Always the first argument to `macro`. +/// \param ... List of second arguments per call to `macro` +#define SOURCEPP_FOREACH1_SEP(sep, macro, a, ...) \ + __VA_OPT__(SOURCEPP_EXPAND5(SOURCEPP_FOREACH1_SEP_HELPER(sep, macro, a, __VA_ARGS__))) +#define SOURCEPP_FOREACH1_SEP_HELPER(sep, macro, a, x, ...) \ + macro(a, x) \ + __VA_OPT__(sep SOURCEPP_UNIT SOURCEPP_DEFER_FOREACH1_SEP_HELPER SOURCEPP_UNIT (sep, macro, a, __VA_ARGS__)) +#define SOURCEPP_DEFER_FOREACH1_SEP_HELPER() SOURCEPP_FOREACH1_SEP_HELPER + +/// Nullary macro that expands to nothing. +#define SOURCEPP_THUNK_NOTHING() + +/// Turn its operand into (effectively) a nullary function-like macro that expands to it. +#define SOURCEPP_THUNK(id) id SOURCEPP_THUNK_NOTHING +/// Nullary macro that expands to a comma. It is necessary to defer expansion to any commas in the +/// desired output of complex macro expansions, to prevent the preprocessor from interpreting such a comma itself. +#define SOURCEPP_THUNK_COMMA() , + +/// Convenience variant of SOURCEPP_FOREACH0_SEP with no separator. +#define SOURCEPP_FOREACH0(macro, ...) SOURCEPP_FOREACH0_SEP(SOURCEPP_THUNK_NOTHING, macro, __VA_ARGS__) +/// Convenience variant of SOURCEPP_FOREACH1_SEP with no separator. +#define SOURCEPP_FOREACH1(macro, a, ...) SOURCEPP_FOREACH1_SEP(SOURCEPP_THUNK_NOTHING, macro, a, __VA_ARGS__) + +/// Callable parenthesization; identity function for 2-tuples when used bare as in: +/// +/// SOURCEPP_CONS TUPLE +#define SOURCEPP_CONS(a, d) (a, d) +/// Called bare to destructure the first of a 2-tuple. +#define SOURCEPP_CAR(a, d) a +/// Called bare to destructure the second of a 2-tuple. +#define SOURCEPP_CDR(a, d) d diff --git a/include/vtfpp/ImageConversion.h b/include/vtfpp/ImageConversion.h index 5d069bce0..dbd1f01d1 100644 --- a/include/vtfpp/ImageConversion.h +++ b/include/vtfpp/ImageConversion.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -8,6 +9,7 @@ #include #include +#include #include #include "ImageFormats.h" @@ -327,6 +329,160 @@ concept PixelType = } // namespace ImagePixel +namespace ImagePixelV2 { + +// TODO: run this through a filter that calls the compiler with comment-preserving preprocessing; +// doxygen can't really cope even with its preprocessing turned as indiscriminate as possible. +#define VTFPP_PIXFMTS \ + VTFPP_PIXFMT(RGBA8888, VTFPP_DECLARE_SIMPLE, uint8_t, r, g, b, a) \ + VTFPP_PIXFMT(ABGR8888, VTFPP_DECLARE_SIMPLE, uint8_t, a, b, g, r) \ + VTFPP_PIXFMT(RGB888, VTFPP_DECLARE_SIMPLE, uint8_t, r, g, b) \ + VTFPP_PIXFMT(RGB888_BLUESCREEN, VTFPP_DECLARE_INHERIT, RGB888) \ + VTFPP_PIXFMT(BGR888, VTFPP_DECLARE_SIMPLE, uint8_t, b, g, r) \ + VTFPP_PIXFMT(BGR888_BLUESCREEN, VTFPP_DECLARE_INHERIT, BGR888) \ + VTFPP_PIXFMT(RGB565, VTFPP_DECLARE_BITS, uint8_t, 16, (r, 5), (g, 6), (b, 5)) \ + VTFPP_PIXFMT(I8, VTFPP_DECLARE_SIMPLE, uint8_t, i) \ + VTFPP_PIXFMT(IA88, VTFPP_DECLARE_SIMPLE, uint8_t, i, a) \ + VTFPP_PIXFMT(P8, VTFPP_DECLARE_SIMPLE, uint8_t, p) \ + VTFPP_PIXFMT(A8, VTFPP_DECLARE_SIMPLE, uint8_t, a) \ + VTFPP_PIXFMT(ARGB8888, VTFPP_DECLARE_SIMPLE, uint8_t, a, r, g, b) \ + VTFPP_PIXFMT(BGRA8888, VTFPP_DECLARE_SIMPLE, uint8_t, b, g, r, a) \ + VTFPP_PIXFMT(BGRX8888, VTFPP_DECLARE_SIMPLE, uint8_t, b, g, r, x) \ + VTFPP_PIXFMT(BGR565, VTFPP_DECLARE_BITS, uint8_t, 16, (b, 5), (g, 6), (r, 5)) \ + VTFPP_PIXFMT(BGRX5551, VTFPP_DECLARE_BITS, uint8_t, 16, (b, 5), (g, 5), (r, 5), (x, 1)) \ + VTFPP_PIXFMT(BGRA4444, VTFPP_DECLARE_BITS, uint8_t, 16, (b, 4), (g, 4), (r, 4), (a, 4)) \ + VTFPP_PIXFMT(BGRA5551, VTFPP_DECLARE_BITS, uint8_t, 16, (b, 5), (g, 5), (r, 5), (a, 1)) \ + VTFPP_PIXFMT(UV88, VTFPP_DECLARE_SIMPLE, uint8_t, u, v) \ + VTFPP_PIXFMT(UVWQ8888, VTFPP_DECLARE_SIMPLE, uint8_t, u, v, w, q) \ + VTFPP_PIXFMT(RGBA16161616F, VTFPP_DECLARE_SIMPLE, f16le, r, g, b, a) \ + VTFPP_PIXFMT(RGBA16161616, VTFPP_DECLARE_SIMPLE, ui16le, r, g, b, a) \ + VTFPP_PIXFMT(UVLX8888, VTFPP_DECLARE_SIMPLE, uint8_t, u, v, l, x) \ + VTFPP_PIXFMT(R32F, VTFPP_DECLARE_SIMPLE, f32le, r) \ + VTFPP_PIXFMT(RGB323232F, VTFPP_DECLARE_SIMPLE, f32le, r, g, b) \ + VTFPP_PIXFMT(RGBA32323232F, VTFPP_DECLARE_SIMPLE, f32le, r, g, b, a) \ + VTFPP_PIXFMT(RG1616F, VTFPP_DECLARE_SIMPLE, f16le, r, g) \ + VTFPP_PIXFMT(RG3232F, VTFPP_DECLARE_SIMPLE, f32le, r, g) \ + VTFPP_PIXFMT(RGBX8888, VTFPP_DECLARE_SIMPLE, uint8_t, r, g, b, x) \ + VTFPP_PIXFMT(RGBA1010102, VTFPP_DECLARE_BITS, uint16_t, 32, (r, 10), (g, 10), (b, 10), (a, 2)) \ + VTFPP_PIXFMT(BGRA1010102, VTFPP_DECLARE_BITS, uint16_t, 32, (b, 10), (g, 10), (r, 10), (a, 2)) \ + VTFPP_PIXFMT(R16F, VTFPP_DECLARE_SIMPLE, f16le, r) \ + VTFPP_PIXFMT(R8, VTFPP_DECLARE_SIMPLE, uint8_t, r) \ + VTFPP_PIXFMT(CONSOLE_BGRX8888_LINEAR, VTFPP_DECLARE_INHERIT, BGRX8888) \ + VTFPP_PIXFMT(CONSOLE_RGBA8888_LINEAR, VTFPP_DECLARE_INHERIT, RGBA8888) \ + VTFPP_PIXFMT(CONSOLE_ABGR8888_LINEAR, VTFPP_DECLARE_INHERIT, ABGR8888) \ + VTFPP_PIXFMT(CONSOLE_ARGB8888_LINEAR, VTFPP_DECLARE_INHERIT, ARGB8888) \ + VTFPP_PIXFMT(CONSOLE_BGRA8888_LINEAR, VTFPP_DECLARE_INHERIT, BGRA8888) \ + VTFPP_PIXFMT(CONSOLE_RGB888_LINEAR, VTFPP_DECLARE_INHERIT, RGB888) \ + VTFPP_PIXFMT(CONSOLE_BGR888_LINEAR, VTFPP_DECLARE_INHERIT, BGR888) \ + VTFPP_PIXFMT(CONSOLE_BGRX5551_LINEAR, VTFPP_DECLARE_INHERIT, BGRX5551) \ + VTFPP_PIXFMT(CONSOLE_I8_LINEAR, VTFPP_DECLARE_INHERIT, I8) \ + VTFPP_PIXFMT(CONSOLE_RGBA16161616_LINEAR, VTFPP_DECLARE_INHERIT, RGBA16161616) \ + VTFPP_PIXFMT(CONSOLE_BGRX8888_LE, VTFPP_DECLARE_INHERIT, BGRX8888) \ + VTFPP_PIXFMT(CONSOLE_BGRA8888_LE, VTFPP_DECLARE_INHERIT, BGRA8888) + +#define VTFPP_TAKE_CHANNEL(T, C) \ + T SOURCEPP_CONCAT(i, C) + +#define VTFPP_DECLARE_SIMPLE_CHANNEL(T, C) \ + private: \ + T _##C; \ + public: \ + constexpr T C() { return this->_##C; } \ + void set_##C(T i##C) { this->_##C = i##C; } + +#define VTFPP_SET_CHANNEL(C) \ + this->SOURCEPP_CONCAT(set_, C)(SOURCEPP_CONCAT(i, C)); + +#define VTFPP_CHECK_SIZE(N) \ + static_assert(sizeof(N) == ImageFormatDetails::bpp(ImageFormat::N) / 8) + +#define VTFPP_DECLARE_SIMPLE(N, T, ...) \ + class N { \ + SOURCEPP_FOREACH1(VTFPP_DECLARE_SIMPLE_CHANNEL, T, __VA_ARGS__) \ + public: \ + static constexpr auto FORMAT = ImageFormat::N; \ + constexpr N(SOURCEPP_FOREACH1_SEP(SOURCEPP_THUNK_COMMA, VTFPP_TAKE_CHANNEL, T, __VA_ARGS__)) { \ + SOURCEPP_FOREACH0(VTFPP_SET_CHANNEL, __VA_ARGS__) \ + } \ + }; VTFPP_CHECK_SIZE(N) + +#define VTFPP_DECLARE_INHERIT(N, P) \ + class N : public P { \ + public: \ + using P::P; \ + static constexpr auto FORMAT = ImageFormat::N; \ + }; VTFPP_CHECK_SIZE(N) + +#define VTFPP_DECLARE_BITS_OFFS(...) \ + __VA_OPT__(SOURCEPP_EXPAND7(VTFPP_DECLARE_BITS_OFFS_HELPER(__VA_ARGS__))) +#define VTFPP_DECLARE_BITS_OFFS_HELPER(a, ...) \ + static constexpr size_t SOURCEPP_CONCAT(offs_, SOURCEPP_CAR a) = SOURCEPP_FOREACH0_SEP(SOURCEPP_THUNK(+), SOURCEPP_CDR SOURCEPP_ID, __VA_ARGS__) + 0; \ + __VA_OPT__(VTFPP_DEFER_DECLARE_BITS_OFFS_HELPER SOURCEPP_UNIT (__VA_ARGS__)) +#define VTFPP_DEFER_DECLARE_BITS_OFFS_HELPER() VTFPP_DECLARE_BITS_OFFS_HELPER + +#define VTFPP_DECLARE_BITS_CHANNEL(T, C, BW) \ + private: \ + static constexpr REPRTYPE SOURCEPP_CONCAT(max_, C) = (1 << BW) - 1; \ + public: \ + constexpr T C() { \ + return static_cast(this->_repr >> SOURCEPP_CONCAT(offs_, C) & SOURCEPP_CONCAT(max_, C)); \ + } \ + void SOURCEPP_CONCAT(set_, C) (T SOURCEPP_CONCAT(i, C)) { \ + this->_repr &= std::numeric_limits::max() ^ SOURCEPP_CONCAT(max_, C) << SOURCEPP_CONCAT(offs_, C); \ + /* clamping is an arbitrary choice vs masking; proper lerp is done in conversion anyway. */ \ + this->_repr |= std::clamp(SOURCEPP_CONCAT(i, C), 0, SOURCEPP_CONCAT(max_, C)) << SOURCEPP_CONCAT(offs_, C); \ + } + +#define VTFPP_DECLARE_BITS_CHANNEL_UNPACK(T, TUPLE) VTFPP_DECLARE_BITS_CHANNEL(T, SOURCEPP_CAR TUPLE, SOURCEPP_CDR TUPLE) +#define VTFPP_SET_CHANNEL_UNPACK(TUPLE) VTFPP_SET_CHANNEL(SOURCEPP_CAR TUPLE) +#define VTFPP_TAKE_CHANNEL_UNPACK(T, TUPLE) VTFPP_TAKE_CHANNEL(T, SOURCEPP_CAR TUPLE) +#define VTFPP_DECLARE_BITS(N, T, W, ...) \ + class N { \ + private: \ + using REPRTYPE = uint##W##_t; \ + REPRTYPE _repr; \ + VTFPP_DECLARE_BITS_OFFS(__VA_ARGS__) \ + SOURCEPP_FOREACH1(VTFPP_DECLARE_BITS_CHANNEL_UNPACK, T, __VA_ARGS__) \ + public: \ + static constexpr auto FORMAT = ImageFormat::N; \ + constexpr N(SOURCEPP_FOREACH1_SEP(SOURCEPP_THUNK_COMMA, VTFPP_TAKE_CHANNEL_UNPACK, T, __VA_ARGS__)) { \ + SOURCEPP_FOREACH0(VTFPP_SET_CHANNEL_UNPACK, __VA_ARGS__) \ + } \ + }; VTFPP_CHECK_SIZE(N) + +// aaaaaaand instantiate. +#define VTFPP_PIXFMT(N, D, ...) \ + D(N, __VA_ARGS__); +VTFPP_PIXFMTS +#undef VTFPP_PIXFMT + +template +concept PixelType = +# define VTFPP_PIXFMT(N, _D, ...) \ + (std::same_as), + // function style macros only ignore tokens within parens. the template invo is wrapped to + // be a black box, then it's doubly unwrapped so it's bare after all expansion. + SOURCEPP_FOREACH0_SEP(SOURCEPP_THUNK(||), SOURCEPP_ID SOURCEPP_ID, VTFPP_PIXFMTS); +# undef VTFPP_PIXFMT + +#undef VTFPP_DECLARE_BITS +#undef VTFPP_TAKE_CHANNEL_UNPACK +#undef VTFPP_SET_CHANNEL_UNPACK +#undef VTFPP_DECLARE_BITS_CHANNEL_UNPACK +#undef VTFPP_DECLARE_BITS_CHANNEL +#undef VTFPP_DEFER_DECLARE_BITS_OFFS_HELPER +#undef VTFPP_DECLARE_BITS_OFFS_HELPER +#undef VTFPP_DECLARE_BITS_OFFS +#undef VTFPP_DECLARE_INHERIT +#undef VTFPP_DECLARE_SIMPLE +#undef VTFPP_CHECK_SIZE +#undef VTFPP_SET_CHANNEL +#undef VTFPP_DECLARE_SIMPLE_CHANNEL +#undef VTFPP_TAKE_CHANNEL +#undef VTFPP_PIXFMTS + +} // namespace ImagePixelV2 + namespace ImageConversion { constexpr float DEFAULT_COMPRESSED_QUALITY = 0.105f; From 1f4cf9b7d279be88800e78bfc36af91d567dae7b Mon Sep 17 00:00:00 2001 From: yuuko Date: Sat, 13 Sep 2025 19:57:12 -0700 Subject: [PATCH 10/32] vtfpp: low-hanging fruit for ImagePixelV2 --- bench/vtfpp.cpp | 2 +- src/vtfpp/ImageConversion.cpp | 326 +++++++++++++++++----------------- 2 files changed, 164 insertions(+), 164 deletions(-) diff --git a/bench/vtfpp.cpp b/bench/vtfpp.cpp index 2e5d8b4c8..a17803fe8 100644 --- a/bench/vtfpp.cpp +++ b/bench/vtfpp.cpp @@ -21,7 +21,7 @@ namespace { 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)); \ + std::vector data(vtf.GetWidth() * vtf.GetHeight() * sizeof(ImagePixelV2::RGBA8888)); \ benchmark::DoNotOptimize(VTFLib::CVTFFile::ConvertToRGBA8888(vtf.GetData(0, 0, 0, 0), reinterpret_cast(data.data()), vtf.GetWidth(), vtf.GetHeight(), IMAGE_FORMAT_##VTFLibFormat)); \ } \ } \ diff --git a/src/vtfpp/ImageConversion.cpp b/src/vtfpp/ImageConversion.cpp index bc9f1945a..4d5410547 100644 --- a/src/vtfpp/ImageConversion.cpp +++ b/src/vtfpp/ImageConversion.cpp @@ -336,14 +336,14 @@ namespace { } std::vector newData; - newData.resize(imageData.size() / (ImageFormatDetails::bpp(format) / 8) * sizeof(ImagePixel::RGBA8888)); - std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA8888)}; + newData.resize(imageData.size() / (ImageFormatDetails::bpp(format) / 8) * sizeof(ImagePixelV2::RGBA8888)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::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 { \ + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::InputType)}; \ + std::transform(__VA_ARGS__ __VA_OPT__(,) imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::InputType pixel) -> ImagePixelV2::RGBA8888 { \ return {(r), (g), (b), (a)}; \ }) #ifdef SOURCEPP_BUILD_WITH_TBB @@ -356,38 +356,38 @@ namespace { switch (format) { using enum ImageFormat; - 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_AND_BREAK(RGB565, VTFPP_REMAP_TO_8(pixel.r, 5), VTFPP_REMAP_TO_8(pixel.g, 6), VTFPP_REMAP_TO_8(pixel.b, 5), 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_AND_BREAK(BGR565, VTFPP_REMAP_TO_8(pixel.r, 5), VTFPP_REMAP_TO_8(pixel.g, 6), VTFPP_REMAP_TO_8(pixel.b, 5), 0xff); - VTFPP_CASE_CONVERT_AND_BREAK(BGRA5551, VTFPP_REMAP_TO_8(pixel.r, 5), VTFPP_REMAP_TO_8(pixel.g, 5), VTFPP_REMAP_TO_8(pixel.b, 5), static_cast(pixel.a * 0xff)); - VTFPP_CASE_CONVERT_AND_BREAK(BGRX5551, VTFPP_REMAP_TO_8(pixel.r, 5), VTFPP_REMAP_TO_8(pixel.g, 5), VTFPP_REMAP_TO_8(pixel.b, 5), 1); - VTFPP_CASE_CONVERT_AND_BREAK(BGRA4444, VTFPP_REMAP_TO_8(pixel.r, 4), VTFPP_REMAP_TO_8(pixel.g, 4), VTFPP_REMAP_TO_8(pixel.b, 4), VTFPP_REMAP_TO_8(pixel.a, 4)); - VTFPP_CASE_CONVERT_AND_BREAK(UV88, pixel.u, pixel.v, 0, 0xff); - VTFPP_CASE_CONVERT_AND_BREAK(UVLX8888, pixel.u, pixel.v, pixel.l, 0xff); - VTFPP_CASE_CONVERT_AND_BREAK(RGBX8888, pixel.r, pixel.g, pixel.b, 0xff); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRX8888_LINEAR, pixel.r, pixel.g, pixel.b, 0xff); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_RGBA8888_LINEAR, pixel.r, pixel.g, pixel.b, pixel.a); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_ABGR8888_LINEAR, pixel.r, pixel.g, pixel.b, pixel.a); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_ARGB8888_LINEAR, pixel.r, pixel.g, pixel.b, pixel.a); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRA8888_LINEAR, pixel.r, pixel.g, pixel.b, pixel.a); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_RGB888_LINEAR, pixel.r, pixel.g, pixel.b, 0xff); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGR888_LINEAR, pixel.r, pixel.g, pixel.b, 0xff); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRX5551_LINEAR, VTFPP_REMAP_TO_8(pixel.r, 5), VTFPP_REMAP_TO_8(pixel.g, 5), VTFPP_REMAP_TO_8(pixel.b, 5), 1); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_I8_LINEAR, pixel.i, pixel.i, pixel.i, 0xff); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRX8888_LE, pixel.r, pixel.g, pixel.b, 0xff); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRA8888_LE, pixel.r, pixel.g, pixel.b, pixel.a); - VTFPP_CASE_CONVERT_AND_BREAK(R8, pixel.r, 0, 0, 0xff); + 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_AND_BREAK(RGB565, VTFPP_REMAP_TO_8(pixel.r(), 5), VTFPP_REMAP_TO_8(pixel.g(), 6), VTFPP_REMAP_TO_8(pixel.b(), 5), 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_AND_BREAK(BGR565, VTFPP_REMAP_TO_8(pixel.r(), 5), VTFPP_REMAP_TO_8(pixel.g(), 6), VTFPP_REMAP_TO_8(pixel.b(), 5), 0xff); + VTFPP_CASE_CONVERT_AND_BREAK(BGRA5551, VTFPP_REMAP_TO_8(pixel.r(), 5), VTFPP_REMAP_TO_8(pixel.g(), 5), VTFPP_REMAP_TO_8(pixel.b(), 5), static_cast(pixel.a() * 0xff)); + VTFPP_CASE_CONVERT_AND_BREAK(BGRX5551, VTFPP_REMAP_TO_8(pixel.r(), 5), VTFPP_REMAP_TO_8(pixel.g(), 5), VTFPP_REMAP_TO_8(pixel.b(), 5), 1); + VTFPP_CASE_CONVERT_AND_BREAK(BGRA4444, VTFPP_REMAP_TO_8(pixel.r(), 4), VTFPP_REMAP_TO_8(pixel.g(), 4), VTFPP_REMAP_TO_8(pixel.b(), 4), VTFPP_REMAP_TO_8(pixel.a(), 4)); + VTFPP_CASE_CONVERT_AND_BREAK(UV88, pixel.u(), pixel.v(), 0, 0xff); + VTFPP_CASE_CONVERT_AND_BREAK(UVLX8888, pixel.u(), pixel.v(), pixel.l(), 0xff); + VTFPP_CASE_CONVERT_AND_BREAK(RGBX8888, pixel.r(), pixel.g(), pixel.b(), 0xff); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRX8888_LINEAR, pixel.r(), pixel.g(), pixel.b(), 0xff); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_RGBA8888_LINEAR, pixel.r(), pixel.g(), pixel.b(), pixel.a()); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_ABGR8888_LINEAR, pixel.r(), pixel.g(), pixel.b(), pixel.a()); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_ARGB8888_LINEAR, pixel.r(), pixel.g(), pixel.b(), pixel.a()); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRA8888_LINEAR, pixel.r(), pixel.g(), pixel.b(), pixel.a()); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_RGB888_LINEAR, pixel.r(), pixel.g(), pixel.b(), 0xff); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGR888_LINEAR, pixel.r(), pixel.g(), pixel.b(), 0xff); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRX5551_LINEAR, VTFPP_REMAP_TO_8(pixel.r(), 5), VTFPP_REMAP_TO_8(pixel.g(), 5), VTFPP_REMAP_TO_8(pixel.b(), 5), 1); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_I8_LINEAR, pixel.i(), pixel.i(), pixel.i(), 0xff); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRX8888_LE, pixel.r(), pixel.g(), pixel.b(), 0xff); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRA8888_LE, pixel.r(), pixel.g(), pixel.b(), pixel.a()); + VTFPP_CASE_CONVERT_AND_BREAK(R8, pixel.r(), 0, 0, 0xff); default: SOURCEPP_DEBUG_BREAK; break; } @@ -410,23 +410,23 @@ namespace { return {imageData.begin(), imageData.end()}; } - std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA8888)}; + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA8888)}; std::vector newData; - newData.resize(imageData.size() / sizeof(ImagePixel::RGBA8888) * (ImageFormatDetails::bpp(format) / 8)); + newData.resize(imageData.size() / sizeof(ImagePixelV2::RGBA8888) * (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::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA8888 pixel) -> ImagePixel::InputType { \ - return __VA_ARGS__; \ + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::InputType)}; \ + std::transform(std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA8888 pixel) -> ImagePixelV2::InputType { \ + return ImagePixelV2::InputType(__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__; \ + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::InputType)}; \ + std::transform(imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA8888 pixel) -> ImagePixelV2::InputType { \ + return ImagePixelV2::InputType(__VA_ARGS__); \ }) #endif #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, ...) \ @@ -434,38 +434,38 @@ namespace { switch (format) { using enum ImageFormat; - 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), 1}); - 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}); - VTFPP_CASE_CONVERT_AND_BREAK(UVLX8888, {pixel.r, pixel.g, pixel.b}); - VTFPP_CASE_CONVERT_AND_BREAK(RGBX8888, {pixel.r, pixel.g, pixel.b, 0xff}); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRX8888_LINEAR, {pixel.b, pixel.g, pixel.r, 0xff}); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_RGBA8888_LINEAR, {pixel.r, pixel.g, pixel.b, pixel.a}); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_ABGR8888_LINEAR, {pixel.a, pixel.b, pixel.g, pixel.r}); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_ARGB8888_LINEAR, {pixel.a, pixel.r, pixel.g, pixel.b}); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRA8888_LINEAR, {pixel.b, pixel.g, pixel.r, pixel.a}); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_RGB888_LINEAR, {pixel.r, pixel.g, pixel.b}); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGR888_LINEAR, {pixel.b, pixel.g, pixel.r}); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRX5551_LINEAR, {VTFPP_REMAP_FROM_8(pixel.b, 5), VTFPP_REMAP_FROM_8(pixel.g, 5), VTFPP_REMAP_FROM_8(pixel.r, 5), 1}); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_I8_LINEAR, {pixel.r}); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRX8888_LE, {pixel.b, pixel.g, pixel.r, 0xff}); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRA8888_LE, {pixel.b, pixel.g, pixel.r, pixel.a}); - VTFPP_CASE_CONVERT_AND_BREAK(R8, {pixel.r}); + 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 ? ImagePixelV2::RGB888_BLUESCREEN(pixel.r(), pixel.g(), pixel.b()) : ImagePixelV2::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 ? ImagePixelV2::BGR888_BLUESCREEN(pixel.b(), pixel.g(), pixel.r()) : ImagePixelV2::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), 1); + 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()); + VTFPP_CASE_CONVERT_AND_BREAK(UVLX8888, pixel.r(), pixel.g(), pixel.b(), 0xff); + VTFPP_CASE_CONVERT_AND_BREAK(RGBX8888, pixel.r(), pixel.g(), pixel.b(), 0xff); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRX8888_LINEAR, pixel.b(), pixel.g(), pixel.r(), 0xff); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_RGBA8888_LINEAR, pixel.r(), pixel.g(), pixel.b(), pixel.a()); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_ABGR8888_LINEAR, pixel.a(), pixel.b(), pixel.g(), pixel.r()); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_ARGB8888_LINEAR, pixel.a(), pixel.r(), pixel.g(), pixel.b()); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRA8888_LINEAR, pixel.b(), pixel.g(), pixel.r(), pixel.a()); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_RGB888_LINEAR, pixel.r(), pixel.g(), pixel.b()); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGR888_LINEAR, pixel.b(), pixel.g(), pixel.r()); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRX5551_LINEAR, VTFPP_REMAP_FROM_8(pixel.b(), 5), VTFPP_REMAP_FROM_8(pixel.g(), 5), VTFPP_REMAP_FROM_8(pixel.r(), 5), 1); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_I8_LINEAR, pixel.r()); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRX8888_LE, pixel.b(), pixel.g(), pixel.r(), 0xff); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_BGRA8888_LE, pixel.b(), pixel.g(), pixel.r(), pixel.a()); + VTFPP_CASE_CONVERT_AND_BREAK(R8, pixel.r()); default: SOURCEPP_DEBUG_BREAK; break; } @@ -488,14 +488,14 @@ namespace { } std::vector newData; - newData.resize(imageData.size() / (ImageFormatDetails::bpp(format) / 8) * sizeof(ImagePixel::RGBA16161616)); - std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA16161616)}; + newData.resize(imageData.size() / (ImageFormatDetails::bpp(format) / 8) * sizeof(ImagePixelV2::RGBA16161616)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::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 { \ + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::InputType)}; \ + std::transform(__VA_ARGS__ __VA_OPT__(,) imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::InputType pixel) -> ImagePixelV2::RGBA16161616 { \ return { static_cast(r), static_cast(g), static_cast(b), static_cast(a) }; \ }) #ifdef SOURCEPP_BUILD_WITH_TBB @@ -534,9 +534,9 @@ namespace { switch (format) { using enum ImageFormat; - VTFPP_CASE_CONVERT_REMAP_AND_BREAK(RGBA1010102, pixel.r, pixel.g, pixel.b, pixel.a); - VTFPP_CASE_CONVERT_REMAP_AND_BREAK(BGRA1010102, pixel.r, pixel.g, pixel.b, pixel.a); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_RGBA16161616_LINEAR, pixel.r, pixel.g, pixel.b, pixel.a); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(RGBA1010102, pixel.r(), pixel.g(), pixel.b(), pixel.a()); + VTFPP_CASE_CONVERT_REMAP_AND_BREAK(BGRA1010102, pixel.r(), pixel.g(), pixel.b(), pixel.a()); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_RGBA16161616_LINEAR, pixel.r(), pixel.g(), pixel.b(), pixel.a()); default: SOURCEPP_DEBUG_BREAK; break; } @@ -561,22 +561,22 @@ namespace { return {imageData.begin(), imageData.end()}; } - std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA16161616)}; + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA16161616)}; std::vector newData; - newData.resize(imageData.size() / sizeof(ImagePixel::RGBA16161616) * (ImageFormatDetails::bpp(format) / 8)); + newData.resize(imageData.size() / sizeof(ImagePixelV2::RGBA16161616) * (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::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA16161616 pixel) -> ImagePixel::InputType { \ + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::InputType)}; \ + std::transform(std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA16161616 pixel) -> ImagePixelV2::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 { \ + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::InputType)}; \ + std::transform(imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA16161616 pixel) -> ImagePixelV2::InputType { \ return __VA_ARGS__; \ }) #endif @@ -585,9 +585,9 @@ namespace { switch (format) { using enum ImageFormat; - VTFPP_CASE_CONVERT_AND_BREAK(RGBA1010102, {VTFPP_REMAP_FROM_16(pixel.r, 10), VTFPP_REMAP_FROM_16(pixel.g, 10), VTFPP_REMAP_FROM_16(pixel.b, 10), VTFPP_REMAP_FROM_16(pixel.a, 2)}); - VTFPP_CASE_CONVERT_AND_BREAK(BGRA1010102, {VTFPP_REMAP_FROM_16(pixel.b, 10), VTFPP_REMAP_FROM_16(pixel.g, 10), VTFPP_REMAP_FROM_16(pixel.r, 10), VTFPP_REMAP_FROM_16(pixel.a, 2)}); - VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_RGBA16161616_LINEAR, {pixel.r, pixel.g, pixel.b, pixel.a}); + VTFPP_CASE_CONVERT_AND_BREAK(RGBA1010102, {VTFPP_REMAP_FROM_16(pixel.r(), 10), VTFPP_REMAP_FROM_16(pixel.g(), 10), VTFPP_REMAP_FROM_16(pixel.b(), 10), VTFPP_REMAP_FROM_16(pixel.a(), 2)}); + VTFPP_CASE_CONVERT_AND_BREAK(BGRA1010102, {VTFPP_REMAP_FROM_16(pixel.b(), 10), VTFPP_REMAP_FROM_16(pixel.g(), 10), VTFPP_REMAP_FROM_16(pixel.r(), 10), VTFPP_REMAP_FROM_16(pixel.a(), 2)}); + VTFPP_CASE_CONVERT_AND_BREAK(CONSOLE_RGBA16161616_LINEAR, {pixel.r(), pixel.g(), pixel.b(), pixel.a()}); default: SOURCEPP_DEBUG_BREAK; break; } @@ -608,12 +608,12 @@ namespace { } std::vector newData; - newData.resize(imageData.size() / (ImageFormatDetails::bpp(format) / 8) * sizeof(ImagePixel::RGBA32323232F)); - std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA32323232F)}; + newData.resize(imageData.size() / (ImageFormatDetails::bpp(format) / 8) * sizeof(ImagePixelV2::RGBA32323232F)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::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)}; }) + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::InputType)}; \ + std::transform(__VA_ARGS__ __VA_OPT__(,) imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::InputType pixel) -> ImagePixelV2::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::par_unseq) #else @@ -624,12 +624,12 @@ namespace { switch (format) { using enum ImageFormat; - VTFPP_CASE_CONVERT_AND_BREAK(R32F, pixel.r, 0.f, 0.f, 1.f); - VTFPP_CASE_CONVERT_AND_BREAK(RG3232F, pixel.r, pixel.g, 0.f, 1.f); - VTFPP_CASE_CONVERT_AND_BREAK(RGB323232F, pixel.r, pixel.g, pixel.b, 1.f); - VTFPP_CASE_CONVERT_AND_BREAK(R16F, pixel.r, 0.f, 0.f, 1.f); - VTFPP_CASE_CONVERT_AND_BREAK(RG1616F, pixel.r, pixel.g, 0.f, 1.f); - VTFPP_CASE_CONVERT_AND_BREAK(RGBA16161616F, pixel.r, pixel.g, pixel.b, pixel.a); + VTFPP_CASE_CONVERT_AND_BREAK(R32F, pixel.r(), 0.f, 0.f, 1.f); + VTFPP_CASE_CONVERT_AND_BREAK(RG3232F, pixel.r(), pixel.g(), 0.f, 1.f); + VTFPP_CASE_CONVERT_AND_BREAK(RGB323232F, pixel.r(), pixel.g(), pixel.b(), 1.f); + VTFPP_CASE_CONVERT_AND_BREAK(R16F, pixel.r(), 0.f, 0.f, 1.f); + VTFPP_CASE_CONVERT_AND_BREAK(RG1616F, pixel.r(), pixel.g(), 0.f, 1.f); + VTFPP_CASE_CONVERT_AND_BREAK(RGBA16161616F, pixel.r(), pixel.g(), pixel.b(), pixel.a()); default: SOURCEPP_DEBUG_BREAK; break; } @@ -651,20 +651,20 @@ namespace { return {imageData.begin(), imageData.end()}; } - std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA32323232F)}; + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA32323232F)}; std::vector newData; - newData.resize(imageData.size() / sizeof(ImagePixel::RGBA32323232F) * (ImageFormatDetails::bpp(format) / 8)); + newData.resize(imageData.size() / sizeof(ImagePixelV2::RGBA32323232F) * (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::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA32323232F pixel) -> ImagePixel::InputType { \ + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::InputType)}; \ + std::transform(std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA32323232F pixel) -> ImagePixelV2::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 { \ + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::InputType)}; \ + std::transform(imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA32323232F pixel) -> ImagePixelV2::InputType { \ return __VA_ARGS__; \ }) #endif @@ -673,12 +673,12 @@ namespace { switch (format) { using enum ImageFormat; - VTFPP_CASE_CONVERT_AND_BREAK(R32F, {pixel.r}); - VTFPP_CASE_CONVERT_AND_BREAK(RG3232F, {pixel.r, pixel.g}); - VTFPP_CASE_CONVERT_AND_BREAK(RGB323232F, {pixel.r, pixel.g, pixel.b}); - VTFPP_CASE_CONVERT_AND_BREAK(R16F, {half{pixel.r}}); - VTFPP_CASE_CONVERT_AND_BREAK(RG1616F, {half{pixel.r}, half{pixel.g}}); - VTFPP_CASE_CONVERT_AND_BREAK(RGBA16161616F, {half{pixel.r}, half{pixel.g}, half{pixel.b}, half{pixel.a}}); + VTFPP_CASE_CONVERT_AND_BREAK(R32F, {pixel.r()}); + VTFPP_CASE_CONVERT_AND_BREAK(RG3232F, {pixel.r(), pixel.g()}); + VTFPP_CASE_CONVERT_AND_BREAK(RGB323232F, {pixel.r(), pixel.g(), pixel.b()}); + VTFPP_CASE_CONVERT_AND_BREAK(R16F, {half{pixel.r()}}); + VTFPP_CASE_CONVERT_AND_BREAK(RG1616F, {half{pixel.r()}, half{pixel.g()}}); + VTFPP_CASE_CONVERT_AND_BREAK(RGBA16161616F, {half{pixel.r()}, half{pixel.g()}, half{pixel.b()}, half{pixel.a()}}); default: SOURCEPP_DEBUG_BREAK; break; } @@ -694,20 +694,20 @@ namespace { } std::vector newData; - newData.resize(imageData.size() / sizeof(ImagePixel::RGBA8888) * sizeof(ImagePixel::RGBA32323232F)); - std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA32323232F)}; + newData.resize(imageData.size() / sizeof(ImagePixelV2::RGBA8888) * sizeof(ImagePixelV2::RGBA32323232F)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::RGBA32323232F)}; - std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA8888)}; + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA8888)}; std::transform( #ifdef SOURCEPP_BUILD_WITH_TBB std::execution::par_unseq, #endif - imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA8888 pixel) -> ImagePixel::RGBA32323232F { + imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA8888 pixel) -> ImagePixelV2::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), + 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), }; }); @@ -720,20 +720,20 @@ namespace { } std::vector newData; - newData.resize(imageData.size() / sizeof(ImagePixel::RGBA32323232F) * sizeof(ImagePixel::RGBA8888)); - std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA8888)}; + newData.resize(imageData.size() / sizeof(ImagePixelV2::RGBA32323232F) * sizeof(ImagePixelV2::RGBA8888)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::RGBA8888)}; - std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA32323232F)}; + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA32323232F)}; std::transform( #ifdef SOURCEPP_BUILD_WITH_TBB std::execution::par_unseq, #endif - imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA32323232F pixel) -> ImagePixel::RGBA8888 { + imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA32323232F pixel) -> ImagePixelV2::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)), + 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)), }; }); @@ -746,20 +746,20 @@ namespace { } std::vector newData; - newData.resize(imageData.size() / sizeof(ImagePixel::RGBA8888) * sizeof(ImagePixel::RGBA16161616)); - std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA16161616)}; + newData.resize(imageData.size() / sizeof(ImagePixelV2::RGBA8888) * sizeof(ImagePixelV2::RGBA16161616)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::RGBA16161616)}; - std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA8888)}; + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA8888)}; std::transform( #ifdef SOURCEPP_BUILD_WITH_TBB std::execution::par_unseq, #endif - imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA8888 pixel) -> ImagePixel::RGBA16161616 { + imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA8888 pixel) -> ImagePixelV2::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), + 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), }; }); @@ -772,20 +772,20 @@ namespace { } std::vector newData; - newData.resize(imageData.size() / sizeof(ImagePixel::RGBA16161616) * sizeof(ImagePixel::RGBA8888)); - std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA8888)}; + newData.resize(imageData.size() / sizeof(ImagePixelV2::RGBA16161616) * sizeof(ImagePixelV2::RGBA8888)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::RGBA8888)}; - std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA16161616)}; + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA16161616)}; std::transform( #ifdef SOURCEPP_BUILD_WITH_TBB std::execution::par_unseq, #endif - imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA16161616 pixel) -> ImagePixel::RGBA8888 { + imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA16161616 pixel) -> ImagePixelV2::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)), + 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)), }; }); @@ -798,20 +798,20 @@ namespace { } std::vector newData; - newData.resize(imageData.size() / sizeof(ImagePixel::RGBA32323232F) * sizeof(ImagePixel::RGBA16161616)); - std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA16161616)}; + newData.resize(imageData.size() / sizeof(ImagePixelV2::RGBA32323232F) * sizeof(ImagePixelV2::RGBA16161616)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::RGBA16161616)}; - std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA32323232F)}; + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA32323232F)}; std::transform( #ifdef SOURCEPP_BUILD_WITH_TBB std::execution::par_unseq, #endif - imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA32323232F pixel) -> ImagePixel::RGBA16161616 { + imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA32323232F pixel) -> ImagePixelV2::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)), + 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)), }; }); @@ -824,20 +824,20 @@ namespace { } std::vector newData; - newData.resize(imageData.size() / sizeof(ImagePixel::RGBA16161616) * sizeof(ImagePixel::RGBA32323232F)); - std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::RGBA32323232F)}; + newData.resize(imageData.size() / sizeof(ImagePixelV2::RGBA16161616) * sizeof(ImagePixelV2::RGBA32323232F)); + std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::RGBA32323232F)}; - std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::RGBA16161616)}; + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA16161616)}; std::transform( #ifdef SOURCEPP_BUILD_WITH_TBB std::execution::par_unseq, #endif - imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA16161616 pixel) -> ImagePixel::RGBA32323232F { + imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA16161616 pixel) -> ImagePixelV2::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), + 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), }; }); @@ -988,7 +988,7 @@ std::array, 6> ImageConversion::convertHDRIToCubeMap(std: const auto right = startRightUp[i][1]; const auto up = startRightUp[i][2]; - faceData[i].resize(resolution * resolution * sizeof(ImagePixel::RGBA32323232F)); + faceData[i].resize(resolution * resolution * sizeof(ImagePixelV2::RGBA32323232F)); std::span face{reinterpret_cast(faceData[i].data()), reinterpret_cast(faceData[i].data() + faceData[i].size())}; for (int row = 0; row < resolution; row++) { From 25e5282e8b9cd15b7cc559ffa79968dc2d2a5e33 Mon Sep 17 00:00:00 2001 From: yuuko Date: Sat, 13 Sep 2025 21:01:09 -0700 Subject: [PATCH 11/32] sourcepp: add SOURCEPP_REVERSE to macros and clean up helper names --- include/sourcepp/Macros.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/include/sourcepp/Macros.h b/include/sourcepp/Macros.h index a8fc37484..490415db7 100644 --- a/include/sourcepp/Macros.h +++ b/include/sourcepp/Macros.h @@ -94,8 +94,8 @@ __VA_OPT__(SOURCEPP_EXPAND5(SOURCEPP_FOREACH0_SEP_HELPER(sep, macro, __VA_ARGS__))) #define SOURCEPP_FOREACH0_SEP_HELPER(sep, macro, x, ...) \ macro(x) \ - __VA_OPT__(sep SOURCEPP_UNIT SOURCEPP_DEFER_FOREACH0_SEP_HELPER SOURCEPP_UNIT (sep, macro, __VA_ARGS__)) -#define SOURCEPP_DEFER_FOREACH0_SEP_HELPER() SOURCEPP_FOREACH0_SEP_HELPER + __VA_OPT__(sep SOURCEPP_UNIT SOURCEPP_FOREACH0_SEP_HELPER_THUNK SOURCEPP_UNIT (sep, macro, __VA_ARGS__)) +#define SOURCEPP_FOREACH0_SEP_HELPER_THUNK() SOURCEPP_FOREACH0_SEP_HELPER /// Apply a binary macro to each of `__VA_ARGS__` with a set first argument. /// \param sep Nullary function-like macro expected to expand to a separator. For rationale, see \ref SOURCEPP_THUNK_COMMA. @@ -106,8 +106,15 @@ __VA_OPT__(SOURCEPP_EXPAND5(SOURCEPP_FOREACH1_SEP_HELPER(sep, macro, a, __VA_ARGS__))) #define SOURCEPP_FOREACH1_SEP_HELPER(sep, macro, a, x, ...) \ macro(a, x) \ - __VA_OPT__(sep SOURCEPP_UNIT SOURCEPP_DEFER_FOREACH1_SEP_HELPER SOURCEPP_UNIT (sep, macro, a, __VA_ARGS__)) -#define SOURCEPP_DEFER_FOREACH1_SEP_HELPER() SOURCEPP_FOREACH1_SEP_HELPER + __VA_OPT__(sep SOURCEPP_UNIT SOURCEPP_FOREACH1_SEP_HELPER_THUNK SOURCEPP_UNIT (sep, macro, a, __VA_ARGS__)) +#define SOURCEPP_FOREACH1_SEP_HELPER_THUNK() SOURCEPP_FOREACH1_SEP_HELPER + +/// Reverse an argument list; evaluates comma-separated but unparenthesized. +#define SOURCEPP_REVERSE(...) \ + __VA_OPT__(SOURCEPP_EXPAND5(SOURCEPP_REVERSE_HELPER(__VA_ARGS__))) +#define SOURCEPP_REVERSE_HELPER(a, ...) \ + __VA_OPT__(SOURCEPP_REVERSE_HELPER_THUNK SOURCEPP_UNIT (__VA_ARGS__),) a +#define SOURCEPP_REVERSE_HELPER_THUNK() SOURCEPP_REVERSE_HELPER /// Nullary macro that expands to nothing. #define SOURCEPP_THUNK_NOTHING() From 249569834c12ad403595b2694930bceed6211dd5 Mon Sep 17 00:00:00 2001 From: yuuko Date: Sat, 13 Sep 2025 21:02:11 -0700 Subject: [PATCH 12/32] vtfpp: ImagePixelV2: reverse offsets in BITS declarations (oopsie) --- include/vtfpp/ImageConversion.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/include/vtfpp/ImageConversion.h b/include/vtfpp/ImageConversion.h index dbd1f01d1..1923bb0ed 100644 --- a/include/vtfpp/ImageConversion.h +++ b/include/vtfpp/ImageConversion.h @@ -417,20 +417,19 @@ namespace ImagePixelV2 { __VA_OPT__(SOURCEPP_EXPAND7(VTFPP_DECLARE_BITS_OFFS_HELPER(__VA_ARGS__))) #define VTFPP_DECLARE_BITS_OFFS_HELPER(a, ...) \ static constexpr size_t SOURCEPP_CONCAT(offs_, SOURCEPP_CAR a) = SOURCEPP_FOREACH0_SEP(SOURCEPP_THUNK(+), SOURCEPP_CDR SOURCEPP_ID, __VA_ARGS__) + 0; \ - __VA_OPT__(VTFPP_DEFER_DECLARE_BITS_OFFS_HELPER SOURCEPP_UNIT (__VA_ARGS__)) -#define VTFPP_DEFER_DECLARE_BITS_OFFS_HELPER() VTFPP_DECLARE_BITS_OFFS_HELPER + __VA_OPT__(VTFPP_DECLARE_BITS_OFFS_HELPER_THUNK SOURCEPP_UNIT (__VA_ARGS__)) +#define VTFPP_DECLARE_BITS_OFFS_HELPER_THUNK() VTFPP_DECLARE_BITS_OFFS_HELPER #define VTFPP_DECLARE_BITS_CHANNEL(T, C, BW) \ private: \ static constexpr REPRTYPE SOURCEPP_CONCAT(max_, C) = (1 << BW) - 1; \ public: \ constexpr T C() { \ - return static_cast(this->_repr >> SOURCEPP_CONCAT(offs_, C) & SOURCEPP_CONCAT(max_, C)); \ + return static_cast((this->_repr >> SOURCEPP_CONCAT(offs_, C)) & SOURCEPP_CONCAT(max_, C)); \ } \ void SOURCEPP_CONCAT(set_, C) (T SOURCEPP_CONCAT(i, C)) { \ - this->_repr &= std::numeric_limits::max() ^ SOURCEPP_CONCAT(max_, C) << SOURCEPP_CONCAT(offs_, C); \ - /* clamping is an arbitrary choice vs masking; proper lerp is done in conversion anyway. */ \ - this->_repr |= std::clamp(SOURCEPP_CONCAT(i, C), 0, SOURCEPP_CONCAT(max_, C)) << SOURCEPP_CONCAT(offs_, C); \ + this->_repr &= (std::numeric_limits::max() ^ SOURCEPP_CONCAT(max_, C)) << SOURCEPP_CONCAT(offs_, C); \ + this->_repr |= (SOURCEPP_CONCAT(i, C) & SOURCEPP_CONCAT(max_, C)) << SOURCEPP_CONCAT(offs_, C); \ } #define VTFPP_DECLARE_BITS_CHANNEL_UNPACK(T, TUPLE) VTFPP_DECLARE_BITS_CHANNEL(T, SOURCEPP_CAR TUPLE, SOURCEPP_CDR TUPLE) @@ -441,7 +440,7 @@ namespace ImagePixelV2 { private: \ using REPRTYPE = uint##W##_t; \ REPRTYPE _repr; \ - VTFPP_DECLARE_BITS_OFFS(__VA_ARGS__) \ + VTFPP_DECLARE_BITS_OFFS(SOURCEPP_REVERSE(__VA_ARGS__)) \ SOURCEPP_FOREACH1(VTFPP_DECLARE_BITS_CHANNEL_UNPACK, T, __VA_ARGS__) \ public: \ static constexpr auto FORMAT = ImageFormat::N; \ @@ -470,7 +469,7 @@ concept PixelType = #undef VTFPP_SET_CHANNEL_UNPACK #undef VTFPP_DECLARE_BITS_CHANNEL_UNPACK #undef VTFPP_DECLARE_BITS_CHANNEL -#undef VTFPP_DEFER_DECLARE_BITS_OFFS_HELPER +#undef VTFPP_DECLARE_BITS_OFFS_HELPER_THUNK #undef VTFPP_DECLARE_BITS_OFFS_HELPER #undef VTFPP_DECLARE_BITS_OFFS #undef VTFPP_DECLARE_INHERIT From 9694ad5056b1e72f5114cdabdbc354931dd8467c Mon Sep 17 00:00:00 2001 From: yuuko Date: Sat, 13 Sep 2025 21:13:13 -0700 Subject: [PATCH 13/32] vtfpp: ImagePixelV2: make BITS pixel formats use LERep! --- include/vtfpp/ImageConversion.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/vtfpp/ImageConversion.h b/include/vtfpp/ImageConversion.h index 1923bb0ed..23f927757 100644 --- a/include/vtfpp/ImageConversion.h +++ b/include/vtfpp/ImageConversion.h @@ -425,11 +425,11 @@ namespace ImagePixelV2 { static constexpr REPRTYPE SOURCEPP_CONCAT(max_, C) = (1 << BW) - 1; \ public: \ constexpr T C() { \ - return static_cast((this->_repr >> SOURCEPP_CONCAT(offs_, C)) & SOURCEPP_CONCAT(max_, C)); \ + return static_cast((this->_repr.operator REPRTYPE() >> SOURCEPP_CONCAT(offs_, C)) & SOURCEPP_CONCAT(max_, C)); \ } \ void SOURCEPP_CONCAT(set_, C) (T SOURCEPP_CONCAT(i, C)) { \ - this->_repr &= (std::numeric_limits::max() ^ SOURCEPP_CONCAT(max_, C)) << SOURCEPP_CONCAT(offs_, C); \ - this->_repr |= (SOURCEPP_CONCAT(i, C) & SOURCEPP_CONCAT(max_, C)) << SOURCEPP_CONCAT(offs_, C); \ + this->_repr = this->_repr.operator REPRTYPE() & ((std::numeric_limits::max() ^ SOURCEPP_CONCAT(max_, C)) << SOURCEPP_CONCAT(offs_, C)); \ + this->_repr = this->_repr.operator REPRTYPE() | ((SOURCEPP_CONCAT(i, C) & SOURCEPP_CONCAT(max_, C)) << SOURCEPP_CONCAT(offs_, C)); \ } #define VTFPP_DECLARE_BITS_CHANNEL_UNPACK(T, TUPLE) VTFPP_DECLARE_BITS_CHANNEL(T, SOURCEPP_CAR TUPLE, SOURCEPP_CDR TUPLE) @@ -439,7 +439,7 @@ namespace ImagePixelV2 { class N { \ private: \ using REPRTYPE = uint##W##_t; \ - REPRTYPE _repr; \ + sourcepp::bits::LERep _repr; \ VTFPP_DECLARE_BITS_OFFS(SOURCEPP_REVERSE(__VA_ARGS__)) \ SOURCEPP_FOREACH1(VTFPP_DECLARE_BITS_CHANNEL_UNPACK, T, __VA_ARGS__) \ public: \ From a82ddfdc58697429b861dd3fa45cedd5bd494ce4 Mon Sep 17 00:00:00 2001 From: yuuko Date: Sat, 13 Sep 2025 21:43:07 -0700 Subject: [PATCH 14/32] fix(cmake): enable compliant preprocessor on msvc --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bd275f7a..a4873b13f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,10 @@ cmake_minimum_required(VERSION 3.25 FATAL_ERROR) project(sourcepp DESCRIPTION "Several modern C++20 libraries for sanely parsing Valve formats.") set(CMAKE_CXX_STANDARD 20) +if(MSVC) + # secret second flag to *actually* make msvc a c++20-compilant compiler + add_compile_options($<$:/Zc:preprocessor>) +endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) From ad37b2d7bf0fcc8d2e3035de91c2465c4c2c69f8 Mon Sep 17 00:00:00 2001 From: yuuko Date: Sat, 13 Sep 2025 21:43:35 -0700 Subject: [PATCH 15/32] vtfpp: ImagePixelV2: constexpr correctness --- include/vtfpp/ImageConversion.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/vtfpp/ImageConversion.h b/include/vtfpp/ImageConversion.h index 23f927757..9af6fc620 100644 --- a/include/vtfpp/ImageConversion.h +++ b/include/vtfpp/ImageConversion.h @@ -388,7 +388,7 @@ namespace ImagePixelV2 { T _##C; \ public: \ constexpr T C() { return this->_##C; } \ - void set_##C(T i##C) { this->_##C = i##C; } + constexpr void set_##C(T i##C) { this->_##C = i##C; } #define VTFPP_SET_CHANNEL(C) \ this->SOURCEPP_CONCAT(set_, C)(SOURCEPP_CONCAT(i, C)); @@ -427,7 +427,7 @@ namespace ImagePixelV2 { constexpr T C() { \ return static_cast((this->_repr.operator REPRTYPE() >> SOURCEPP_CONCAT(offs_, C)) & SOURCEPP_CONCAT(max_, C)); \ } \ - void SOURCEPP_CONCAT(set_, C) (T SOURCEPP_CONCAT(i, C)) { \ + constexpr void SOURCEPP_CONCAT(set_, C) (T SOURCEPP_CONCAT(i, C)) { \ this->_repr = this->_repr.operator REPRTYPE() & ((std::numeric_limits::max() ^ SOURCEPP_CONCAT(max_, C)) << SOURCEPP_CONCAT(offs_, C)); \ this->_repr = this->_repr.operator REPRTYPE() | ((SOURCEPP_CONCAT(i, C) & SOURCEPP_CONCAT(max_, C)) << SOURCEPP_CONCAT(offs_, C)); \ } From a761f0c5cbc863923333f91cc9ada7e0f218609e Mon Sep 17 00:00:00 2001 From: yuuko Date: Sun, 14 Sep 2025 00:32:34 -0700 Subject: [PATCH 16/32] sourcepp: reinterpret_le -> deref_as_le name sat better; reinterpret_le reads too much like pointer-to-pointer --- include/sourcepp/Bits.h | 4 ++-- src/vtfpp/VTF.cpp | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/sourcepp/Bits.h b/include/sourcepp/Bits.h index 08e72babd..6a4fdc531 100644 --- a/include/sourcepp/Bits.h +++ b/include/sourcepp/Bits.h @@ -78,9 +78,9 @@ class LERep : public std::array { /// /// , such invocations, where spuriously dependent upon host byte order, may be replaced with: /// -/// reinterpret_le(p) +/// deref_as_le(p) template -[[nodiscard]] constexpr A reinterpret_le(const void *p) { +[[nodiscard]] constexpr A deref_as_le(const void *p) { return LERep(std::span(static_cast(p), sizeof(A))).operator A(); } diff --git a/src/vtfpp/VTF.cpp b/src/vtfpp/VTF.cpp index 13027dc43..7ccaac28c 100644 --- a/src/vtfpp/VTF.cpp +++ b/src/vtfpp/VTF.cpp @@ -235,13 +235,13 @@ Resource::ConvertedData Resource::convertData() const { if (this->data.size() <= sizeof(uint32_t)) { return {}; } - return SHT{{reinterpret_cast(this->data.data()) + sizeof(uint32_t), reinterpret_le(this->data.data())}}; + return SHT{{reinterpret_cast(this->data.data()) + sizeof(uint32_t), deref_as_le(this->data.data())}}; case TYPE_CRC: case TYPE_EXTENDED_FLAGS: if (this->data.size() != sizeof(uint32_t)) { return {}; } - return reinterpret_le(this->data.data()); + return deref_as_le(this->data.data()); case TYPE_LOD_CONTROL_INFO: if (this->data.size() != sizeof(uint32_t)) { return {}; @@ -255,12 +255,12 @@ Resource::ConvertedData Resource::convertData() const { if (this->data.size() <= sizeof(uint32_t)) { return ""; } - return std::string(reinterpret_cast(this->data.data()) + sizeof(uint32_t), reinterpret_le(this->data.data())); + return std::string(reinterpret_cast(this->data.data()) + sizeof(uint32_t), deref_as_le(this->data.data())); case TYPE_HOTSPOT_DATA: if (this->data.size() <= sizeof(uint32_t)) { return {}; } - return HOT{{reinterpret_cast(this->data.data()) + sizeof(uint32_t), reinterpret_le(this->data.data())}}; + return HOT{{reinterpret_cast(this->data.data()) + sizeof(uint32_t), deref_as_le(this->data.data())}}; case TYPE_AUX_COMPRESSION: if (this->data.size() <= sizeof(uint32_t) || this->data.size() % sizeof(uint32_t) != 0) { return {}; @@ -356,7 +356,7 @@ VTF::VTF(std::vector&& vtfData, bool parseHeaderOnly) if (!(lhs.flags & Resource::FLAG_LOCAL_DATA) && (rhs.flags & Resource::FLAG_LOCAL_DATA)) { return false; } - return reinterpret_le(lhs.data.data()) < reinterpret_le(rhs.data.data()); + return deref_as_le(lhs.data.data()) < deref_as_le(rhs.data.data()); }); // Fix up data spans to point to the actual data @@ -364,8 +364,8 @@ VTF::VTF(std::vector&& vtfData, bool parseHeaderOnly) for (auto& resource : this->resources) { if (!(resource.flags & Resource::FLAG_LOCAL_DATA)) { if (lastResource) { - const auto lastOffset = reinterpret_le(lastResource->data.data()); - const auto currentOffset = reinterpret_le(resource.data.data()); + const auto lastOffset = deref_as_le(lastResource->data.data()); + const auto currentOffset = deref_as_le(resource.data.data()); const auto curPos = stream.tell(); stream.seek(lastOffset); lastResource->data = stream.read_span(currentOffset - lastOffset); @@ -375,7 +375,7 @@ VTF::VTF(std::vector&& vtfData, bool parseHeaderOnly) } } if (lastResource) { - auto offset = reinterpret_le(lastResource->data.data()); + auto offset = deref_as_le(lastResource->data.data()); auto curPos = stream.tell(); stream.seek(offset); lastResource->data = stream.read_span(stream.size() - offset); From d83a7608d4de932501bf8fab7e2587974a75581d Mon Sep 17 00:00:00 2001 From: yuuko Date: Sun, 14 Sep 2025 04:46:42 -0700 Subject: [PATCH 17/32] vtfpp: account for byte order sensitivity in compressonator --- src/vtfpp/ImageConversion.cpp | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/vtfpp/ImageConversion.cpp b/src/vtfpp/ImageConversion.cpp index 4d5410547..91c5b1b98 100644 --- a/src/vtfpp/ImageConversion.cpp +++ b/src/vtfpp/ImageConversion.cpp @@ -288,15 +288,31 @@ namespace { return {}; } + auto swizzledInput = imageData; + std::vector swizzle; + + // compressonator seems to do absolutely everything right *except* writing output per native-order dword. oh well. + if constexpr (std::endian::native == std::endian::big) { + auto t = imageData.size(); + swizzle.resize(t); + for (size_t i = 0; i < t; i += 4) { + swizzle[i] = imageData[i + 3]; + swizzle[i + 1] = imageData[i + 2]; + swizzle[i + 2] = imageData[i + 1]; + swizzle[i + 3] = imageData[i]; + } + swizzledInput = std::span(swizzle); + } + CMP_Texture srcTexture{}; srcTexture.dwSize = sizeof(srcTexture); srcTexture.dwWidth = width; srcTexture.dwHeight = height; srcTexture.dwPitch = ImageFormatDetails::compressed(newFormat) ? 0 : width * (ImageFormatDetails::bpp(newFormat) / 8); srcTexture.format = ::imageFormatToCompressonatorFormat(oldFormat); - srcTexture.dwDataSize = imageData.size(); + srcTexture.dwDataSize = swizzledInput.size(); - srcTexture.pData = const_cast(reinterpret_cast(imageData.data())); + srcTexture.pData = const_cast(reinterpret_cast(swizzledInput.data())); CMP_Texture destTexture{}; destTexture.dwSize = sizeof(destTexture); @@ -321,6 +337,16 @@ namespace { if (CMP_ConvertTexture(&srcTexture, &destTexture, &options, nullptr) != CMP_OK) { return {}; } + + // "i dont like it but it works" + if constexpr (std::endian::native == std::endian::big) { + auto t = destData.size(); + for (size_t i = 0; i < t; i += 4) { + std::swap(destData[i], destData[i + 3]); + std::swap(destData[i + 2], destData[i + 1]); + } + } + return destData; } From e816e6be82c9b6109ab34d958cdb3c976525f268 Mon Sep 17 00:00:00 2001 From: yuuko Date: Sun, 14 Sep 2025 06:44:48 -0700 Subject: [PATCH 18/32] vtfpp: use f32le when cubemapping --- src/vtfpp/ImageConversion.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vtfpp/ImageConversion.cpp b/src/vtfpp/ImageConversion.cpp index 91c5b1b98..9f3c4f8a3 100644 --- a/src/vtfpp/ImageConversion.cpp +++ b/src/vtfpp/ImageConversion.cpp @@ -984,12 +984,12 @@ std::array, 6> ImageConversion::convertHDRIToCubeMap(std: resolution = height; } - std::span imageDataRGBA32323232F{reinterpret_cast(imageData.data()), reinterpret_cast(imageData.data() + imageData.size())}; + std::span imageDataRGBA32323232F{reinterpret_cast(imageData.data()), reinterpret_cast(imageData.data() + imageData.size())}; std::vector possiblyConvertedDataOrEmptyDontUseMeDirectly; if (format != ImageFormat::RGBA32323232F) { possiblyConvertedDataOrEmptyDontUseMeDirectly = convertImageDataToFormat(imageData, format, ImageFormat::RGBA32323232F, width, height); - imageDataRGBA32323232F = {reinterpret_cast(possiblyConvertedDataOrEmptyDontUseMeDirectly.data()), reinterpret_cast(possiblyConvertedDataOrEmptyDontUseMeDirectly.data() + possiblyConvertedDataOrEmptyDontUseMeDirectly.size())}; + imageDataRGBA32323232F = {reinterpret_cast(possiblyConvertedDataOrEmptyDontUseMeDirectly.data()), reinterpret_cast(possiblyConvertedDataOrEmptyDontUseMeDirectly.data() + possiblyConvertedDataOrEmptyDontUseMeDirectly.size())}; } // For each face, contains the 3d starting point (corresponding to left bottom pixel), right direction, @@ -1015,7 +1015,7 @@ std::array, 6> ImageConversion::convertHDRIToCubeMap(std: const auto up = startRightUp[i][2]; faceData[i].resize(resolution * resolution * sizeof(ImagePixelV2::RGBA32323232F)); - std::span face{reinterpret_cast(faceData[i].data()), reinterpret_cast(faceData[i].data() + faceData[i].size())}; + std::span face{reinterpret_cast(faceData[i].data()), reinterpret_cast(faceData[i].data() + faceData[i].size())}; for (int row = 0; row < resolution; row++) { for (int col = 0; col < resolution; col++) { @@ -1068,10 +1068,10 @@ std::array, 6> ImageConversion::convertHDRIToCubeMap(std: float f4 = factorRow * factorCol; for (int j = 0; j < 4; j++) { face[col * 4 + resolution * row * 4 + j] = - imageDataRGBA32323232F[low_idx_column * 4 + width * low_idx_row * 4 + j] * f1 + - imageDataRGBA32323232F[low_idx_column * 4 + width * high_idx_row * 4 + j] * f2 + - imageDataRGBA32323232F[high_idx_column * 4 + width * low_idx_row * 4 + j] * f3 + - imageDataRGBA32323232F[high_idx_column * 4 + width * high_idx_row * 4 + j] * f4; + static_cast(imageDataRGBA32323232F[low_idx_column * 4 + width * low_idx_row * 4 + j]) * f1 + + static_cast(imageDataRGBA32323232F[low_idx_column * 4 + width * high_idx_row * 4 + j]) * f2 + + static_cast(imageDataRGBA32323232F[high_idx_column * 4 + width * low_idx_row * 4 + j]) * f3 + + static_cast(imageDataRGBA32323232F[high_idx_column * 4 + width * high_idx_row * 4 + j]) * f4; } } } From e0e722f86c023b922f4298ec6be73cba29842955 Mon Sep 17 00:00:00 2001 From: yuuko Date: Sun, 14 Sep 2025 15:25:30 -0700 Subject: [PATCH 19/32] vtfpp: ImagePixelV2: more conster more gooder --- include/vtfpp/ImageConversion.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/vtfpp/ImageConversion.h b/include/vtfpp/ImageConversion.h index 9af6fc620..f34b2ec04 100644 --- a/include/vtfpp/ImageConversion.h +++ b/include/vtfpp/ImageConversion.h @@ -387,7 +387,7 @@ namespace ImagePixelV2 { private: \ T _##C; \ public: \ - constexpr T C() { return this->_##C; } \ + constexpr T C() const { return this->_##C; } \ constexpr void set_##C(T i##C) { this->_##C = i##C; } #define VTFPP_SET_CHANNEL(C) \ @@ -424,7 +424,7 @@ namespace ImagePixelV2 { private: \ static constexpr REPRTYPE SOURCEPP_CONCAT(max_, C) = (1 << BW) - 1; \ public: \ - constexpr T C() { \ + constexpr T C() const { \ return static_cast((this->_repr.operator REPRTYPE() >> SOURCEPP_CONCAT(offs_, C)) & SOURCEPP_CONCAT(max_, C)); \ } \ constexpr void SOURCEPP_CONCAT(set_, C) (T SOURCEPP_CONCAT(i, C)) { \ From f5bde62796454a7353dceae8e46c1db2939f5253 Mon Sep 17 00:00:00 2001 From: yuuko Date: Sun, 14 Sep 2025 15:35:52 -0700 Subject: [PATCH 20/32] vtfpp: more ImagePixelV2 --- src/vtfpp/ImageConversion.cpp | 106 +++++++++++++++++----------------- src/vtfpp/ImageQuantize.cpp | 6 +- src/vtfpp/VTF.cpp | 10 ++-- test/vtfpp.cpp | 2 +- 4 files changed, 62 insertions(+), 62 deletions(-) diff --git a/src/vtfpp/ImageConversion.cpp b/src/vtfpp/ImageConversion.cpp index 9f3c4f8a3..fbb5d2854 100644 --- a/src/vtfpp/ImageConversion.cpp +++ b/src/vtfpp/ImageConversion.cpp @@ -1118,52 +1118,52 @@ std::vector ImageConversion::convertImageDataToFile(std::span ImageConversion::convertImageDataToFile(std::span(imageData.data()), static_cast(width * sizeof(ImagePixel::RGB888))); + WebPPictureImportRGB(&pic, reinterpret_cast(imageData.data()), static_cast(width * sizeof(ImagePixelV2::RGB888))); } else if (format == ImageFormat::RGBA8888) { - WebPPictureImportRGBA(&pic, reinterpret_cast(imageData.data()), static_cast(width * sizeof(ImagePixel::RGBA8888))); + WebPPictureImportRGBA(&pic, reinterpret_cast(imageData.data()), static_cast(width * sizeof(ImagePixelV2::RGBA8888))); } else if (ImageFormatDetails::opaque(format)) { const auto rgb = convertImageDataToFormat(imageData, format, ImageFormat::RGB888, width, height); - WebPPictureImportRGB(&pic, reinterpret_cast(rgb.data()), static_cast(width * sizeof(ImagePixel::RGB888))); + WebPPictureImportRGB(&pic, reinterpret_cast(rgb.data()), static_cast(width * sizeof(ImagePixelV2::RGB888))); } else { const auto rgba = convertImageDataToFormat(imageData, format, ImageFormat::RGBA8888, width, height); - WebPPictureImportRGBA(&pic, reinterpret_cast(rgba.data()), static_cast(width * sizeof(ImagePixel::RGBA8888))); + WebPPictureImportRGBA(&pic, reinterpret_cast(rgba.data()), static_cast(width * sizeof(ImagePixelV2::RGBA8888))); } WebPMemoryWriter writer; @@ -2093,19 +2093,19 @@ std::vector ImageConversion::gammaCorrectImageData(std::span(imageData.data()), imageData.size() / sizeof(ImagePixel::InputType)}; \ - std::span outSpan{reinterpret_cast(out.data()), out.size() / sizeof(ImagePixel::InputType)}; \ - std::transform(std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), outSpan.begin(), [gammaLUTs](ImagePixel::InputType pixel) -> ImagePixel::InputType { \ - using PIXEL_TYPE = ImagePixel::InputType; \ - return __VA_ARGS__; \ + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::InputType)}; \ + std::span outSpan{reinterpret_cast(out.data()), out.size() / sizeof(ImagePixelV2::InputType)}; \ + std::transform(std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), outSpan.begin(), [gammaLUTs](ImagePixelV2::InputType pixel) -> ImagePixelV2::InputType { \ + using PIXEL_TYPE = ImagePixelV2::InputType; \ + return PIXEL_TYPE(__VA_ARGS__); \ }) #else #define VTFPP_GAMMA_CORRECT(InputType, ...) \ - std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::InputType)}; \ - std::span outSpan{reinterpret_cast(out.data()), out.size() / sizeof(ImagePixel::InputType)}; \ - std::transform(imageDataSpan.begin(), imageDataSpan.end(), outSpan.begin(), [gammaLUTs](ImagePixel::InputType pixel) -> ImagePixel::InputType { \ - using PIXEL_TYPE = ImagePixel::InputType; \ - return __VA_ARGS__; \ + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::InputType)}; \ + std::span outSpan{reinterpret_cast(out.data()), out.size() / sizeof(ImagePixelV2::InputType)}; \ + std::transform(imageDataSpan.begin(), imageDataSpan.end(), outSpan.begin(), [gammaLUTs](ImagePixelV2::InputType pixel) -> ImagePixelV2::InputType { \ + using PIXEL_TYPE = ImagePixelV2::InputType; \ + return PIXEL_TYPE(__VA_ARGS__); \ }) #endif #define VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(InputType, ...) \ @@ -2113,23 +2113,23 @@ std::vector ImageConversion::gammaCorrectImageData(std::span ImageConversion::invertGreenChannelForImageData(std::span } #define VTFPP_INVERT_GREEN(PixelType, ChannelName, ...) \ - static constexpr auto channelSize = ImageFormatDetails::green(ImagePixel::PixelType::FORMAT); \ - std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::PixelType)}; \ - std::span outSpan{reinterpret_cast(out.data()), out.size() / sizeof(ImagePixel::PixelType)}; \ - std::transform(__VA_ARGS__ __VA_OPT__(,) imageDataSpan.begin(), imageDataSpan.end(), outSpan.begin(), [](ImagePixel::PixelType pixel) -> ImagePixel::PixelType { \ - if constexpr (std::same_as || std::same_as) { \ - pixel.ChannelName = static_cast(static_cast(static_cast(1) << channelSize) - 1.f - static_cast(pixel.ChannelName)); \ + static constexpr auto channelSize = ImageFormatDetails::green(ImagePixelV2::PixelType::FORMAT); \ + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::PixelType)}; \ + std::span outSpan{reinterpret_cast(out.data()), out.size() / sizeof(ImagePixelV2::PixelType)}; \ + std::transform(__VA_ARGS__ __VA_OPT__(,) imageDataSpan.begin(), imageDataSpan.end(), outSpan.begin(), [](ImagePixelV2::PixelType pixel) -> ImagePixelV2::PixelType { \ + if constexpr (std::same_as || std::same_as) { \ + pixel.set_##ChannelName(static_cast(static_cast(static_cast(1) << channelSize) - 1.f - static_cast(pixel.ChannelName()))); \ } else { \ if constexpr (channelSize >= sizeof(uint32_t) * 8) { \ - pixel.ChannelName = static_cast((static_cast(1) << channelSize) - 1 - static_cast(pixel.ChannelName)); \ + pixel.set_##ChannelName(static_cast((static_cast(1) << channelSize) - 1 - static_cast(pixel.ChannelName()))); \ } else { \ - pixel.ChannelName = static_cast(static_cast(1 << channelSize) - 1 - static_cast(pixel.ChannelName)); \ + pixel.set_##ChannelName(static_cast(static_cast(1 << channelSize) - 1 - static_cast(pixel.ChannelName()))); \ } \ } \ return pixel; \ diff --git a/src/vtfpp/ImageQuantize.cpp b/src/vtfpp/ImageQuantize.cpp index ebe2fdf83..2af6976a8 100644 --- a/src/vtfpp/ImageQuantize.cpp +++ b/src/vtfpp/ImageQuantize.cpp @@ -3,14 +3,14 @@ using namespace vtfpp; std::vector ImageQuantize::convertP8ImageDataToBGRA8888(std::span paletteData, std::span imageData) { - if (paletteData.size() != 256 * sizeof(ImagePixel::BGRA8888)) { + if (paletteData.size() != 256 * sizeof(ImagePixelV2::BGRA8888)) { return {}; } - std::span palettePixelData{reinterpret_cast(paletteData.data()), 256}; + std::span palettePixelData{reinterpret_cast(paletteData.data()), 256}; std::vector out; - out.resize(imageData.size() * sizeof(ImagePixel::BGRA8888)); + out.resize(imageData.size() * sizeof(ImagePixelV2::BGRA8888)); BufferStream stream{out}; for (const auto index : imageData) { stream << palettePixelData[static_cast(index)]; diff --git a/src/vtfpp/VTF.cpp b/src/vtfpp/VTF.cpp index 7ccaac28c..004ccbb0a 100644 --- a/src/vtfpp/VTF.cpp +++ b/src/vtfpp/VTF.cpp @@ -567,7 +567,7 @@ VTF::VTF(std::vector&& vtfData, bool parseHeaderOnly) this->resources.push_back({ .type = Resource::TYPE_PALETTE_DATA, .flags = Resource::FLAG_NONE, - .data = stream.read_span(256 * sizeof(ImagePixel::BGRA8888)), + .data = stream.read_span(256 * sizeof(ImagePixelV2::BGRA8888)), }); } @@ -1288,9 +1288,9 @@ void VTF::setReflectivity(sourcepp::math::Vec3f newReflectivity) { void VTF::computeReflectivity() { static constexpr auto getReflectivityForImage = [](const VTF& vtf, uint16_t frame, uint8_t face, uint16_t slice) { - static constexpr auto getReflectivityForPixel = [](const ImagePixel::RGBA8888* pixel) -> math::Vec3f { + static constexpr auto getReflectivityForPixel = [](const ImagePixelV2::RGBA8888* pixel) -> math::Vec3f { // http://markjstock.org/doc/gsd_talk_11_notes.pdf page 11 - math::Vec3f ref{static_cast(pixel->r), static_cast(pixel->g), static_cast(pixel->b)}; + math::Vec3f ref{static_cast(pixel->r()), static_cast(pixel->g()), static_cast(pixel->b())}; ref /= 255.f * 0.9f; ref[0] *= ref[0]; ref[1] *= ref[1]; @@ -1301,9 +1301,9 @@ void VTF::computeReflectivity() { auto rgba8888Data = vtf.getImageDataAsRGBA8888(0, frame, face, slice); math::Vec3f out{}; for (uint64_t i = 0; i < rgba8888Data.size(); i += 4) { - out += getReflectivityForPixel(reinterpret_cast(rgba8888Data.data() + i)); + out += getReflectivityForPixel(reinterpret_cast(rgba8888Data.data() + i)); } - return out / (rgba8888Data.size() / sizeof(ImagePixel::RGBA8888)); + return out / (rgba8888Data.size() / sizeof(ImagePixelV2::RGBA8888)); }; const auto faceCount = this->getFaceCount(); diff --git a/test/vtfpp.cpp b/test/vtfpp.cpp index b3d3b290f..2aae571d3 100644 --- a/test/vtfpp.cpp +++ b/test/vtfpp.cpp @@ -1169,7 +1169,7 @@ TEST(vtfpp, read_xbox_p8) { const auto* palette = vtf.getResource(Resource::TYPE_PALETTE_DATA); ASSERT_TRUE(palette); EXPECT_EQ(palette->flags, Resource::FLAG_NONE); - EXPECT_EQ(palette->data.size(), 256 * sizeof(ImagePixel::BGRA8888)); + EXPECT_EQ(palette->data.size(), 256 * sizeof(ImagePixelV2::BGRA8888)); const auto* fallback = vtf.getResource(Resource::TYPE_FALLBACK_DATA); ASSERT_TRUE(fallback); From 4688d229fddfcf529981f03253766d1a1457b5d3 Mon Sep 17 00:00:00 2001 From: yuuko Date: Sun, 14 Sep 2025 16:46:09 -0700 Subject: [PATCH 21/32] vtfpp: all usage of ImagePixel replaced by ImagePixelV2 --- include/vtfpp/ImageConversion.h | 28 +++++++++++++-- src/vtfpp/ImageConversion.cpp | 63 +++++++++++++++++---------------- 2 files changed, 59 insertions(+), 32 deletions(-) diff --git a/include/vtfpp/ImageConversion.h b/include/vtfpp/ImageConversion.h index f34b2ec04..0496c8bea 100644 --- a/include/vtfpp/ImageConversion.h +++ b/include/vtfpp/ImageConversion.h @@ -20,7 +20,7 @@ using sourcepp::bits::ui16le; namespace vtfpp { -namespace ImagePixel { +namespace [[deprecated("Not portable & subject to removal; use ImagePixelV2")]] ImagePixel { #define VTFPP_CHECK_SIZE(format) \ static_assert(sizeof(format) == ImageFormatDetails::bpp(ImageFormat::format) / 8) @@ -400,6 +400,7 @@ namespace ImagePixelV2 { class N { \ SOURCEPP_FOREACH1(VTFPP_DECLARE_SIMPLE_CHANNEL, T, __VA_ARGS__) \ public: \ + using CHANTYPE = T; \ static constexpr auto FORMAT = ImageFormat::N; \ constexpr N(SOURCEPP_FOREACH1_SEP(SOURCEPP_THUNK_COMMA, VTFPP_TAKE_CHANNEL, T, __VA_ARGS__)) { \ SOURCEPP_FOREACH0(VTFPP_SET_CHANNEL, __VA_ARGS__) \ @@ -443,6 +444,7 @@ namespace ImagePixelV2 { VTFPP_DECLARE_BITS_OFFS(SOURCEPP_REVERSE(__VA_ARGS__)) \ SOURCEPP_FOREACH1(VTFPP_DECLARE_BITS_CHANNEL_UNPACK, T, __VA_ARGS__) \ public: \ + using CHANTYPE = T; \ static constexpr auto FORMAT = ImageFormat::N; \ constexpr N(SOURCEPP_FOREACH1_SEP(SOURCEPP_THUNK_COMMA, VTFPP_TAKE_CHANNEL_UNPACK, T, __VA_ARGS__)) { \ SOURCEPP_FOREACH0(VTFPP_SET_CHANNEL_UNPACK, __VA_ARGS__) \ @@ -570,12 +572,34 @@ void setResizedDims(uint16_t& width, ResizeMethod widthResize, uint16_t& height, /// Invert the green channel. Meant for converting normal maps between OpenGL and DirectX formats [[nodiscard]] std::vector invertGreenChannelForImageData(std::span imageData, ImageFormat format, uint16_t width, uint16_t height); +/// Extracts a single channel from the given image data. +/// May have unexpected behavior if called on bit-packed formats like BGRA5551! +/// Data is packed according to the return types of the pixel format's accessors, as determined to be high enough to hold any channel. +/// (e.g. in the case of BGRA5551's green channel, you'll get 3 high bits of 0-padding, and 5 low bits of actual channel data. +/// With, say, RGBA1010102, you'll get 2 bytes even for the alpha channel, as CHANTYPE is big enough to hold red/green/blue.) +template +[[nodiscard]] std::vector extractChannelFromImageDataV2(std::span imageData, typename P::CHANTYPE (P::*accessor)() const) { + using C = P::CHANTYPE; + if (imageData.empty() || imageData.size() % sizeof(P) != 0) { + return {}; + } + + std::span pixels{reinterpret_cast(imageData.data()), imageData.size() / sizeof(P)}; + + std::vector out(imageData.size() / sizeof(P) * sizeof(C)); + BufferStream stream{out, false}; + for (const auto& pixel : pixels) { + stream << (pixel.*accessor)(); + } + return out; +} + /// Extracts a single channel from the given image data. /// May have unexpected behavior if called on formats that use bitfields like BGRA5551! /// Data is packed according to pixel channel C++ type size /// (e.g. in the case of BGRA5551's green channel, it'll be 2 bytes per green value despite only 5 bits being used in the original data) template -[[nodiscard]] std::vector extractChannelFromImageData(std::span imageData, auto P::*channel) { +[[deprecated("Use extractChannelFromImageDataV2")]] [[nodiscard]] std::vector extractChannelFromImageData(std::span imageData, auto P::*channel) { using C = sourcepp::member_type_t; if (imageData.empty() || imageData.size() % sizeof(P) != 0) { return {}; diff --git a/src/vtfpp/ImageConversion.cpp b/src/vtfpp/ImageConversion.cpp index fbb5d2854..c194c841b 100644 --- a/src/vtfpp/ImageConversion.cpp +++ b/src/vtfpp/ImageConversion.cpp @@ -24,6 +24,7 @@ #define QOI_NO_STDIO #include +#include #include #include @@ -56,6 +57,8 @@ using namespace sourcepp; using namespace vtfpp; +using bits::ui16le; + namespace { [[nodiscard]] constexpr CMP_FORMAT imageFormatToCompressonatorFormat(ImageFormat format) { @@ -1314,15 +1317,15 @@ std::vector ImageConversion::convertImageDataToFile(std::span ImageConversion::convertImageDataToFile(std::span ImageConversion::convertFileToImageData(std::span(stbImage, dirOffset); + return apngDecoder.template operator()(stbImage, dirOffset); } } else { const ::stb_ptr stbImage{ @@ -1800,7 +1803,7 @@ std::vector ImageConversion::convertFileToImageData(std::span(stbImage, dirOffset); + return apngDecoder.template operator()(stbImage, dirOffset); } } } @@ -1842,47 +1845,47 @@ std::vector ImageConversion::convertFileToImageData(std::span out(ImageFormatDetails::getDataLength(format, width, height)); - std::span outPixels{reinterpret_cast(out.data()), out.size() / sizeof(ImagePixel::RGBA16161616)}; + std::span outPixels{reinterpret_cast(out.data()), out.size() / sizeof(ImagePixelV2::RGBA16161616)}; switch (channels) { case 1: { - std::span inPixels{reinterpret_cast(stbImage.get()), outPixels.size()}; + std::span inPixels{reinterpret_cast(stbImage.get()), outPixels.size()}; std::transform( #ifdef SOURCEPP_BUILD_WITH_TBB std::execution::par_unseq, #endif - inPixels.begin(), inPixels.end(), outPixels.begin(), [](uint16_t pixel) -> ImagePixel::RGBA16161616 { - return {pixel, 0, 0, 0xffff}; + inPixels.begin(), inPixels.end(), outPixels.begin(), [](uint16_t pixel) -> ImagePixelV2::RGBA16161616 { + return ImagePixelV2::RGBA16161616(pixel, 0, 0, 0xffff); }); return out; } case 2: { struct RG1616 { - uint16_t r; - uint16_t g; + ui16le r; + ui16le g; }; std::span inPixels{reinterpret_cast(stbImage.get()), outPixels.size()}; std::transform( #ifdef SOURCEPP_BUILD_WITH_TBB std::execution::par_unseq, #endif - inPixels.begin(), inPixels.end(), outPixels.begin(), [](RG1616 pixel) -> ImagePixel::RGBA16161616 { - return {pixel.r, pixel.g, 0, 0xffff}; + inPixels.begin(), inPixels.end(), outPixels.begin(), [](RG1616 pixel) -> ImagePixelV2::RGBA16161616 { + return ImagePixelV2::RGBA16161616(pixel.r, pixel.g, 0, 0xffff); }); return out; } case 3: { struct RGB161616 { - uint16_t r; - uint16_t g; - uint16_t b; + ui16le r; + ui16le g; + ui16le b; }; std::span inPixels{reinterpret_cast(stbImage.get()), outPixels.size()}; std::transform( #ifdef SOURCEPP_BUILD_WITH_TBB std::execution::par_unseq, #endif - inPixels.begin(), inPixels.end(), outPixels.begin(), [](RGB161616 pixel) -> ImagePixel::RGBA16161616 { - return {pixel.r, pixel.g, pixel.b, 0xffff}; + inPixels.begin(), inPixels.end(), outPixels.begin(), [](RGB161616 pixel) -> ImagePixelV2::RGBA16161616 { + return ImagePixelV2::RGBA16161616(pixel.r, pixel.g, pixel.b, 0xffff); }); return out; } From a530687490a3146d345a9a4cf02f14eaaae5a587 Mon Sep 17 00:00:00 2001 From: yuuko Date: Mon, 15 Sep 2025 03:47:31 -0700 Subject: [PATCH 22/32] vtfpp: ImagePixelV2 simple declarations: store LERep; callable as base type --- include/vtfpp/ImageConversion.h | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/include/vtfpp/ImageConversion.h b/include/vtfpp/ImageConversion.h index 0496c8bea..95d7dae88 100644 --- a/include/vtfpp/ImageConversion.h +++ b/include/vtfpp/ImageConversion.h @@ -331,6 +331,11 @@ concept PixelType = namespace ImagePixelV2 { +namespace detail { +template +using RepOrSelf = std::conditional_t>; +} + // TODO: run this through a filter that calls the compiler with comment-preserving preprocessing; // doxygen can't really cope even with its preprocessing turned as indiscriminate as possible. #define VTFPP_PIXFMTS \ @@ -354,18 +359,18 @@ namespace ImagePixelV2 { VTFPP_PIXFMT(BGRA5551, VTFPP_DECLARE_BITS, uint8_t, 16, (b, 5), (g, 5), (r, 5), (a, 1)) \ VTFPP_PIXFMT(UV88, VTFPP_DECLARE_SIMPLE, uint8_t, u, v) \ VTFPP_PIXFMT(UVWQ8888, VTFPP_DECLARE_SIMPLE, uint8_t, u, v, w, q) \ - VTFPP_PIXFMT(RGBA16161616F, VTFPP_DECLARE_SIMPLE, f16le, r, g, b, a) \ - VTFPP_PIXFMT(RGBA16161616, VTFPP_DECLARE_SIMPLE, ui16le, r, g, b, a) \ + VTFPP_PIXFMT(RGBA16161616F, VTFPP_DECLARE_SIMPLE, half, r, g, b, a) \ + VTFPP_PIXFMT(RGBA16161616, VTFPP_DECLARE_SIMPLE, uint16_t, r, g, b, a) \ VTFPP_PIXFMT(UVLX8888, VTFPP_DECLARE_SIMPLE, uint8_t, u, v, l, x) \ - VTFPP_PIXFMT(R32F, VTFPP_DECLARE_SIMPLE, f32le, r) \ - VTFPP_PIXFMT(RGB323232F, VTFPP_DECLARE_SIMPLE, f32le, r, g, b) \ - VTFPP_PIXFMT(RGBA32323232F, VTFPP_DECLARE_SIMPLE, f32le, r, g, b, a) \ - VTFPP_PIXFMT(RG1616F, VTFPP_DECLARE_SIMPLE, f16le, r, g) \ - VTFPP_PIXFMT(RG3232F, VTFPP_DECLARE_SIMPLE, f32le, r, g) \ + VTFPP_PIXFMT(R32F, VTFPP_DECLARE_SIMPLE, float, r) \ + VTFPP_PIXFMT(RGB323232F, VTFPP_DECLARE_SIMPLE, float, r, g, b) \ + VTFPP_PIXFMT(RGBA32323232F, VTFPP_DECLARE_SIMPLE, float, r, g, b, a) \ + VTFPP_PIXFMT(RG1616F, VTFPP_DECLARE_SIMPLE, half, r, g) \ + VTFPP_PIXFMT(RG3232F, VTFPP_DECLARE_SIMPLE, float, r, g) \ VTFPP_PIXFMT(RGBX8888, VTFPP_DECLARE_SIMPLE, uint8_t, r, g, b, x) \ VTFPP_PIXFMT(RGBA1010102, VTFPP_DECLARE_BITS, uint16_t, 32, (r, 10), (g, 10), (b, 10), (a, 2)) \ VTFPP_PIXFMT(BGRA1010102, VTFPP_DECLARE_BITS, uint16_t, 32, (b, 10), (g, 10), (r, 10), (a, 2)) \ - VTFPP_PIXFMT(R16F, VTFPP_DECLARE_SIMPLE, f16le, r) \ + VTFPP_PIXFMT(R16F, VTFPP_DECLARE_SIMPLE, half, r) \ VTFPP_PIXFMT(R8, VTFPP_DECLARE_SIMPLE, uint8_t, r) \ VTFPP_PIXFMT(CONSOLE_BGRX8888_LINEAR, VTFPP_DECLARE_INHERIT, BGRX8888) \ VTFPP_PIXFMT(CONSOLE_RGBA8888_LINEAR, VTFPP_DECLARE_INHERIT, RGBA8888) \ @@ -385,7 +390,7 @@ namespace ImagePixelV2 { #define VTFPP_DECLARE_SIMPLE_CHANNEL(T, C) \ private: \ - T _##C; \ + detail::RepOrSelf _##C; \ public: \ constexpr T C() const { return this->_##C; } \ constexpr void set_##C(T i##C) { this->_##C = i##C; } From 8cbcea68f1ff26aa5615e2bb9a3bdda1dce1482c Mon Sep 17 00:00:00 2001 From: yuuko Date: Mon, 15 Sep 2025 03:47:59 -0700 Subject: [PATCH 23/32] vtfpp: add image format tests --- test/vtfpp.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/test/vtfpp.cpp b/test/vtfpp.cpp index 2aae571d3..0ade8e623 100644 --- a/test/vtfpp.cpp +++ b/test/vtfpp.cpp @@ -193,6 +193,54 @@ 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) +#define GETCHAN(o, c) \ + static_cast(o.c()) + +// the input images are 2x2 composites of 16x16 tiles with r/g/b/a increasing/decreasing +// in distinctive orders to catch out any byte order issues. +#define TEST_READ_EXTFMT_BLOCK_RGBA(LE, GE) do { \ + EXPECT_##GE(GETCHAN(pixels[i], r), GETCHAN(pixels[i+1], r)); \ + EXPECT_##LE(GETCHAN(pixels[i], g), GETCHAN(pixels[i+1], g)); \ + EXPECT_##GE(GETCHAN(pixels[i], b), GETCHAN(pixels[i+1], b)); \ + EXPECT_##LE(GETCHAN(pixels[i], a), GETCHAN(pixels[i+1], a)); \ +} while (0) + +#define TEST_READ_EXTFMT_BLOCK_RGB(LE, GE) do { \ + EXPECT_##GE(GETCHAN(pixels[i], r), GETCHAN(pixels[i+1], r)); \ + EXPECT_##LE(GETCHAN(pixels[i], g), GETCHAN(pixels[i+1], g)); \ + EXPECT_##LE(GETCHAN(pixels[i], b), GETCHAN(pixels[i+1], b)); \ +} while (0) + +#define TEST_READ_EXTFMT(Chan, Rest, Ext) \ + TEST(vtfpp, read_extfmt_##Chan##Rest##_##Ext) { \ + using PIXFMT = ImagePixelV2::Chan##Rest; \ + VTF::CreationOptions options { \ + .outputFormat = VTF::FORMAT_UNCHANGED, \ + }; \ + auto vtf = VTF::create(ASSET_ROOT "vtfpp/extfmt/" #Chan #Rest "." #Ext, options); \ + ASSERT_TRUE(vtf); \ + EXPECT_EQ(vtf.getFormat(), ImageFormat::Chan##Rest); \ + EXPECT_EQ(vtf.getWidth(), 32); \ + EXPECT_EQ(vtf.getHeight(), 32); \ + auto rawspan = vtf.getImageDataRaw(); \ + auto pixels = std::span(reinterpret_cast(rawspan.data()), rawspan.size() / sizeof(PIXFMT)); \ + for (size_t i = 0; i < 15; i++) { \ + TEST_READ_EXTFMT_BLOCK_##Chan(LT, GT); \ + } \ + for (size_t i = 16 * 32; i < 15 + 16 * 32; i++) { \ + TEST_READ_EXTFMT_BLOCK_##Chan(GT, LT); \ + } \ + } + +TEST_READ_EXTFMT(RGB, 888, png) +TEST_READ_EXTFMT(RGB, 888, qoi) +TEST_READ_EXTFMT(RGBA, 16161616, png) +TEST_READ_EXTFMT(RGBA, 32323232F, exr) +TEST_READ_EXTFMT(RGBA, 8888, png) +TEST_READ_EXTFMT(RGBA, 8888, qoi) +TEST_READ_EXTFMT(RGBA, 8888, tga) +TEST_READ_EXTFMT(RGBA, 8888, webp) + #endif TEST(vtfpp, write_non_po2) { From 7ae949ec06dd8081778914b1fd3d89eb5e334b7f Mon Sep 17 00:00:00 2001 From: yuuko Date: Mon, 15 Sep 2025 18:36:26 -0700 Subject: [PATCH 24/32] sourcepp: why was this public in the first place --- include/sourcepp/Bits.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/sourcepp/Bits.h b/include/sourcepp/Bits.h index 6a4fdc531..6c292bf8a 100644 --- a/include/sourcepp/Bits.h +++ b/include/sourcepp/Bits.h @@ -16,7 +16,7 @@ namespace sourcepp::bits { /// /// Special provisions for arithmetic outside possible implicit conversions are intentionally omitted. template -class LERep : public std::array { +class LERep : private std::array { using uint_according = typename std::conditional { uint16_t, uint8_t>::type>::type>::type; public: + /// Conversion to native byte order. /// `LERep` ⇒ `A`. constexpr operator A() const { From 1d9a33ea654cb1e261ec326284f16e8075497a38 Mon Sep 17 00:00:00 2001 From: yuuko Date: Mon, 15 Sep 2025 18:36:54 -0700 Subject: [PATCH 25/32] vtfpp: fix qoi parse on BE --- src/vtfpp/ImageConversion.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vtfpp/ImageConversion.cpp b/src/vtfpp/ImageConversion.cpp index c194c841b..9c40c6f21 100644 --- a/src/vtfpp/ImageConversion.cpp +++ b/src/vtfpp/ImageConversion.cpp @@ -57,7 +57,7 @@ using namespace sourcepp; using namespace vtfpp; -using bits::ui16le; +using namespace ::sourcepp::bits; namespace { @@ -1810,7 +1810,7 @@ std::vector ImageConversion::convertFileToImageData(std::span= 26 && *reinterpret_cast(fileData.data()) == parser::binary::makeFourCC("qoif")) { + if (fileData.size() >= 26 && deref_as_le(fileData.data()) == parser::binary::makeFourCC("qoif")) { qoi_desc descriptor; const ::stb_ptr qoiImage{ static_cast(qoi_decode(fileData.data(), fileData.size(), &descriptor, 0)), From 7ba4aea6fde1e47d04f88defd2ad95db312cf561 Mon Sep 17 00:00:00 2001 From: yuuko Date: Mon, 15 Sep 2025 22:33:53 -0700 Subject: [PATCH 26/32] vtfpp: lift execution conditionals to a dedicated macro; deduplicate accordingly --- include/sourcepp/Macros.h | 10 +++ src/vtfpp/ImageConversion.cpp | 154 ++++++++-------------------------- src/vtfpp/VTF.cpp | 6 +- 3 files changed, 45 insertions(+), 125 deletions(-) diff --git a/include/sourcepp/Macros.h b/include/sourcepp/Macros.h index 490415db7..64c1db821 100644 --- a/include/sourcepp/Macros.h +++ b/include/sourcepp/Macros.h @@ -138,3 +138,13 @@ #define SOURCEPP_CAR(a, d) a /// Called bare to destructure the second of a 2-tuple. #define SOURCEPP_CDR(a, d) d + +#define SOURCEPP_CALL_WITH_POLICY_IF_TBB(ident, policy, ...) ident(__VA_ARGS__) +#ifdef SOURCEPP_BUILD_WITH_TBB +# undef SOURCEPP_CALL_WITH_POLICY_IF_TBB +# if __has_include() +# define SOURCEPP_CALL_WITH_POLICY_IF_TBB(ident, policy, ...) ident(policy __VA_OPT__(,) __VA_ARGS__) +# else +# define SOURCEPP_CALL_WITH_POLICY_IF_TBB(...) static_assert(false, "This macro needs present.") +# endif +#endif diff --git a/src/vtfpp/ImageConversion.cpp b/src/vtfpp/ImageConversion.cpp index 9c40c6f21..3de0a6553 100644 --- a/src/vtfpp/ImageConversion.cpp +++ b/src/vtfpp/ImageConversion.cpp @@ -1,5 +1,4 @@ #include - #include #include #include @@ -370,16 +369,12 @@ namespace { #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, ...) \ + #define VTFPP_CONVERT(InputType, r, g, b, a) \ std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::InputType)}; \ - std::transform(__VA_ARGS__ __VA_OPT__(,) imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::InputType pixel) -> ImagePixelV2::RGBA8888 { \ + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::InputType pixel) -> ImagePixelV2::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::par_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 @@ -422,7 +417,6 @@ namespace { #undef VTFPP_CASE_CONVERT_AND_BREAK #undef VTFPP_CONVERT - #undef VTFPP_CONVERT_DETAIL #undef VTFPP_REMAP_TO_8 return newData; @@ -445,19 +439,12 @@ namespace { #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(ImagePixelV2::InputType)}; \ - std::transform(std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA8888 pixel) -> ImagePixelV2::InputType { \ - return ImagePixelV2::InputType(__VA_ARGS__); \ - }) -#else #define VTFPP_CONVERT(InputType, ...) \ std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::InputType)}; \ - std::transform(imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA8888 pixel) -> ImagePixelV2::InputType { \ + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA8888 pixel) -> ImagePixelV2::InputType { \ return ImagePixelV2::InputType(__VA_ARGS__); \ }) -#endif + #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, ...) \ case InputType: { VTFPP_CONVERT(InputType, __VA_ARGS__); } break @@ -522,16 +509,12 @@ namespace { #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, ...) \ + #define VTFPP_CONVERT(InputType, r, g, b, a, ...) \ std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::InputType)}; \ - std::transform(__VA_ARGS__ __VA_OPT__(,) imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::InputType pixel) -> ImagePixelV2::RGBA16161616 { \ + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::InputType pixel) -> ImagePixelV2::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::par_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 @@ -573,7 +556,6 @@ namespace { #undef VTFPP_CONVERT_REMAP #undef VTFPP_CASE_CONVERT_AND_BREAK #undef VTFPP_CONVERT - #undef VTFPP_CONVERT_DETAIL #undef VTFPP_REMAP_TO_16 return newData; @@ -596,19 +578,12 @@ namespace { #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(ImagePixelV2::InputType)}; \ - std::transform(std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA16161616 pixel) -> ImagePixelV2::InputType { \ + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA16161616 pixel) -> ImagePixelV2::InputType { \ return __VA_ARGS__; \ }) -#else - #define VTFPP_CONVERT(InputType, ...) \ - std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::InputType)}; \ - std::transform(imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA16161616 pixel) -> ImagePixelV2::InputType { \ - return __VA_ARGS__; \ - }) -#endif + #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, ...) \ case InputType: { VTFPP_CONVERT(InputType, __VA_ARGS__); } break @@ -640,14 +615,10 @@ namespace { newData.resize(imageData.size() / (ImageFormatDetails::bpp(format) / 8) * sizeof(ImagePixelV2::RGBA32323232F)); std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::RGBA32323232F)}; - #define VTFPP_CONVERT_DETAIL(InputType, r, g, b, a, ...) \ + #define VTFPP_CONVERT(InputType, r, g, b, a, ...) \ std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::InputType)}; \ - std::transform(__VA_ARGS__ __VA_OPT__(,) imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::InputType pixel) -> ImagePixelV2::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::par_unseq) -#else - #define VTFPP_CONVERT(InputType, r, g, b, a) VTFPP_CONVERT_DETAIL(InputType, r, g, b, a) -#endif + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::InputType pixel) -> ImagePixelV2::RGBA32323232F { return {(r), (g), (b), (a)}; }) + #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, r, g, b, a) \ case InputType: { VTFPP_CONVERT(InputType, r, g, b, a); } break @@ -664,7 +635,6 @@ namespace { #undef VTFPP_CASE_CONVERT_AND_BREAK #undef VTFPP_CONVERT - #undef VTFPP_CONVERT_DETAIL return newData; } @@ -684,19 +654,12 @@ namespace { std::vector newData; newData.resize(imageData.size() / sizeof(ImagePixelV2::RGBA32323232F) * (ImageFormatDetails::bpp(format) / 8)); -#ifdef SOURCEPP_BUILD_WITH_TBB #define VTFPP_CONVERT(InputType, ...) \ std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::InputType)}; \ - std::transform(std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA32323232F pixel) -> ImagePixelV2::InputType { \ + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA32323232F pixel) -> ImagePixelV2::InputType { \ return __VA_ARGS__; \ }) -#else - #define VTFPP_CONVERT(InputType, ...) \ - std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::InputType)}; \ - std::transform(imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA32323232F pixel) -> ImagePixelV2::InputType { \ - return __VA_ARGS__; \ - }) -#endif + #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, ...) \ case InputType: { VTFPP_CONVERT(InputType, __VA_ARGS__); } break @@ -727,10 +690,7 @@ namespace { std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::RGBA32323232F)}; std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA8888)}; - std::transform( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::par_unseq, -#endif + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA8888 pixel) -> ImagePixelV2::RGBA32323232F { return { static_cast(pixel.r()) / static_cast((1 << 8) - 1), @@ -753,10 +713,7 @@ namespace { std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::RGBA8888)}; std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA32323232F)}; - std::transform( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::par_unseq, -#endif + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA32323232F pixel) -> ImagePixelV2::RGBA8888 { return { static_cast(std::clamp(pixel.r(), 0.f, 1.f) * ((1 << 8) - 1)), @@ -779,10 +736,7 @@ namespace { std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::RGBA16161616)}; std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA8888)}; - std::transform( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::par_unseq, -#endif + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA8888 pixel) -> ImagePixelV2::RGBA16161616 { return { math::remap(pixel.r(), (1 << 8) - 1, (1 << 16) - 1), @@ -805,10 +759,7 @@ namespace { std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::RGBA8888)}; std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA16161616)}; - std::transform( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::par_unseq, -#endif + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA16161616 pixel) -> ImagePixelV2::RGBA8888 { return { static_cast(math::remap(pixel.r(), (1 << 16) - 1, (1 << 8) - 1)), @@ -831,10 +782,7 @@ namespace { std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::RGBA16161616)}; std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA32323232F)}; - std::transform( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::par_unseq, -#endif + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA32323232F pixel) -> ImagePixelV2::RGBA16161616 { return { static_cast(std::clamp(pixel.r(), 0.f, 1.f) * ((1 << 16) - 1)), @@ -857,10 +805,7 @@ namespace { std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixelV2::RGBA32323232F)}; std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA16161616)}; - std::transform( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::par_unseq, -#endif + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixelV2::RGBA16161616 pixel) -> ImagePixelV2::RGBA32323232F { return { static_cast(pixel.r()) / static_cast((1 << 16) - 1), @@ -1513,7 +1458,7 @@ std::vector ImageConversion::convertFileToImageData(std::span { const auto channelCount = hasRed + hasGreen + hasBlue + hasAlpha; - std::span out{reinterpret_cast(combinedChannels.data()), combinedChannels.size() / sizeof(C)}; + std::span out{reinterpret_cast*>(combinedChannels.data()), combinedChannels.size() / sizeof(C)}; if (header.tiled) { for (int t = 0; t < image.num_tiles; t++) { auto** src = reinterpret_cast(image.tiles[t].images); @@ -1648,10 +1593,7 @@ std::vector ImageConversion::convertFileToImageData(std::span dst, uint32_t dstWidth, uint32_t dstHeight, std::span src, uint32_t srcWidth, uint32_t srcHeight, uint32_t srcOffsetX, uint32_t srcOffsetY) { for (uint32_t y = 0; y < srcHeight; y++) { - std::copy( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::unseq, -#endif + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::copy, std::execution::unseq, src.data() + calcPixelOffset( 0, y, srcWidth), src.data() + calcPixelOffset( srcWidth, y, srcWidth), dst.data() + calcPixelOffset(srcOffsetX, srcOffsetY + y, dstWidth)); @@ -1661,10 +1603,7 @@ std::vector ImageConversion::convertFileToImageData(std::span dst, std::span src, uint32_t imgWidth, uint32_t imgHeight, uint32_t subWidth, uint32_t subHeight, uint32_t subOffsetX, uint32_t subOffsetY) { for (uint32_t y = subOffsetY; y < subOffsetY + subHeight; y++) { - std::copy( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::unseq, -#endif + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::copy, std::execution::unseq, src.data() + calcPixelOffset(subOffsetX, y, imgWidth), src.data() + calcPixelOffset(subOffsetX + subWidth, y, imgWidth), dst.data() + calcPixelOffset(subOffsetX, y, imgWidth)); @@ -1673,10 +1612,7 @@ std::vector ImageConversion::convertFileToImageData(std::span dst, uint32_t dstWidth, uint32_t dstHeight, uint32_t clrWidth, uint32_t clrHeight, uint32_t clrOffsetX, uint32_t clrOffsetY) { for (uint32_t y = 0; y < clrHeight; y++) { - std::transform( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::unseq, -#endif + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::unseq, dst.data() + calcPixelOffset(clrOffsetX, clrOffsetY + y, dstWidth), dst.data() + calcPixelOffset(clrOffsetX, clrOffsetY + y, dstWidth) + (clrWidth * sizeof(P)), dst.data() + calcPixelOffset(clrOffsetX, clrOffsetY + y, dstWidth), @@ -1849,10 +1785,7 @@ std::vector ImageConversion::convertFileToImageData(std::span inPixels{reinterpret_cast(stbImage.get()), outPixels.size()}; - std::transform( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::par_unseq, -#endif + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, inPixels.begin(), inPixels.end(), outPixels.begin(), [](uint16_t pixel) -> ImagePixelV2::RGBA16161616 { return ImagePixelV2::RGBA16161616(pixel, 0, 0, 0xffff); }); @@ -1864,10 +1797,7 @@ std::vector ImageConversion::convertFileToImageData(std::span inPixels{reinterpret_cast(stbImage.get()), outPixels.size()}; - std::transform( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::par_unseq, -#endif + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, inPixels.begin(), inPixels.end(), outPixels.begin(), [](RG1616 pixel) -> ImagePixelV2::RGBA16161616 { return ImagePixelV2::RGBA16161616(pixel.r, pixel.g, 0, 0xffff); }); @@ -1880,10 +1810,7 @@ std::vector ImageConversion::convertFileToImageData(std::span inPixels{reinterpret_cast(stbImage.get()), outPixels.size()}; - std::transform( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::par_unseq, -#endif + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, inPixels.begin(), inPixels.end(), outPixels.begin(), [](RGB161616 pixel) -> ImagePixelV2::RGBA16161616 { return ImagePixelV2::RGBA16161616(pixel.r, pixel.g, pixel.b, 0xffff); }); @@ -2094,23 +2021,14 @@ std::vector ImageConversion::gammaCorrectImageData(std::span out(imageData.size()); -#ifdef SOURCEPP_BUILD_WITH_TBB #define VTFPP_GAMMA_CORRECT(InputType, ...) \ std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::InputType)}; \ std::span outSpan{reinterpret_cast(out.data()), out.size() / sizeof(ImagePixelV2::InputType)}; \ - std::transform(std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), outSpan.begin(), [gammaLUTs](ImagePixelV2::InputType pixel) -> ImagePixelV2::InputType { \ + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), outSpan.begin(), [gammaLUTs](ImagePixelV2::InputType pixel) -> ImagePixelV2::InputType { \ using PIXEL_TYPE = ImagePixelV2::InputType; \ return PIXEL_TYPE(__VA_ARGS__); \ }) -#else - #define VTFPP_GAMMA_CORRECT(InputType, ...) \ - std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::InputType)}; \ - std::span outSpan{reinterpret_cast(out.data()), out.size() / sizeof(ImagePixelV2::InputType)}; \ - std::transform(imageDataSpan.begin(), imageDataSpan.end(), outSpan.begin(), [gammaLUTs](ImagePixelV2::InputType pixel) -> ImagePixelV2::InputType { \ - using PIXEL_TYPE = ImagePixelV2::InputType; \ - return PIXEL_TYPE(__VA_ARGS__); \ - }) -#endif + #define VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(InputType, ...) \ case InputType: { VTFPP_CREATE_GAMMA_LUTS(InputType) VTFPP_GAMMA_CORRECT(InputType, __VA_ARGS__); } break @@ -2157,11 +2075,11 @@ std::vector ImageConversion::invertGreenChannelForImageData(std::span return convertImageDataToFormat(invertGreenChannelForImageData(convertImageDataToFormat(imageData, format, container, width, height), container, width, height), container, format, width, height); } - #define VTFPP_INVERT_GREEN(PixelType, ChannelName, ...) \ + #define VTFPP_INVERT_GREEN(PixelType, ChannelName) \ static constexpr auto channelSize = ImageFormatDetails::green(ImagePixelV2::PixelType::FORMAT); \ std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::PixelType)}; \ std::span outSpan{reinterpret_cast(out.data()), out.size() / sizeof(ImagePixelV2::PixelType)}; \ - std::transform(__VA_ARGS__ __VA_OPT__(,) imageDataSpan.begin(), imageDataSpan.end(), outSpan.begin(), [](ImagePixelV2::PixelType pixel) -> ImagePixelV2::PixelType { \ + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, imageDataSpan.begin(), imageDataSpan.end(), outSpan.begin(), [](ImagePixelV2::PixelType pixel) -> ImagePixelV2::PixelType { \ if constexpr (std::same_as || std::same_as) { \ pixel.set_##ChannelName(static_cast(static_cast(static_cast(1) << channelSize) - 1.f - static_cast(pixel.ChannelName()))); \ } else { \ @@ -2173,17 +2091,11 @@ std::vector ImageConversion::invertGreenChannelForImageData(std::span } \ return pixel; \ }) -#ifdef SOURCEPP_BUILD_WITH_TBB - #define VTFPP_INVERT_GREEN_CASE(PixelType) \ - case ImageFormat::PixelType: { VTFPP_INVERT_GREEN(PixelType, g, std::execution::par_unseq); break; } - #define VTFPP_INVERT_GREEN_CASE_CA_OVERRIDE(PixelType, ChannelName) \ - case ImageFormat::PixelType: { VTFPP_INVERT_GREEN(PixelType, ChannelName, std::execution::par_unseq); break; } -#else + #define VTFPP_INVERT_GREEN_CASE(PixelType) \ case ImageFormat::PixelType: { VTFPP_INVERT_GREEN(PixelType, g); } break #define VTFPP_INVERT_GREEN_CASE_CA_OVERRIDE(PixelType, ChannelName) \ case ImageFormat::PixelType: { VTFPP_INVERT_GREEN(PixelType, ChannelName); } break -#endif std::vector out(imageData.size()); switch (format) { diff --git a/src/vtfpp/VTF.cpp b/src/vtfpp/VTF.cpp index 004ccbb0a..ab3d82b8f 100644 --- a/src/vtfpp/VTF.cpp +++ b/src/vtfpp/VTF.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -142,10 +143,7 @@ void swapImageDataEndianForConsole(std::span imageData, ImageFormat f case DXT5: case UV88: { std::span dxtData{reinterpret_cast(imageData.data()), imageData.size() / sizeof(uint16_t)}; - std::for_each( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::par_unseq, -#endif + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::for_each, std::execution::par_unseq, dxtData.begin(), dxtData.end(), [](uint16_t& value) { BufferStream::swap_endian(&value); }); From 2e9f78bebed34c10bc87d2bce96c20f65bdafcba Mon Sep 17 00:00:00 2001 From: yuuko Date: Mon, 15 Sep 2025 23:46:53 -0700 Subject: [PATCH 27/32] vtfpp: make 16 bit stb reads portable --- src/vtfpp/ImageConversion.cpp | 89 +++++++++++++++++------------------ 1 file changed, 42 insertions(+), 47 deletions(-) diff --git a/src/vtfpp/ImageConversion.cpp b/src/vtfpp/ImageConversion.cpp index 3de0a6553..077281ac7 100644 --- a/src/vtfpp/ImageConversion.cpp +++ b/src/vtfpp/ImageConversion.cpp @@ -1775,54 +1775,49 @@ std::vector ImageConversion::convertFileToImageData(std::span= 1 && channels < 4) { - // There are no other 16-bit integer formats in Source, so we have to do a conversion here - format = ImageFormat::RGBA16161616; - std::vector out(ImageFormatDetails::getDataLength(format, width, height)); - std::span outPixels{reinterpret_cast(out.data()), out.size() / sizeof(ImagePixelV2::RGBA16161616)}; - switch (channels) { - case 1: { - std::span inPixels{reinterpret_cast(stbImage.get()), outPixels.size()}; - SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, - inPixels.begin(), inPixels.end(), outPixels.begin(), [](uint16_t pixel) -> ImagePixelV2::RGBA16161616 { - return ImagePixelV2::RGBA16161616(pixel, 0, 0, 0xffff); - }); - return out; - } - case 2: { - struct RG1616 { - ui16le r; - ui16le g; - }; - std::span inPixels{reinterpret_cast(stbImage.get()), outPixels.size()}; - SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, - inPixels.begin(), inPixels.end(), outPixels.begin(), [](RG1616 pixel) -> ImagePixelV2::RGBA16161616 { - return ImagePixelV2::RGBA16161616(pixel.r, pixel.g, 0, 0xffff); - }); - return out; - } - case 3: { - struct RGB161616 { - ui16le r; - ui16le g; - ui16le b; - }; - std::span inPixels{reinterpret_cast(stbImage.get()), outPixels.size()}; - SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, - inPixels.begin(), inPixels.end(), outPixels.begin(), [](RGB161616 pixel) -> ImagePixelV2::RGBA16161616 { - return ImagePixelV2::RGBA16161616(pixel.r, pixel.g, pixel.b, 0xffff); - }); - return out; - } - default: - return {}; - } - } else { - return {}; + format = ImageFormat::RGBA16161616; + + std::vector out(ImageFormatDetails::getDataLength(format, width, height)); + std::span outPixels{reinterpret_cast(out.data()), out.size() / sizeof(ImagePixelV2::RGBA16161616)}; + + const auto populateBuffer = [&stbImage, &outPixels]() { + // yes, ordinary uint16. stb gives us a buffer in host order. + std::span> inPixels{reinterpret_cast*>(stbImage.get()), outPixels.size()}; + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, + inPixels.begin(), inPixels.end(), outPixels.begin(), [](std::array pixel) -> ImagePixelV2::RGBA16161616 { + static_assert(sizeof(pixel) == sizeof(uint16_t) * channels0); + uint16_t r = pixel[0], g = 0, b = 0, a = 0xffff; + + if constexpr (channels0 > 1) { + g = pixel[1]; + } + if constexpr (channels0 > 2) { + b = pixel[2]; + } + if constexpr (channels0 > 3) { + a = pixel[3]; + } + + return ImagePixelV2::RGBA16161616(r, g, b, a); + }); + }; + + switch(channels) { + case 1: + populateBuffer.operator()<1>(); + return out; + case 2: + populateBuffer.operator()<2>(); + return out; + case 3: + populateBuffer.operator()<3>(); + return out; + case 4: + populateBuffer.operator()<4>(); + return out; } - return {reinterpret_cast(stbImage.get()), reinterpret_cast(stbImage.get()) + ImageFormatDetails::getDataLength(format, width, height)}; + + return {}; } // 8-bit or less single frame image From 45fa7a145db3ee85a38cdc901ac6bdf6ccbdcc6f Mon Sep 17 00:00:00 2001 From: yuuko Date: Tue, 16 Sep 2025 00:31:17 -0700 Subject: [PATCH 28/32] vtfpp: add vtf read order checks to tests as well --- test/vtfpp.cpp | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/test/vtfpp.cpp b/test/vtfpp.cpp index 0ade8e623..395b5c40a 100644 --- a/test/vtfpp.cpp +++ b/test/vtfpp.cpp @@ -211,13 +211,10 @@ TEST_WRITE_FMT(UVLX8888, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FL EXPECT_##LE(GETCHAN(pixels[i], b), GETCHAN(pixels[i+1], b)); \ } while (0) -#define TEST_READ_EXTFMT(Chan, Rest, Ext) \ +#define TEST_READ_EXTFMT_DETAIL(Chan, Rest, Ext, Mkvtf) \ TEST(vtfpp, read_extfmt_##Chan##Rest##_##Ext) { \ using PIXFMT = ImagePixelV2::Chan##Rest; \ - VTF::CreationOptions options { \ - .outputFormat = VTF::FORMAT_UNCHANGED, \ - }; \ - auto vtf = VTF::create(ASSET_ROOT "vtfpp/extfmt/" #Chan #Rest "." #Ext, options); \ + Mkvtf(ASSET_ROOT "vtfpp/extfmt/" #Chan #Rest "." #Ext); \ ASSERT_TRUE(vtf); \ EXPECT_EQ(vtf.getFormat(), ImageFormat::Chan##Rest); \ EXPECT_EQ(vtf.getWidth(), 32); \ @@ -232,6 +229,18 @@ TEST_WRITE_FMT(UVLX8888, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FL } \ } +#define GETEXT(Path) \ + VTF::CreationOptions options { \ + .outputFormat = VTF::FORMAT_UNCHANGED, \ + }; \ + VTF vtf = VTF::create(Path, options) + +#define GETVTF(Path) \ + VTF vtf(Path) + +#define TEST_READ_EXTFMT(Chan, Rest, Ext) TEST_READ_EXTFMT_DETAIL(Chan, Rest, Ext, GETEXT) +#define TEST_READ_EXTFMT_VTF(Chan, Rest) TEST_READ_EXTFMT_DETAIL(Chan, Rest, vtf, GETVTF) + TEST_READ_EXTFMT(RGB, 888, png) TEST_READ_EXTFMT(RGB, 888, qoi) TEST_READ_EXTFMT(RGBA, 16161616, png) @@ -241,6 +250,11 @@ TEST_READ_EXTFMT(RGBA, 8888, qoi) TEST_READ_EXTFMT(RGBA, 8888, tga) TEST_READ_EXTFMT(RGBA, 8888, webp) +TEST_READ_EXTFMT_VTF(RGB, 888) +TEST_READ_EXTFMT_VTF(RGBA, 8888) +TEST_READ_EXTFMT_VTF(RGBA, 16161616) +TEST_READ_EXTFMT_VTF(RGBA, 32323232F) + #endif TEST(vtfpp, write_non_po2) { From 2d300ffad108ae242074ac5f2e05618859ee4874 Mon Sep 17 00:00:00 2001 From: yuuko Date: Tue, 16 Sep 2025 01:06:35 -0700 Subject: [PATCH 29/32] vtfpp: give extfmt_img tests corresponding roundtrip attempts --- test/vtfpp.cpp | 71 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/test/vtfpp.cpp b/test/vtfpp.cpp index 395b5c40a..d0f4f7161 100644 --- a/test/vtfpp.cpp +++ b/test/vtfpp.cpp @@ -211,10 +211,10 @@ TEST_WRITE_FMT(UVLX8888, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FL EXPECT_##LE(GETCHAN(pixels[i], b), GETCHAN(pixels[i+1], b)); \ } while (0) -#define TEST_READ_EXTFMT_DETAIL(Chan, Rest, Ext, Mkvtf) \ - TEST(vtfpp, read_extfmt_##Chan##Rest##_##Ext) { \ +#define TEST_READ_EXTFMT_DETAIL(Type, Chan, Rest, Ext, Mkvtf) \ + TEST(vtfpp, read_extfmt_##Type##_##Chan##Rest##_##Ext) { \ using PIXFMT = ImagePixelV2::Chan##Rest; \ - Mkvtf(ASSET_ROOT "vtfpp/extfmt/" #Chan #Rest "." #Ext); \ + Mkvtf(vtf, Chan, Rest, Ext); \ ASSERT_TRUE(vtf); \ EXPECT_EQ(vtf.getFormat(), ImageFormat::Chan##Rest); \ EXPECT_EQ(vtf.getWidth(), 32); \ @@ -229,31 +229,52 @@ TEST_WRITE_FMT(UVLX8888, VTF::FLAG_NO_MIP | VTF::FLAG_NO_LOD | VTF::FL } \ } -#define GETEXT(Path) \ +#define DEFPATH(Chan, Rest, Ext) \ + ASSET_ROOT "vtfpp/extfmt/" #Chan #Rest "." #Ext + +#define GETEXT(As, Chan, Rest, Ext) \ VTF::CreationOptions options { \ .outputFormat = VTF::FORMAT_UNCHANGED, \ }; \ - VTF vtf = VTF::create(Path, options) - -#define GETVTF(Path) \ - VTF vtf(Path) - -#define TEST_READ_EXTFMT(Chan, Rest, Ext) TEST_READ_EXTFMT_DETAIL(Chan, Rest, Ext, GETEXT) -#define TEST_READ_EXTFMT_VTF(Chan, Rest) TEST_READ_EXTFMT_DETAIL(Chan, Rest, vtf, GETVTF) - -TEST_READ_EXTFMT(RGB, 888, png) -TEST_READ_EXTFMT(RGB, 888, qoi) -TEST_READ_EXTFMT(RGBA, 16161616, png) -TEST_READ_EXTFMT(RGBA, 32323232F, exr) -TEST_READ_EXTFMT(RGBA, 8888, png) -TEST_READ_EXTFMT(RGBA, 8888, qoi) -TEST_READ_EXTFMT(RGBA, 8888, tga) -TEST_READ_EXTFMT(RGBA, 8888, webp) - -TEST_READ_EXTFMT_VTF(RGB, 888) -TEST_READ_EXTFMT_VTF(RGBA, 8888) -TEST_READ_EXTFMT_VTF(RGBA, 16161616) -TEST_READ_EXTFMT_VTF(RGBA, 32323232F) + VTF As = VTF::create(DEFPATH(Chan, Rest, Ext), options) + +#define GETVTF(As, Chan, Rest, Ext) \ + VTF As(DEFPATH(Chan, Rest, Ext)) + +#define GETROUND(As, Chan, Rest, Ext) \ + GETEXT(roundtmp, Chan, Rest, Ext); \ + ASSERT_TRUE(roundtmp); \ + const auto bakedpath = "roundtrip_" #Chan #Rest "_" #Ext ".vtf"; \ + ASSERT_TRUE(roundtmp.bake(bakedpath)); \ + VTF As(bakedpath) + +#define TEST_READ_EXTFMT_IMG(Chan, Rest, Ext) TEST_READ_EXTFMT_DETAIL(img, Chan, Rest, Ext, GETEXT) +#define TEST_READ_EXTFMT_TEX(Chan, Rest) TEST_READ_EXTFMT_DETAIL(tex, Chan, Rest, vtf, GETVTF) + +#define EXTFMT_CASES \ + EXTFMT_CASE(RGB, 888, png) \ + EXTFMT_CASE(RGB, 888, qoi) \ + EXTFMT_CASE(RGBA, 16161616, png) \ + EXTFMT_CASE(RGBA, 32323232F, exr) \ + EXTFMT_CASE(RGBA, 8888, png) \ + EXTFMT_CASE(RGBA, 8888, qoi) \ + EXTFMT_CASE(RGBA, 8888, tga) \ + EXTFMT_CASE(RGBA, 8888, webp) + +#define EXTFMT_CASE TEST_READ_EXTFMT_IMG + EXTFMT_CASES +#undef EXTFMT_CASE + +TEST_READ_EXTFMT_TEX(RGB, 888) +TEST_READ_EXTFMT_TEX(RGBA, 8888) +TEST_READ_EXTFMT_TEX(RGBA, 16161616) +TEST_READ_EXTFMT_TEX(RGBA, 32323232F) + +#define TEST_READ_EXTFMT_ROUNDTRIP(Chan, Rest, Ext) TEST_READ_EXTFMT_DETAIL(roundtrip, Chan, Rest, Ext, GETROUND) + +#define EXTFMT_CASE TEST_READ_EXTFMT_ROUNDTRIP + EXTFMT_CASES +#undef EXTFMT_CASE #endif From 5d037794f6351edadb721adfbc4d4a06978cb6de Mon Sep 17 00:00:00 2001 From: yuuko Date: Tue, 16 Sep 2025 08:48:51 -0700 Subject: [PATCH 30/32] vtfpp: back to native during resize --- src/vtfpp/ImageConversion.cpp | 91 +++++++++++++++++++++++++++++------ 1 file changed, 76 insertions(+), 15 deletions(-) diff --git a/src/vtfpp/ImageConversion.cpp b/src/vtfpp/ImageConversion.cpp index 077281ac7..60eeebaa6 100644 --- a/src/vtfpp/ImageConversion.cpp +++ b/src/vtfpp/ImageConversion.cpp @@ -60,6 +60,45 @@ using namespace ::sourcepp::bits; namespace { +template +using Subpixel = std::array; + +template +static Subpixel swizzleSubpixel(const Subpixel inpx) { + Subpixel ret; + std::copy(inpx.crbegin(), inpx.crend(), ret.begin()); + return ret; +} + +template +static void swizzleSrcDst(std::span src, std::span dst) { + auto inputs = std::span>{reinterpret_cast *>(src.data()), src.size() / Chansize}; + auto outputs = std::span>{reinterpret_cast *>(dst.data()), dst.size() / Chansize}; + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::transform, std::execution::par_unseq, inputs.begin(), inputs.end(), outputs.begin(), &swizzleSubpixel); +} + +template +static void swizzleInPlace(std::span src) { + auto inputs = std::span>{reinterpret_cast *>(src.data()), src.size() / Chansize}; + SOURCEPP_CALL_WITH_POLICY_IF_TBB(std::for_each, std::execution::par_unseq, inputs.begin(), inputs.end(), [](Subpixel &modpx) { modpx = swizzleSubpixel(modpx); }); +} + +struct SwizzleCtxSTB { + size_t channels; + std::span buf; +}; + +template +static const void *swizzle_stbir_input(void *optional_output, const void *input_ptr, int num_pixels, int x, int y, void *context) { + auto ctx = reinterpret_cast(context); + auto num_subpixels = ctx->channels * num_pixels; + auto src = std::span{reinterpret_cast(input_ptr), num_subpixels * Chansize}; + + swizzleSrcDst(src, ctx->buf); + + return ctx->buf.data(); +} + [[nodiscard]] constexpr CMP_FORMAT imageFormatToCompressonatorFormat(ImageFormat format) { switch (format) { using enum ImageFormat; @@ -295,15 +334,9 @@ namespace { // compressonator seems to do absolutely everything right *except* writing output per native-order dword. oh well. if constexpr (std::endian::native == std::endian::big) { - auto t = imageData.size(); - swizzle.resize(t); - for (size_t i = 0; i < t; i += 4) { - swizzle[i] = imageData[i + 3]; - swizzle[i + 1] = imageData[i + 2]; - swizzle[i + 2] = imageData[i + 1]; - swizzle[i + 3] = imageData[i]; - } - swizzledInput = std::span(swizzle); + swizzle.resize(imageData.size()); + swizzleSrcDst<4>(imageData, swizzle); + swizzledInput = std::span{swizzle}; } CMP_Texture srcTexture{}; @@ -342,11 +375,7 @@ namespace { // "i dont like it but it works" if constexpr (std::endian::native == std::endian::big) { - auto t = destData.size(); - for (size_t i = 0; i < t; i += 4) { - std::swap(destData[i], destData[i + 3]); - std::swap(destData[i + 2], destData[i + 1]); - } + swizzleInPlace<4>(destData); } return destData; @@ -1859,7 +1888,7 @@ std::vector ImageConversion::resizeImageData(std::span(edge), static_cast(edge)); switch (filter) { case ResizeFilter::DEFAULT: @@ -1917,7 +1946,39 @@ std::vector ImageConversion::resizeImageData(std::span{}; + auto ctx = SwizzleCtxSTB { + .channels = 0, + .buf = std::span{swizzleBuf}, + }; + if constexpr (std::endian::native == std::endian::big) { + if (size_t bpc = ImageFormatDetails::red(format); bpc > 8) { + ctx.channels = ImageFormatDetails::bpp(format) / bpc; + swizzleBuf.resize(bpc * ctx.channels * width); + ctx.buf = std::span{swizzleBuf}; + switch ((chansize = bpc / 8)) { +# define VTFPP_CASE_AND_SET(n) \ + case n: \ + stbir_set_user_data(&resize, &ctx); \ + stbir_set_pixel_callbacks(&resize, &swizzle_stbir_input, NULL); \ + break; + SOURCEPP_FOREACH0(VTFPP_CASE_AND_SET, 2, 4, 8) +# undef VTFPP_CASE_AND_SET + } + } + } stbir_resize_extended(&resize); + if constexpr (std::endian::native == std::endian::big) { + switch (chansize) { +# define VTFPP_CASE_AND_SWIZZLE(n) \ + case n: \ + swizzleInPlace(std::span{reinterpret_cast(resize.output_pixels), ImageFormatDetails::getDataLength(format, newWidth, newHeight)}); \ + break; + SOURCEPP_FOREACH0(VTFPP_CASE_AND_SWIZZLE, 2, 4, 8) +# undef VTFPP_CASE_AND_SET + } + } }; const auto pixelLayout = ::imageFormatToSTBIRPixelLayout(format); From 72083c970ea7ad5723c61a8e079a6a551c37796d Mon Sep 17 00:00:00 2001 From: yuuko Date: Wed, 17 Sep 2025 06:46:21 -0700 Subject: [PATCH 31/32] vtfpp: compressed write tests --- test/vtfpp.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/vtfpp.cpp b/test/vtfpp.cpp index d0f4f7161..45d672744 100644 --- a/test/vtfpp.cpp +++ b/test/vtfpp.cpp @@ -276,6 +276,23 @@ TEST_READ_EXTFMT_TEX(RGBA, 32323232F) EXTFMT_CASES #undef EXTFMT_CASE +#define TEST_WRITE_EXTFMT(Chan, Rest, Ext, To) \ + TEST(vtfpp, write_extfmt_##Chan##Rest##_##Ext##_to_##To) { \ + VTF::CreationOptions options { \ + .outputFormat = ImageFormat::To, \ + }; \ + VTF vtf = VTF::create(DEFPATH(Chan, Rest, Ext), options); \ + ASSERT_TRUE(vtf.bake("write_" #Chan #Rest "_to_" #To ".vtf")); \ + } + +TEST_WRITE_EXTFMT(RGB, 888, png, DXT1) +TEST_WRITE_EXTFMT(RGB, 888, qoi, DXT1) +TEST_WRITE_EXTFMT(RGBA, 8888, png, BC7) +TEST_WRITE_EXTFMT(RGBA, 8888, png, DXT5) +TEST_WRITE_EXTFMT(RGBA, 8888, png, DXT1_ONE_BIT_ALPHA) +TEST_WRITE_EXTFMT(RGBA, 16161616, png, DXT5) +TEST_WRITE_EXTFMT(RGBA, 32323232F, exr, DXT5) + #endif TEST(vtfpp, write_non_po2) { From fcdd59c0768ef43d2b701a4ab82ba58197c7df9a Mon Sep 17 00:00:00 2001 From: yuuko Date: Thu, 18 Sep 2025 01:45:37 -0700 Subject: [PATCH 32/32] fix(ext): cmake indentation --- ext/compressonator/CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/compressonator/CMakeLists.txt b/ext/compressonator/CMakeLists.txt index 092bb3843..f3bf119d6 100644 --- a/ext/compressonator/CMakeLists.txt +++ b/ext/compressonator/CMakeLists.txt @@ -28,12 +28,12 @@ function(target_link_compressonator TARGET) target_link_libraries(${TARGET} PRIVATE "${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCompressonator$<$:d>.a" "${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCMP_Core$<$:d>.a") - if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") - target_link_libraries(${TARGET} PRIVATE - "${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCMP_Core_AVX$<$:d>.a" - "${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCMP_Core_AVX512$<$:d>.a" - "${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCMP_Core_SSE$<$:d>.a") - endif() + if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + target_link_libraries(${TARGET} PRIVATE + "${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCMP_Core_AVX$<$:d>.a" + "${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCMP_Core_AVX512$<$:d>.a" + "${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCMP_Core_SSE$<$:d>.a") + endif() else() message(FATAL_ERROR "Unable to link to Compressonator library!") endif()