diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c459af34..f9cca0eb9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ endif() list(APPEND ${PROJECT_NAME}_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/include/sourcepp/buffer/Buffer.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/sourcepp/math/Angles.h" + "${CMAKE_CURRENT_SOURCE_DIR}/include/sourcepp/math/Float.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/sourcepp/math/Integer.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/sourcepp/math/Matrix.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/sourcepp/math/Vector.h" diff --git a/THIRDPARTY_LEGAL_NOTICES.txt b/THIRDPARTY_LEGAL_NOTICES.txt new file mode 100644 index 000000000..ddd57d7ec --- /dev/null +++ b/THIRDPARTY_LEGAL_NOTICES.txt @@ -0,0 +1,89 @@ +--------------- bufferstream --------------- + +MIT License + +Copyright (c) 2023 Laura Lewis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------- Compressonator --------------- + +Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------- miniz --------------- + +Copyright 2013-2014 RAD Game Tools and Valve Software +Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + +All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +--------------- stb --------------- + +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/include/sourcepp/math/Float.h b/include/sourcepp/math/Float.h new file mode 100644 index 000000000..bf7bd971f --- /dev/null +++ b/include/sourcepp/math/Float.h @@ -0,0 +1,42 @@ +#pragma once + +#include "Integer.h" + +namespace sourcepp::math { + +// https://stackoverflow.com/a/60047308 +class FloatCompressed16 { +public: + explicit FloatCompressed16(uint16_t in) { + this->data = in; + } + + explicit FloatCompressed16(float in) { // IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits + const auto b = *reinterpret_cast(&in) + 0x00001000; // round-to-nearest-even: add last bit after truncated mantissa + const auto e = (b & 0x7F800000) >> 23; // exponent + const auto m = b & 0x007FFFFF; // mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding + this->data = (b & 0x80000000) >> 16 | (e > 112) * ((((e - 112) << 10) & 0x7C00) | m >> 13) | ((e < 113) & (e > 101)) * ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) | (e > 143) * 0x7FFF; // sign : normalized : denormalized : saturate + } + + [[nodiscard]] uint16_t toFloat16() const { + return this->data; + } + + [[nodiscard]] float toFloat32() const { // IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits + const uint32_t e = (this->data & 0x7C00) >> 10; // exponent + const uint32_t m = (this->data & 0x03FF) << 13; // mantissa + const auto mf = static_cast(m); + const uint32_t v = *reinterpret_cast(&mf) >> 23; // evil log2 bit hack to count leading zeros in denormalized format + const uint32_t vu = (this->data & 0x8000) << 16 | (e != 0) * ((e + 112) << 23 | m) | ((e == 0) & (m != 0)) * ((v - 37) << 23 | ((m << (150 - v)) & 0x007FE000)); // sign : normalized : denormalized + return *reinterpret_cast(&vu); + } + + [[nodiscard]] float operator*() const { + return this->toFloat32(); + } + +private: + uint16_t data; +}; + +} // namespace sourcepp::math diff --git a/src/thirdparty/bufferstream b/src/thirdparty/bufferstream index 4741d64b9..fd12a5a50 160000 --- a/src/thirdparty/bufferstream +++ b/src/thirdparty/bufferstream @@ -1 +1 @@ -Subproject commit 4741d64b900830e1dc1bea12c325b618894a1fd7 +Subproject commit fd12a5a506cd1aaec415914e357b3ff26ea6f266 diff --git a/src/vtfpp/ImageConversion.cpp b/src/vtfpp/ImageConversion.cpp index d76a6682b..32ce02a32 100644 --- a/src/vtfpp/ImageConversion.cpp +++ b/src/vtfpp/ImageConversion.cpp @@ -1,10 +1,13 @@ #include +#include #include #include +#include #include +using namespace sourcepp; using namespace vtfpp; namespace { @@ -434,20 +437,143 @@ namespace { return ::convertImageDataUsingCompressonator(imageData, ImageFormat::RGBA8888, format, width, height); } -[[nodiscard]] std::vector convertImageDataToRGBA32323232F(std::span imageData, ImageFormat format, uint16_t width, uint16_t height) { - return ::convertImageDataUsingCompressonator(imageData, format, ImageFormat::RGBA32323232F, width, height); +[[nodiscard]] std::vector convertImageDataToRGBA32323232F(std::span imageData, ImageFormat format) { + std::vector newData; + newData.reserve(imageData.size() / (ImageFormatDetails::bpp(format) / 8) * (ImageFormatDetails::bpp(ImageFormat::RGBA32323232F) / 8)); + switch (format) { + using enum ImageFormat; + case RGBA32323232F: + newData = {imageData.begin(), imageData.end()}; + break; + case RGB323232F: + for (int i = 0; i < imageData.size(); i += 12) { + for (int j = 0; j < 16; j++) { + newData.push_back({}); + } + *reinterpret_cast(&newData[newData.size() - 16]) = *reinterpret_cast(&imageData[i]); + *reinterpret_cast(&newData[newData.size() - 12]) = *reinterpret_cast(&imageData[i + 4]); + *reinterpret_cast(&newData[newData.size() - 8]) = *reinterpret_cast(&imageData[i + 8]); + *reinterpret_cast(&newData[newData.size() - 4]) = 1.f; + } + break; + case R32F: + for (int i = 0; i < imageData.size(); i += 4) { + for (int j = 0; j < 16; j++) { + newData.push_back({}); + } + *reinterpret_cast(&newData[newData.size() - 16]) = *reinterpret_cast(&imageData[i]); + *reinterpret_cast(&newData[newData.size() - 12]) = 0.f; + *reinterpret_cast(&newData[newData.size() - 8]) = 0.f; + *reinterpret_cast(&newData[newData.size() - 4]) = 1.f; + } + break; + case RGBA16161616F: + for (int i = 0; i < imageData.size(); i += 8) { + for (int j = 0; j < 16; j++) { + newData.push_back({}); + } + *reinterpret_cast(&newData[newData.size() - 16]) = *math::FloatCompressed16{*reinterpret_cast(&imageData[i])}; + *reinterpret_cast(&newData[newData.size() - 12]) = *math::FloatCompressed16{*reinterpret_cast(&imageData[i + 2])}; + *reinterpret_cast(&newData[newData.size() - 8]) = *math::FloatCompressed16{*reinterpret_cast(&imageData[i + 4])}; + *reinterpret_cast(&newData[newData.size() - 4]) = *math::FloatCompressed16{*reinterpret_cast(&imageData[i + 6])}; + } + break; + case RGBA16161616: + for (int i = 0; i < imageData.size(); i += 8) { + for (int j = 0; j < 16; j++) { + newData.push_back({}); + } + *reinterpret_cast(&newData[newData.size() - 16]) = static_cast(*reinterpret_cast(&imageData[i])) / 65535.f; + *reinterpret_cast(&newData[newData.size() - 12]) = static_cast(*reinterpret_cast(&imageData[i + 2])) / 65535.f; + *reinterpret_cast(&newData[newData.size() - 8]) = static_cast(*reinterpret_cast(&imageData[i + 4])) / 65535.f; + *reinterpret_cast(&newData[newData.size() - 4]) = static_cast(*reinterpret_cast(&imageData[i + 6])) / 65535.f; + } + break; + default: + break; + } + return newData; } -[[nodiscard]] std::vector convertImageDataFromRGBA32323232F(std::span imageData, ImageFormat format, uint16_t width, uint16_t height) { - return ::convertImageDataUsingCompressonator(imageData, ImageFormat::RGBA32323232F, format, width, height); +[[nodiscard]] std::vector convertImageDataFromRGBA32323232F(std::span imageData, ImageFormat format) { + std::vector newData; + newData.reserve(imageData.size() / (ImageFormatDetails::bpp(ImageFormat::RGBA32323232F) / 8) * (ImageFormatDetails::bpp(format) / 8)); + switch (format) { + using enum ImageFormat; + case RGBA32323232F: + newData = {imageData.begin(), imageData.end()}; + break; + case RGB323232F: + for (int i = 0; i < imageData.size(); i += 16) { + for (int j = 0; j < 12; j++) { + newData.push_back({}); + } + *reinterpret_cast(&newData[newData.size() - 12]) = *reinterpret_cast(&imageData[i]); + *reinterpret_cast(&newData[newData.size() - 8]) = *reinterpret_cast(&imageData[i + 4]); + *reinterpret_cast(&newData[newData.size() - 4]) = *reinterpret_cast(&imageData[i + 8]); + } + break; + case R32F: + for (int i = 0; i < imageData.size(); i += 16) { + for (int j = 0; j < 4; j++) { + newData.push_back({}); + } + *reinterpret_cast(&newData[newData.size() - 4]) = *reinterpret_cast(&imageData[i]); + } + break; + case RGBA16161616F: + for (int i = 0; i < imageData.size(); i += 16) { + for (int j = 0; j < 8; j++) { + newData.push_back({}); + } + *reinterpret_cast(&newData[newData.size() - 8]) = math::FloatCompressed16{*reinterpret_cast(&imageData[i])}.toFloat16(); + *reinterpret_cast(&newData[newData.size() - 6]) = math::FloatCompressed16{*reinterpret_cast(&imageData[i + 4])}.toFloat16(); + *reinterpret_cast(&newData[newData.size() - 4]) = math::FloatCompressed16{*reinterpret_cast(&imageData[i + 8])}.toFloat16(); + *reinterpret_cast(&newData[newData.size() - 2]) = math::FloatCompressed16{*reinterpret_cast(&imageData[i + 12])}.toFloat16(); + } + break; + case RGBA16161616: + for (int i = 0; i < imageData.size(); i += 16) { + for (int j = 0; j < 8; j++) { + newData.push_back({}); + } + *reinterpret_cast(&newData[newData.size() - 8]) = static_cast(std::clamp(*reinterpret_cast(&imageData[i]), 0.f, 1.f) * 65535.f); + *reinterpret_cast(&newData[newData.size() - 6]) = static_cast(std::clamp(*reinterpret_cast(&imageData[i + 4]), 0.f, 1.f) * 65535.f); + *reinterpret_cast(&newData[newData.size() - 4]) = static_cast(std::clamp(*reinterpret_cast(&imageData[i + 8]), 0.f, 1.f) * 65535.f); + *reinterpret_cast(&newData[newData.size() - 2]) = static_cast(std::clamp(*reinterpret_cast(&imageData[i + 12]), 0.f, 1.f) * 65535.f); + } + break; + default: + break; + } + return newData; } -[[nodiscard]] std::vector convertImageDataFromRGBA8888ToRGBA32323232F(std::span imageData, uint16_t width, uint16_t height) { - return ::convertImageDataUsingCompressonator(imageData, ImageFormat::RGBA8888, ImageFormat::RGBA32323232F, width, height); +[[nodiscard]] std::vector convertImageDataFromRGBA8888ToRGBA32323232F(std::span imageData) { + std::vector newData; + newData.reserve(imageData.size() / (ImageFormatDetails::bpp(ImageFormat::RGBA8888) / 8) * (ImageFormatDetails::bpp(ImageFormat::RGBA32323232F) / 8)); + for (int i = 0; i < imageData.size(); i += 4) { + for (int j = 0; j < 16; j++) { + newData.push_back({}); + } + *reinterpret_cast(&newData[newData.size() - 16]) = static_cast(*reinterpret_cast(&imageData[i])) / 255.f; + *reinterpret_cast(&newData[newData.size() - 12]) = static_cast(*reinterpret_cast(&imageData[i + 1])) / 255.f; + *reinterpret_cast(&newData[newData.size() - 8]) = static_cast(*reinterpret_cast(&imageData[i + 2])) / 255.f; + *reinterpret_cast(&newData[newData.size() - 4]) = static_cast(*reinterpret_cast(&imageData[i + 3])) / 255.f; + } + return newData; } -[[nodiscard]] std::vector convertImageDataFromRGBA32323232FToRGBA8888(std::span imageData, uint16_t width, uint16_t height) { - return ::convertImageDataUsingCompressonator(imageData, ImageFormat::RGBA32323232F, ImageFormat::RGBA8888, width, height); +[[nodiscard]] std::vector convertImageDataFromRGBA32323232FToRGBA8888(std::span imageData) { + std::vector newData; + newData.reserve(imageData.size() / (ImageFormatDetails::bpp(ImageFormat::RGBA32323232F) / 8) * (ImageFormatDetails::bpp(ImageFormat::RGBA8888) / 8)); + for (int i = 0; i < imageData.size(); i += 16) { + newData.push_back(static_cast(std::clamp(*reinterpret_cast(&imageData[i]), 0.f, 1.f) * 255)); + newData.push_back(static_cast(std::clamp(*reinterpret_cast(&imageData[i + 4]), 0.f, 1.f) * 255)); + newData.push_back(static_cast(std::clamp(*reinterpret_cast(&imageData[i + 8]), 0.f, 1.f) * 255)); + newData.push_back(static_cast(std::clamp(*reinterpret_cast(&imageData[i + 12]), 0.f, 1.f) * 255)); + } + return newData; } } // namespace @@ -464,17 +590,17 @@ std::vector ImageConversion::convertImageDataToFormat(std::span ImageConversion::convertImageDataToFormat(std::span