diff --git a/CMakeLists.txt b/CMakeLists.txt index 45143c234..a738e27bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,10 @@ project(sourcepp DESCRIPTION "Several modern C++20 libraries for sanely parsing Valve formats." HOMEPAGE_URL "https://craftablescience.info/sourcepp") 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) 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 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/ext/compressonator/CMakeLists.txt b/ext/compressonator/CMakeLists.txt index 22cc64f15..f3bf119d6 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() diff --git a/include/sourcepp/Bits.h b/include/sourcepp/Bits.h new file mode 100644 index 000000000..6c292bf8a --- /dev/null +++ b/include/sourcepp/Bits.h @@ -0,0 +1,102 @@ +#pragma once + +#include + +#include + +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. +/// +/// Special provisions for arithmetic outside possible implicit conversions are intentionally omitted. +template +class LERep : private std::array { + using uint_according = typename std::conditional::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) { + ret |= (static_cast(b) << offs) & (uint_according(0xFFu) << offs); + offs += 8; + } + 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) { + b = static_cast((in >> offs) & uint_according(0xFFu)); + offs += 8; + } + 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()); } + + /// 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: +/// +/// deref_as_le(p) +template +[[nodiscard]] constexpr A deref_as_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) \ + 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/sourcepp/Macros.h b/include/sourcepp/Macros.h index 7d90bcf3b..64c1db821 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,80 @@ 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_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. +/// \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_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() + +/// 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 + +#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/include/vtfpp/ImageConversion.h b/include/vtfpp/ImageConversion.h index 240e14edc..d7954abd1 100644 --- a/include/vtfpp/ImageConversion.h +++ b/include/vtfpp/ImageConversion.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -7,13 +8,19 @@ #include #include +#include +#include #include #include "ImageFormats.h" +using sourcepp::bits::f16le; +using sourcepp::bits::f32le; +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) @@ -157,18 +164,18 @@ struct UVWQ8888 { struct RGBA16161616F { static constexpr auto FORMAT = ImageFormat::RGBA16161616F; - half r; - half g; - half b; - half a; + f16le r; + f16le g; + f16le b; + f16le 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; + ui16le r; + ui16le g; + ui16le b; + ui16le a; }; VTFPP_CHECK_SIZE(RGBA16161616); struct UVLX8888 { @@ -181,34 +188,34 @@ struct UVLX8888 { struct R32F { static constexpr auto FORMAT = ImageFormat::R32F; - float r; + f32le r; }; VTFPP_CHECK_SIZE(R32F); struct RGB323232F { static constexpr auto FORMAT = ImageFormat::R32F; - float r; - float g; - float b; + f32le r; + f32le g; + f32le b; }; VTFPP_CHECK_SIZE(RGB323232F); struct RGBA32323232F { static constexpr auto FORMAT = ImageFormat::RGBA32323232F; - float r; - float g; - float b; - float a; + f32le r; + f32le g; + f32le b; + f32le a; }; VTFPP_CHECK_SIZE(RGBA32323232F); struct RG1616F { static constexpr auto FORMAT = ImageFormat::RG1616F; - half r; - half g; + f16le r; + f16le g; }; VTFPP_CHECK_SIZE(RG1616F); struct RG3232F { static constexpr auto FORMAT = ImageFormat::RG3232F; - float r; - float g; + f32le r; + f32le g; }; VTFPP_CHECK_SIZE(RG3232F); struct RGBX8888 { @@ -322,6 +329,166 @@ concept PixelType = } // namespace ImagePixel +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 \ + 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, 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, 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, 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) \ + 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: \ + detail::RepOrSelf _##C; \ + public: \ + constexpr T C() const { return this->_##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)); + +#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: \ + 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__) \ + } \ + }; 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_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() 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)) { \ + 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) +#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; \ + sourcepp::bits::LERep _repr; \ + 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__) \ + } \ + }; 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_DECLARE_BITS_OFFS_HELPER_THUNK +#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; @@ -410,12 +577,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/include/vtfpp/VTF.h b/include/vtfpp/VTF.h index 3ab78408a..72e5cba01 100644 --- a/include/vtfpp/VTF.h +++ b/include/vtfpp/VTF.h @@ -9,6 +9,7 @@ #include #include +#include #include #include diff --git a/src/vtfpp/ImageConversion.cpp b/src/vtfpp/ImageConversion.cpp index a9ec40a86..5024f2e59 100644 --- a/src/vtfpp/ImageConversion.cpp +++ b/src/vtfpp/ImageConversion.cpp @@ -1,5 +1,4 @@ #include - #include #include #include @@ -24,6 +23,7 @@ #define QOI_NO_STDIO #include +#include #include #include @@ -56,8 +56,49 @@ using namespace sourcepp; using namespace vtfpp; +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; @@ -288,15 +329,25 @@ 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) { + swizzle.resize(imageData.size()); + swizzleSrcDst<4>(imageData, swizzle); + 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 +372,12 @@ 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) { + swizzleInPlace<4>(destData); + } + return destData; } @@ -336,64 +393,59 @@ 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 { \ + #define VTFPP_CONVERT(InputType, r, g, b, a) \ + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::InputType)}; \ + 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 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; } #undef VTFPP_CASE_CONVERT_AND_BREAK #undef VTFPP_CONVERT - #undef VTFPP_CONVERT_DETAIL #undef VTFPP_REMAP_TO_8 return newData; @@ -410,62 +462,55 @@ 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)}; \ + 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__); \ }) -#else - #define VTFPP_CONVERT(InputType, ...) \ - std::span newDataSpan{reinterpret_cast(newData.data()), newData.size() / sizeof(ImagePixel::InputType)}; \ - std::transform(imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA8888 pixel) -> ImagePixel::InputType { \ - return __VA_ARGS__; \ - }) -#endif + #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, ...) \ case InputType: { VTFPP_CONVERT(InputType, __VA_ARGS__); } break switch (format) { using enum ImageFormat; - 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,21 +533,17 @@ 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 { \ + #define VTFPP_CONVERT(InputType, r, g, b, a, ...) \ + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::InputType)}; \ + 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 @@ -534,9 +575,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; } @@ -544,7 +585,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; @@ -561,33 +601,26 @@ 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 { \ - 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)}; \ + 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__; \ }) -#endif + #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, ...) \ case InputType: { VTFPP_CONVERT(InputType, __VA_ARGS__); } break switch (format) { using enum ImageFormat; - VTFPP_CASE_CONVERT_AND_BREAK(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,34 +641,29 @@ 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(InputType, r, g, b, a, ...) \ + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::InputType)}; \ + 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_CONVERT_DETAIL(InputType, r, g, b, a, ...) \ - std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixel::InputType)}; \ - std::transform(__VA_ARGS__ __VA_OPT__(,) imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::InputType pixel) -> ImagePixel::RGBA32323232F { return {(r), (g), (b), (a)}; }) -#ifdef SOURCEPP_BUILD_WITH_TBB - #define VTFPP_CONVERT(InputType, r, g, b, a) VTFPP_CONVERT_DETAIL(InputType, r, g, b, a, std::execution::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 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; } #undef VTFPP_CASE_CONVERT_AND_BREAK #undef VTFPP_CONVERT - #undef VTFPP_CONVERT_DETAIL return newData; } @@ -651,34 +679,27 @@ 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 { \ - 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)}; \ + 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__; \ }) -#endif + #define VTFPP_CASE_CONVERT_AND_BREAK(InputType, ...) \ case InputType: { VTFPP_CONVERT(InputType, __VA_ARGS__); } break switch (format) { using enum ImageFormat; - VTFPP_CASE_CONVERT_AND_BREAK(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 +715,17 @@ 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::transform( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::par_unseq, -#endif - imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA8888 pixel) -> ImagePixel::RGBA32323232F { + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA8888)}; + 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), - 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 +738,17 @@ 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::transform( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::par_unseq, -#endif - imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA32323232F pixel) -> ImagePixel::RGBA8888 { + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA32323232F)}; + 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)), - 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 +761,17 @@ 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::transform( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::par_unseq, -#endif - imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA8888 pixel) -> ImagePixel::RGBA16161616 { + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA8888)}; + 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), - 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 +784,17 @@ 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::transform( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::par_unseq, -#endif - imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA16161616 pixel) -> ImagePixel::RGBA8888 { + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA16161616)}; + 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)), - 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 +807,17 @@ 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::transform( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::par_unseq, -#endif - imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA32323232F pixel) -> ImagePixel::RGBA16161616 { + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA32323232F)}; + 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)), - 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 +830,17 @@ 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::transform( -#ifdef SOURCEPP_BUILD_WITH_TBB - std::execution::par_unseq, -#endif - imageDataSpan.begin(), imageDataSpan.end(), newDataSpan.begin(), [](ImagePixel::RGBA16161616 pixel) -> ImagePixel::RGBA32323232F { + std::span imageDataSpan{reinterpret_cast(imageData.data()), imageData.size() / sizeof(ImagePixelV2::RGBA16161616)}; + 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), - 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), }; }); @@ -958,12 +961,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, @@ -988,8 +991,8 @@ 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)); - std::span face{reinterpret_cast(faceData[i].data()), reinterpret_cast(faceData[i].data() + faceData[i].size())}; + 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++) { for (int col = 0; col < resolution; col++) { @@ -1042,10 +1045,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; } } } @@ -1092,52 +1095,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; @@ -1288,15 +1291,15 @@ std::vector ImageConversion::convertImageDataToFile(std::span ImageConversion::convertImageDataToFile(std::span 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); @@ -1619,10 +1622,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)); @@ -1632,10 +1632,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)); @@ -1644,10 +1641,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), @@ -1766,7 +1760,7 @@ std::vector ImageConversion::convertFileToImageData(std::span(stbImage, dirOffset); + return apngDecoder.template operator()(stbImage, dirOffset); } } else { const ::stb_ptr stbImage{ @@ -1774,14 +1768,14 @@ std::vector ImageConversion::convertFileToImageData(std::span(stbImage, dirOffset); + return apngDecoder.template operator()(stbImage, dirOffset); } } } } // QOI header - if (fileData.size() >= 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)), @@ -1810,63 +1804,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(ImagePixel::RGBA16161616)}; - switch (channels) { - case 1: { - 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}; - }); - return out; - } - case 2: { - struct RG1616 { - uint16_t r; - uint16_t 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}; - }); - return out; - } - case 3: { - struct RGB161616 { - uint16_t r; - uint16_t g; - uint16_t 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}; - }); - 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 @@ -1908,7 +1888,7 @@ std::vector ImageConversion::resizeImageData(std::span(edge), static_cast(edge)); switch (filter) { case ResizeFilter::DEFAULT: @@ -1966,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); @@ -2065,45 +2077,36 @@ 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(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__; \ - }) -#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)}; \ + 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__); \ }) -#endif + #define VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(InputType, ...) \ case InputType: { VTFPP_CREATE_GAMMA_LUTS(InputType) VTFPP_GAMMA_CORRECT(InputType, __VA_ARGS__); } break switch (format) { using enum ImageFormat; - VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(ABGR8888, {pixel.a, VTFPP_APPLY_GAMMA_BLUE(pixel.b), VTFPP_APPLY_GAMMA_GREEN(pixel.g), VTFPP_APPLY_GAMMA_RED(pixel.r)}); - VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(RGB888, {VTFPP_APPLY_GAMMA_RED(pixel.r), VTFPP_APPLY_GAMMA_GREEN(pixel.g), VTFPP_APPLY_GAMMA_BLUE(pixel.b)}); - VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(RGB888_BLUESCREEN, pixel.r == 0 && pixel.g == 0 && pixel.b == 0xff ? ImagePixel::RGB888_BLUESCREEN{0, 0, 0xff} : ImagePixel::RGB888_BLUESCREEN{VTFPP_APPLY_GAMMA_RED(pixel.r), VTFPP_APPLY_GAMMA_GREEN(pixel.g), VTFPP_APPLY_GAMMA_BLUE(pixel.b)}); - VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(BGR888, {VTFPP_APPLY_GAMMA_BLUE(pixel.b), VTFPP_APPLY_GAMMA_GREEN(pixel.g), VTFPP_APPLY_GAMMA_RED(pixel.r)}); - VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(BGR888_BLUESCREEN, pixel.r == 0 && pixel.g == 0 && pixel.b == 0xff ? ImagePixel::BGR888_BLUESCREEN{0, 0, 0xff} : ImagePixel::BGR888_BLUESCREEN{VTFPP_APPLY_GAMMA_BLUE(pixel.b), VTFPP_APPLY_GAMMA_GREEN(pixel.g), VTFPP_APPLY_GAMMA_RED(pixel.r)}); - VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(RGB565, {VTFPP_APPLY_GAMMA_RED(pixel.r), VTFPP_APPLY_GAMMA_GREEN(pixel.g), VTFPP_APPLY_GAMMA_BLUE(pixel.b)}); - VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(I8, {VTFPP_APPLY_GAMMA_RED(pixel.i)}); - VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(IA88, {VTFPP_APPLY_GAMMA_RED(pixel.i), pixel.a}); - VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(ARGB8888, {pixel.a, VTFPP_APPLY_GAMMA_RED(pixel.r), VTFPP_APPLY_GAMMA_GREEN(pixel.g), VTFPP_APPLY_GAMMA_BLUE(pixel.b)}); - VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(BGRA8888, {VTFPP_APPLY_GAMMA_BLUE(pixel.b), VTFPP_APPLY_GAMMA_GREEN(pixel.g), VTFPP_APPLY_GAMMA_RED(pixel.r), pixel.a}); - VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(BGRX8888, {VTFPP_APPLY_GAMMA_BLUE(pixel.b), VTFPP_APPLY_GAMMA_GREEN(pixel.g), VTFPP_APPLY_GAMMA_RED(pixel.r), 0xff}); - VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(BGR565, {VTFPP_APPLY_GAMMA_BLUE(pixel.b), VTFPP_APPLY_GAMMA_GREEN(pixel.g), VTFPP_APPLY_GAMMA_RED(pixel.r)}); - VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(BGRA5551, {VTFPP_APPLY_GAMMA_BLUE(pixel.b), VTFPP_APPLY_GAMMA_GREEN(pixel.g), VTFPP_APPLY_GAMMA_RED(pixel.r), pixel.a}); - VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(BGRX5551, {VTFPP_APPLY_GAMMA_BLUE(pixel.b), VTFPP_APPLY_GAMMA_GREEN(pixel.g), VTFPP_APPLY_GAMMA_RED(pixel.r), 1}); - VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(BGRA4444, {VTFPP_APPLY_GAMMA_BLUE(pixel.b), VTFPP_APPLY_GAMMA_GREEN(pixel.g), VTFPP_APPLY_GAMMA_RED(pixel.r), pixel.a}); - VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(RGBX8888, {VTFPP_APPLY_GAMMA_RED(pixel.r), VTFPP_APPLY_GAMMA_GREEN(pixel.g), VTFPP_APPLY_GAMMA_BLUE(pixel.b), 0xff}); - VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(R8, {VTFPP_APPLY_GAMMA_RED(pixel.r)}); + VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(ABGR8888, pixel.a(), VTFPP_APPLY_GAMMA_BLUE(pixel.b()), VTFPP_APPLY_GAMMA_GREEN(pixel.g()), VTFPP_APPLY_GAMMA_RED(pixel.r())); + VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(RGB888, VTFPP_APPLY_GAMMA_RED(pixel.r()), VTFPP_APPLY_GAMMA_GREEN(pixel.g()), VTFPP_APPLY_GAMMA_BLUE(pixel.b())); + VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(RGB888_BLUESCREEN, pixel.r() == 0 && pixel.g() == 0 && pixel.b() == 0xff ? ImagePixelV2::RGB888_BLUESCREEN{0, 0, 0xff} : ImagePixelV2::RGB888_BLUESCREEN{VTFPP_APPLY_GAMMA_RED(pixel.r()), VTFPP_APPLY_GAMMA_GREEN(pixel.g()), VTFPP_APPLY_GAMMA_BLUE(pixel.b())}); + VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(BGR888, VTFPP_APPLY_GAMMA_BLUE(pixel.b()), VTFPP_APPLY_GAMMA_GREEN(pixel.g()), VTFPP_APPLY_GAMMA_RED(pixel.r())); + VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(BGR888_BLUESCREEN, pixel.r() == 0 && pixel.g() == 0 && pixel.b() == 0xff ? ImagePixelV2::BGR888_BLUESCREEN{0, 0, 0xff} : ImagePixelV2::BGR888_BLUESCREEN{VTFPP_APPLY_GAMMA_BLUE(pixel.b()), VTFPP_APPLY_GAMMA_GREEN(pixel.g()), VTFPP_APPLY_GAMMA_RED(pixel.r())}); + VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(RGB565, VTFPP_APPLY_GAMMA_RED(pixel.r()), VTFPP_APPLY_GAMMA_GREEN(pixel.g()), VTFPP_APPLY_GAMMA_BLUE(pixel.b())); + VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(I8, VTFPP_APPLY_GAMMA_RED(pixel.i())); + VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(IA88, VTFPP_APPLY_GAMMA_RED(pixel.i()), pixel.a()); + VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(ARGB8888, pixel.a(), VTFPP_APPLY_GAMMA_RED(pixel.r()), VTFPP_APPLY_GAMMA_GREEN(pixel.g()), VTFPP_APPLY_GAMMA_BLUE(pixel.b())); + VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(BGRA8888, VTFPP_APPLY_GAMMA_BLUE(pixel.b()), VTFPP_APPLY_GAMMA_GREEN(pixel.g()), VTFPP_APPLY_GAMMA_RED(pixel.r()), pixel.a()); + VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(BGRX8888, VTFPP_APPLY_GAMMA_BLUE(pixel.b()), VTFPP_APPLY_GAMMA_GREEN(pixel.g()), VTFPP_APPLY_GAMMA_RED(pixel.r()), 0xff); + VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(BGR565, VTFPP_APPLY_GAMMA_BLUE(pixel.b()), VTFPP_APPLY_GAMMA_GREEN(pixel.g()), VTFPP_APPLY_GAMMA_RED(pixel.r())); + VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(BGRA5551, VTFPP_APPLY_GAMMA_BLUE(pixel.b()), VTFPP_APPLY_GAMMA_GREEN(pixel.g()), VTFPP_APPLY_GAMMA_RED(pixel.r()), pixel.a()); + VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(BGRX5551, VTFPP_APPLY_GAMMA_BLUE(pixel.b()), VTFPP_APPLY_GAMMA_GREEN(pixel.g()), VTFPP_APPLY_GAMMA_RED(pixel.r()), 1); + VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(BGRA4444, VTFPP_APPLY_GAMMA_BLUE(pixel.b()), VTFPP_APPLY_GAMMA_GREEN(pixel.g()), VTFPP_APPLY_GAMMA_RED(pixel.r()), pixel.a()); + VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(RGBX8888, VTFPP_APPLY_GAMMA_RED(pixel.r()), VTFPP_APPLY_GAMMA_GREEN(pixel.g()), VTFPP_APPLY_GAMMA_BLUE(pixel.b()), 0xff); + VTFPP_CASE_GAMMA_CORRECT_AND_BREAK(R8, VTFPP_APPLY_GAMMA_RED(pixel.r())); default: SOURCEPP_DEBUG_BREAK; break; } @@ -2128,33 +2131,27 @@ 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, ...) \ - 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)); \ + #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)}; \ + 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 { \ 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; \ }) -#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/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 6b831ce0a..e2a0a16c4 100644 --- a/src/vtfpp/VTF.cpp +++ b/src/vtfpp/VTF.cpp @@ -18,11 +18,14 @@ #include #include +#include #include +#include #include #include using namespace sourcepp; +using namespace sourcepp::bits; using namespace vtfpp; namespace { @@ -140,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); }); @@ -233,13 +233,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), deref_as_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 deref_as_le(this->data.data()); case TYPE_LOD_CONTROL_INFO: if (this->data.size() != sizeof(uint32_t)) { return {}; @@ -253,12 +253,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_cast(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_cast(this->data.data())}}; + return HOT{{reinterpret_cast(this->data.data()) + sizeof(uint32_t), deref_as_le(this->data.data())}}; default: break; } @@ -349,7 +349,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 deref_as_le(lhs.data.data()) < deref_as_le(rhs.data.data()); }); // Fix up data spans to point to the actual data @@ -357,8 +357,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 = 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); @@ -368,7 +368,7 @@ VTF::VTF(std::vector&& vtfData, bool parseHeaderOnly) } } if (lastResource) { - const auto offset = *reinterpret_cast(lastResource->data.data()); + const auto offset = deref_as_le(lastResource->data.data()); const auto curPos = stream.tell(); stream.seek(offset); lastResource->data = stream.read_span(stream.size() - offset); @@ -560,7 +560,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) * this->frameCount), + .data = stream.read_span(256 * sizeof(ImagePixelV2::BGRA8888) * this->frameCount), }); } @@ -1281,9 +1281,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]; @@ -1294,9 +1294,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(); @@ -2021,7 +2021,9 @@ std::vector VTF::bake() const { .write(this->frameCount) .write(this->startFrame) .pad() - .write(this->reflectivity) + .write(this->reflectivity[0]) + .write(this->reflectivity[1]) + .write(this->reflectivity[2]) .pad() .write(this->bumpMapScale) .write(bakeFormat) diff --git a/test/vtfpp.cpp b/test/vtfpp.cpp index bdd00f17c..ca46a4e60 100644 --- a/test/vtfpp.cpp +++ b/test/vtfpp.cpp @@ -193,6 +193,106 @@ 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_DETAIL(Type, Chan, Rest, Ext, Mkvtf) \ + TEST(vtfpp, read_extfmt_##Type##_##Chan##Rest##_##Ext) { \ + using PIXFMT = ImagePixelV2::Chan##Rest; \ + Mkvtf(vtf, Chan, Rest, Ext); \ + 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); \ + } \ + } + +#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 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 + +#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) { @@ -1169,7 +1269,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) * vtf.getFrameCount()); + EXPECT_EQ(palette->data.size(), 256 * sizeof(ImagePixelV2::BGRA8888) * vtf.getFrameCount()); const auto* fallback = vtf.getResource(Resource::TYPE_FALLBACK_DATA); ASSERT_TRUE(fallback); @@ -1219,7 +1319,7 @@ TEST(vtfpp, read_xbox_broken) { 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) * vtf.getFrameCount()); + EXPECT_EQ(palette->data.size(), 256 * sizeof(ImagePixelV2::BGRA8888) * vtf.getFrameCount()); const auto* fallback = vtf.getResource(Resource::TYPE_FALLBACK_DATA); ASSERT_TRUE(fallback);