diff --git a/src/Magnum/CMakeLists.txt b/src/Magnum/CMakeLists.txt index a127eba73..37fc5c828 100644 --- a/src/Magnum/CMakeLists.txt +++ b/src/Magnum/CMakeLists.txt @@ -85,6 +85,9 @@ set(MagnumMath_SRCS Math/Packing.cpp Math/instantiation.cpp) +set(MagnumMath_GracefulAssert_SRCS + Math/PackingBatch.cpp) + # Objects shared between main and math test library add_library(MagnumMathObjects OBJECT ${MagnumMath_SRCS}) target_include_directories(MagnumMathObjects PUBLIC @@ -120,6 +123,7 @@ set_target_properties(MagnumObjects PROPERTIES FOLDER "Magnum") add_library(Magnum ${SHARED_OR_STATIC} $ $ + ${MagnumMath_GracefulAssert_SRCS} ${Magnum_GracefulAssert_SRCS}) set_target_properties(Magnum PROPERTIES DEBUG_POSTFIX "-d" @@ -194,11 +198,12 @@ if(BUILD_TESTS) # Math library with graceful assert for testing add_library(MagnumMathTestLib ${SHARED_OR_STATIC} $ - ${PROJECT_SOURCE_DIR}/src/dummy.cpp) # XCode workaround, see file comment for details + ${MagnumMath_GracefulAssert_SRCS}) target_include_directories(MagnumMathTestLib PUBLIC ${PROJECT_SOURCE_DIR}/src ${PROJECT_BINARY_DIR}/src) - target_compile_definitions(MagnumMathTestLib PRIVATE "CORRADE_GRACEFUL_ASSERT") + target_compile_definitions(MagnumMathTestLib PRIVATE + "CORRADE_GRACEFUL_ASSERT" "Magnum_EXPORTS") set_target_properties(MagnumMathTestLib PROPERTIES DEBUG_POSTFIX "-d" FOLDER "Magnum/Math") diff --git a/src/Magnum/Math/Packing.h b/src/Magnum/Math/Packing.h index 7ef6e4076..217f5e990 100644 --- a/src/Magnum/Math/Packing.h +++ b/src/Magnum/Math/Packing.h @@ -67,7 +67,7 @@ value in range @f$ [0, 1] @f$ or from *signed* integral to range @f$ [-1, 1] @f$ @attention @snippet MagnumMath.cpp unpack-template-explicit -@see @ref pack() +@see @ref pack(), @ref unpackInto() */ template inline FloatingPoint unpack(const Integral& value); @@ -133,7 +133,7 @@ given *signed* integral type. @attention Return value for floating point numbers outside the normalized range is undefined. -@see @ref unpack() +@see @ref unpack(), @ref packInto() */ #ifdef DOXYGEN_GENERATING_OUTPUT template inline Integral pack(const FloatingPoint& value); diff --git a/src/Magnum/Math/PackingBatch.cpp b/src/Magnum/Math/PackingBatch.cpp new file mode 100644 index 000000000..cd54fd7aa --- /dev/null +++ b/src/Magnum/Math/PackingBatch.cpp @@ -0,0 +1,242 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + 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. +*/ + +#include "PackingBatch.h" + +#include +#include + +#include "Magnum/Math/Packing.h" + +namespace Magnum { namespace Math { + +namespace { + +template inline void unpackUnsignedIntoImplementation(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + CORRADE_ASSERT(src.size() == dst.size(), + "Math::unpackInto(): wrong destination size, got" << dst.size() << "but expected" << src.size(), ); + CORRADE_ASSERT(src.template isContiguous<1>() && dst.isContiguous<1>(), + "Math::unpackInto(): second view dimension is not contiguous", ); + + /** @todo SSE/NEON implementations exploiting second dimension size/stride, + contiguity etc */ + + /* Caching values to avoid inline function calls in ebug builds */ + constexpr Float bitMax = Implementation::bitMax(); + const char* srcPtr = reinterpret_cast(src.data()); + char* dstPtr = reinterpret_cast(dst.data()); + const std::ptrdiff_t srcStride = src.stride()[0]; + const std::ptrdiff_t dstStride = dst.stride()[0]; + const std::size_t maxJ = src.size()[1]; + for(std::size_t i = 0, maxI = src.size()[0]; i != maxI; ++i) { + const T* srcPtrI = reinterpret_cast(srcPtr); + Float* dstPtrI = reinterpret_cast(dstPtr); + for(std::size_t j = 0; j != maxJ; ++j) + *dstPtrI++ = *srcPtrI++/bitMax; + + srcPtr += srcStride; + dstPtr += dstStride; + } +} + +} + +void unpackInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + unpackUnsignedIntoImplementation(src, dst); +} + +void unpackInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + unpackUnsignedIntoImplementation(src, dst); +} + +namespace { + +template inline void unpackSignedIntoImplementation(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + CORRADE_ASSERT(src.size() == dst.size(), + "Math::unpackInto(): wrong destination size, got" << dst.size() << "but expected" << src.size(), ); + CORRADE_ASSERT(src.template isContiguous<1>() && dst.isContiguous<1>(), + "Math::unpackInto(): second view dimension is not contiguous", ); + + /** @todo SSE/NEON implementations exploiting second dimension size/stride, + contiguity etc */ + + /* Caching values to avoid inline function calls in debug builds */ + constexpr Float bitMax = Implementation::bitMax(); + const char* srcPtr = reinterpret_cast(src.data()); + char* dstPtr = reinterpret_cast(dst.data()); + const std::ptrdiff_t srcStride = src.stride()[0]; + const std::ptrdiff_t dstStride = dst.stride()[0]; + const std::size_t maxJ = src.size()[1]; + for(std::size_t i = 0, maxI = src.size()[0]; i != maxI; ++i) { + const T* srcPtrI = reinterpret_cast(srcPtr); + Float* dstPtrI = reinterpret_cast(dstPtr); + for(std::size_t j = 0; j != maxJ; ++j) { + const Float value = *srcPtrI++/bitMax; + /* Avoiding a max() call in Debug */ + *dstPtrI++ = value < -1.0f ? -1.0f : value; + } + + srcPtr += srcStride; + dstPtr += dstStride; + } +} + +} + +void unpackInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + unpackSignedIntoImplementation(src, dst); +} + +void unpackInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + unpackSignedIntoImplementation(src, dst); +} + +namespace { + +template inline void packIntoImplementation(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + CORRADE_ASSERT(src.size() == dst.size(), + "Math::packInto(): wrong destination size, got" << dst.size() << "but expected" << src.size(), ); + CORRADE_ASSERT(src.isContiguous<1>() && dst.template isContiguous<1>(), + "Math::packInto(): second view dimension is not contiguous", ); + + /** @todo SSE/NEON implementations exploiting second dimension size/stride, + contiguity etc */ + + /* Caching values to avoid inline function calls in debug builds */ + constexpr Float bitMax = Implementation::bitMax(); + const char* srcPtr = reinterpret_cast(src.data()); + char* dstPtr = reinterpret_cast(dst.data()); + const std::ptrdiff_t srcStride = src.stride()[0]; + const std::ptrdiff_t dstStride = dst.stride()[0]; + const std::size_t maxJ = src.size()[1]; + for(std::size_t i = 0, maxI = src.size()[0]; i != maxI; ++i) { + const Float* srcPtrI = reinterpret_cast(srcPtr); + T* dstPtrI = reinterpret_cast(dstPtr); + for(std::size_t j = 0; j != maxJ; ++j) + /** @todo provide a version that doesn't do rounding */ + *dstPtrI++ = std::round(*srcPtrI++*bitMax); + + srcPtr += srcStride; + dstPtr += dstStride; + } +} + +} + +void packInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + packIntoImplementation(src, dst); +} + +void packInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + packIntoImplementation(src, dst); +} + +void packInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + packIntoImplementation(src, dst); +} + +void packInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + packIntoImplementation(src, dst); +} + +namespace { + +template inline void castIntoImplementation(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + CORRADE_ASSERT(src.size() == dst.size(), + "Math::castInto(): wrong destination size, got" << dst.size() << "but expected" << src.size(), ); + CORRADE_ASSERT(src.template isContiguous<1>() && dst.template isContiguous<1>(), + "Math::castInto(): second view dimension is not contiguous", ); + + /** @todo SSE/NEON implementations exploiting second dimension size/stride, + contiguity etc */ + + /* Caching values to avoid inline function calls in debug buílds */ + const char* srcPtr = reinterpret_cast(src.data()); + char* dstPtr = reinterpret_cast(dst.data()); + const std::ptrdiff_t srcStride = src.stride()[0]; + const std::ptrdiff_t dstStride = dst.stride()[0]; + const std::size_t maxJ = src.size()[1]; + for(std::size_t i = 0, maxI = src.size()[0]; i != maxI; ++i) { + const T* srcPtrI = reinterpret_cast(srcPtr); + U* dstPtrI = reinterpret_cast(dstPtr); + for(std::size_t j = 0; j != maxJ; ++j) + *dstPtrI++ = U(*srcPtrI++); + + srcPtr += srcStride; + dstPtr += dstStride; + } +} + +} + +void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + castIntoImplementation(src, dst); +} + +void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + castIntoImplementation(src, dst); +} + +void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + castIntoImplementation(src, dst); +} + +void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + castIntoImplementation(src, dst); +} + +void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + castIntoImplementation(src, dst); +} + +void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + castIntoImplementation(src, dst); +} + +void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + castIntoImplementation(src, dst); +} + +void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + castIntoImplementation(src, dst); +} + +void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + castIntoImplementation(src, dst); +} + +void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + castIntoImplementation(src, dst); +} + +void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + castIntoImplementation(src, dst); +} + +void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst) { + castIntoImplementation(src, dst); +} + +}} diff --git a/src/Magnum/Math/PackingBatch.h b/src/Magnum/Math/PackingBatch.h new file mode 100644 index 000000000..859868446 --- /dev/null +++ b/src/Magnum/Math/PackingBatch.h @@ -0,0 +1,234 @@ +#ifndef Magnum_Math_PackingBatch_h +#define Magnum_Math_PackingBatch_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + 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. +*/ + +/** @file + * @brief Functions @ref Magnum::Math::packInto(), @ref Magnum::Math::unpackInto(), @ref Magnum::Math::castInto() + * @m_since_latest + */ + +#include + +#include "Magnum/Types.h" +#include "Magnum/visibility.h" + +namespace Magnum { namespace Math { + +/** +@{ @name Batch packing functions + +These functions process an ubounded range of values, as opposed to single +vectors or scalars. +*/ + +/** +@brief Unpack unsigned integral values into a floating-point representation +@param[in] src Source integral values +@param[out] dst Destination floating-point values +@m_since_latest + +Converts integral values from full range of given *unsigned* integral type to +floating-point values in range @f$ [0, 1] @f$. Second dimension is meant to +contain vector/matrix components, or have a size of 1 for scalars. Expects that +@p src and @p dst have the same size and that the second dimension in both is +contiguous. +@see @ref packInto(), @ref castInto(), + @ref Corrade::Containers::StridedArrayView::isContiguous() +*/ +MAGNUM_EXPORT void unpackInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** + * @overload + * @m_since_latest + */ +MAGNUM_EXPORT void unpackInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** +@brief Unpack signed integral values into a floating-point representation +@param[in] src Source integral values +@param[out] dst Destination floating-point values +@m_since_latest + +Converts integral values from full range of given *signed* integral type to +floating-point values in range @f$ [-1, 1] @f$. Second dimension is meant to +contain vector/matrix components, or have a size of 1 for scalars. Expects that +@p src and @p dst have the same size and that the second dimension in both is +contiguous. +@see @ref packInto(), @ref castInto(), + @ref Corrade::Containers::StridedArrayView::isContiguous() +*/ +MAGNUM_EXPORT void unpackInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** + * @overload + * @m_since_latest + */ +MAGNUM_EXPORT void unpackInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** +@brief Pack floating-point values into an integer representation +@param[in] src Source floating-point values +@param[out] dst Destination integral values +@m_since_latest + +Converts floating-point value in range @f$ [0, 1] @f$ to full range of +given *unsigned* integral type or range @f$ [-1, 1] @f$ to full range of +given *signed* integral type. Second dimension is meant to contain +vector/matrix components, or have a size of 1 for scalars. Expects that @p src +and @p dst have the same size and that the second dimension in both is +contiguous. + +@attention Conversion result for floating-point numbers outside the normalized + range is undefined. + +@see @ref unpackInto(), @ref castInto(), + @ref Corrade::Containers::StridedArrayView::isContiguous() +*/ +MAGNUM_EXPORT void packInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** + * @overload + * @m_since_latest + */ +MAGNUM_EXPORT void packInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** + * @overload + * @m_since_latest + */ +MAGNUM_EXPORT void packInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** + * @overload + * @m_since_latest + */ +MAGNUM_EXPORT void packInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** +@brief Cast integer values into a floating-point representation +@param[in] src Source integral values +@param[out] dst Destination floating-point values +@m_since_latest + +Unlike @ref packInto(), this function performs only an equivalent of +@cpp Float(a) @ce over the range, so e.g. @cpp 135 @ce becomes @cpp 135.0f @ce. +Second dimension is meant to contain vector/matrix components, or have a size +of 1 for scalars. Expects that @p src and @p dst have the same size and that +the second dimension in both is contiguous. + +@attention Numbers with more than 23 bits of precision will not be represented + accurately when cast into a @ref Magnum::Float "Float". + +@see @ref castInto(const Corrade::Containers::StridedArrayView2D&, const Corrade::Containers::StridedArrayView2D&), + @ref Corrade::Containers::StridedArrayView::isContiguous() +*/ +MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** + * @overload + * @m_since_latest + */ +MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** + * @overload + * @m_since_latest + */ +MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** + * @overload + * @m_since_latest + */ +MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** + * @overload + * @m_since_latest + */ +MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** + * @overload + * @m_since_latest + */ +MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** +@brief Cast floating-point values into an integer representation +@param[in] src Source floating-point values +@param[out] dst Destination integral values +@m_since_latest + +Unlike @ref packInto(), this function performs only an equivalent of +@cpp Float(a) @ce over the range, so e.g. @cpp 135 @ce becomes @cpp 135.0f @ce. +Second dimension is meant to contain vector/matrix components, or have a size +of 1 for scalars. Expects that @p src and @p dst have the same size and that +the second dimension in both is contiguous. + +@attention Numbers with more than 23 bits of precision will not be represented + accurately when cast into a @ref Magnum::Float "Float". + +@see @ref castInto(const Corrade::Containers::StridedArrayView2D&, const Corrade::Containers::StridedArrayView2D&), + @ref Corrade::Containers::StridedArrayView::isContiguous() +*/ +MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** + * @overload + * @m_since_latest + */ +MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** + * @overload + * @m_since_latest + */ +MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** + * @overload + * @m_since_latest + */ +MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** + * @overload + * @m_since_latest + */ +MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/** + * @overload + * @m_since_latest + */ +MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D& src, const Corrade::Containers::StridedArrayView2D& dst); + +/*@}*/ + +}} + +#endif diff --git a/src/Magnum/Math/Test/CMakeLists.txt b/src/Magnum/Math/Test/CMakeLists.txt index f5c4e68e8..de48dbe70 100644 --- a/src/Magnum/Math/Test/CMakeLists.txt +++ b/src/Magnum/Math/Test/CMakeLists.txt @@ -29,6 +29,7 @@ corrade_add_test(MathFunctionsTest FunctionsTest.cpp LIBRARIES MagnumMathTestLib corrade_add_test(MathFunctionsBatchTest FunctionsBatchTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathHalfTest HalfTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathPackingTest PackingTest.cpp LIBRARIES MagnumMathTestLib) +corrade_add_test(MathPackingBatchTest PackingBatchTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathTagsTest TagsTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathTypeTraitsTest TypeTraitsTest.cpp LIBRARIES MagnumMathTestLib) @@ -90,8 +91,10 @@ set_target_properties( MathBoolVectorTest MathConstantsTest MathFunctionsTest + MathFunctionsBatchTest MathHalfTest MathPackingTest + MathPackingBatchTest MathTagsTest MathTypeTraitsTest diff --git a/src/Magnum/Math/Test/PackingBatchTest.cpp b/src/Magnum/Math/Test/PackingBatchTest.cpp new file mode 100644 index 000000000..96fe5d06f --- /dev/null +++ b/src/Magnum/Math/Test/PackingBatchTest.cpp @@ -0,0 +1,469 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + 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. +*/ + +#include +#include +#include +#include +#include + +#include "Magnum/Math/Color.h" +#include "Magnum/Math/Packing.h" +#include "Magnum/Math/PackingBatch.h" +#include "Magnum/Math/Vector4.h" + +namespace Magnum { namespace Math { namespace Test { namespace { + +struct PackingBatchTest: Corrade::TestSuite::Tester { + explicit PackingBatchTest(); + + void unpackUnsignedByte(); + void unpackUnsignedShort(); + void unpackSignedByte(); + void unpackSignedShort(); + void packUnsignedByte(); + void packUnsignedShort(); + void packSignedByte(); + void packSignedShort(); + + template void castUnsigned(); + template void castSigned(); + + template void assertionsPackUnpack(); + template void assertionsCast(); +}; + +PackingBatchTest::PackingBatchTest() { + addTests({&PackingBatchTest::unpackUnsignedByte, + &PackingBatchTest::unpackUnsignedShort, + &PackingBatchTest::unpackSignedByte, + &PackingBatchTest::unpackSignedShort, + &PackingBatchTest::packUnsignedByte, + &PackingBatchTest::packUnsignedShort, + &PackingBatchTest::packSignedByte, + &PackingBatchTest::packSignedShort, + + &PackingBatchTest::castUnsigned, + &PackingBatchTest::castUnsigned, + &PackingBatchTest::castUnsigned, + &PackingBatchTest::castSigned, + &PackingBatchTest::castSigned, + &PackingBatchTest::castSigned, + + &PackingBatchTest::assertionsPackUnpack, + &PackingBatchTest::assertionsPackUnpack, + &PackingBatchTest::assertionsPackUnpack, + &PackingBatchTest::assertionsPackUnpack, + &PackingBatchTest::assertionsCast, + &PackingBatchTest::assertionsCast, + &PackingBatchTest::assertionsCast, + &PackingBatchTest::assertionsCast, + &PackingBatchTest::assertionsCast, + &PackingBatchTest::assertionsCast}); +} + +typedef Math::Vector2 Vector2ub; +typedef Math::Vector2 Vector2us; +typedef Math::Vector2 Vector2b; +typedef Math::Vector2 Vector2s; +typedef Math::Vector2 Vector2; +typedef Math::Vector3 Vector3; +typedef Math::Vector4 Vector4; + +void PackingBatchTest::unpackUnsignedByte() { + /* Test data adapted from PackingTest */ + struct Data { + Vector2ub src; + Vector2 dst; + } data[]{ + {{0, 89}, {}}, + {{149, 255}, {}}, + {{0, 255}, {}} + }; + + constexpr Vector2 expected[] { + {0.0f, 0.34902f}, + {0.584314f, 1.0f}, + {0.0f, 1.0f} + }; + + Corrade::Containers::StridedArrayView1D src{data, &data[0].src, 3, sizeof(Data)}; + Corrade::Containers::StridedArrayView1D dst{data, &data[0].dst, 3, sizeof(Data)}; + unpackInto(Corrade::Containers::arrayCast<2, UnsignedByte>(src), + Corrade::Containers::arrayCast<2, Float>(dst)); + CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expected), + Corrade::TestSuite::Compare::Container); + + /* Ensure the results are consistent with non-batch APIs */ + for(std::size_t i = 0; i != Corrade::Containers::arraySize(data); ++i) + CORRADE_COMPARE(Math::unpack(data[i].src), data[i].dst); +} + +void PackingBatchTest::unpackUnsignedShort() { + /* Test data adapted from PackingTest */ + struct Data { + Vector2us src; + Vector2 dst; + } data[]{ + {{0, 8192}, {}}, + {{49152, 65535}, {}}, + {{0, 65535}, {}} + }; + + constexpr Vector2 expected[] { + {0.0f, 0.125002f}, + {0.750011f, 1.0f}, + {0.0f, 1.0f} + }; + + Corrade::Containers::StridedArrayView1D src{data, &data[0].src, 3, sizeof(Data)}; + Corrade::Containers::StridedArrayView1D dst{data, &data[0].dst, 3, sizeof(Data)}; + unpackInto(Corrade::Containers::arrayCast<2, UnsignedShort>(src), + Corrade::Containers::arrayCast<2, Float>(dst)); + CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expected), + Corrade::TestSuite::Compare::Container); + + /* Ensure the results are consistent with non-batch APIs */ + for(std::size_t i = 0; i != Corrade::Containers::arraySize(data); ++i) + CORRADE_COMPARE(Math::unpack(data[i].src), data[i].dst); +} + +void PackingBatchTest::unpackSignedByte() { + /* Test data adapted from PackingTest */ + struct Data { + Vector2b src; + Vector2 dst; + } data[]{ + {{0, 127}, {}}, + {{37, -72}, {}}, + {{-127, -128}, {}} + }; + + constexpr Vector2 expected[] { + {0.0f, 1.0f}, + {0.291339f, -0.566929f}, + {-1.0f, -1.0f} + }; + + Corrade::Containers::StridedArrayView1D src{data, &data[0].src, 3, sizeof(Data)}; + Corrade::Containers::StridedArrayView1D dst{data, &data[0].dst, 3, sizeof(Data)}; + unpackInto(Corrade::Containers::arrayCast<2, Byte>(src), + Corrade::Containers::arrayCast<2, Float>(dst)); + CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expected), + Corrade::TestSuite::Compare::Container); + + /* Ensure the results are consistent with non-batch APIs */ + for(std::size_t i = 0; i != Corrade::Containers::arraySize(data); ++i) + CORRADE_COMPARE(Math::unpack(data[i].src), data[i].dst); +} + +void PackingBatchTest::unpackSignedShort() { + /* Test data adapted from PackingTest */ + struct Data { + Vector2s src; + Vector2 dst; + } data[]{ + {{0, 16384}, {}}, + {{-16384, 32767}, {}}, + {{-32767, -32768}, {}} + }; + + constexpr Vector2 expected[] { + {0.0f, 0.500015f}, + {-0.500015f, 1.0f}, + {-1.0f, -1.0f} + }; + + Corrade::Containers::StridedArrayView1D src{data, &data[0].src, 3, sizeof(Data)}; + Corrade::Containers::StridedArrayView1D dst{data, &data[0].dst, 3, sizeof(Data)}; + unpackInto(Corrade::Containers::arrayCast<2, Short>(src), + Corrade::Containers::arrayCast<2, Float>(dst)); + CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expected), + Corrade::TestSuite::Compare::Container); + + /* Ensure the results are consistent with non-batch APIs */ + for(std::size_t i = 0; i != Corrade::Containers::arraySize(data); ++i) + CORRADE_COMPARE(Math::unpack(data[i].src), data[i].dst); +} + +void PackingBatchTest::packUnsignedByte() { + /* Test data adapted from PackingTest */ + struct Data { + Vector2 src; + Vector2ub dst; + } data[]{ + {{0.0f, 0.0000001f}, {}}, + {{0.4357f, 0.5f}, {}}, + {{1.0f, 0.9999999f}, {}} + }; + + constexpr Vector2ub expected[] { + {0, 0}, + {111, 128}, + {255, 255} + }; + + Corrade::Containers::StridedArrayView1D src{data, &data[0].src, 3, sizeof(Data)}; + Corrade::Containers::StridedArrayView1D dst{data, &data[0].dst, 3, sizeof(Data)}; + packInto(Corrade::Containers::arrayCast<2, Float>(src), + Corrade::Containers::arrayCast<2, UnsignedByte>(dst)); + CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expected), + Corrade::TestSuite::Compare::Container); + + /* Ensure the results are consistent with non-batch APIs */ + for(std::size_t i = 0; i != Corrade::Containers::arraySize(data); ++i) + CORRADE_COMPARE(Math::pack(data[i].src), data[i].dst); +} + +void PackingBatchTest::packUnsignedShort() { + /* Test data adapted from PackingTest */ + struct Data { + Vector2 src; + Vector2us dst; + } data[]{ + {{0.0f, 0.0000001f}, {}}, + {{0.4357f, 0.5f}, {}}, + {{1.0f, 0.9999999f}, {}} + }; + + constexpr Vector2us expected[] { + {0, 0}, + {28554, 32768}, + {65535, 65535} + }; + + Corrade::Containers::StridedArrayView1D src{data, &data[0].src, 3, sizeof(Data)}; + Corrade::Containers::StridedArrayView1D dst{data, &data[0].dst, 3, sizeof(Data)}; + packInto(Corrade::Containers::arrayCast<2, Float>(src), + Corrade::Containers::arrayCast<2, UnsignedShort>(dst)); + CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expected), + Corrade::TestSuite::Compare::Container); + + /* Ensure the results are consistent with non-batch APIs */ + for(std::size_t i = 0; i != Corrade::Containers::arraySize(data); ++i) + CORRADE_COMPARE(Math::pack(data[i].src), data[i].dst); +} + +void PackingBatchTest::packSignedByte() { + /* Test data adapted from PackingTest */ + struct Data { + Vector2 src; + Vector2b dst; + } data[]{ + {{-1.0f, -0.732f}, {}}, + {{0.0f, 0.1357f}, {}}, + {{1.0f, 0.9999999f}, {}} + }; + + constexpr Vector2b expected[] { + {-127, -93}, + {0, 17}, + {127, 127} + }; + + Corrade::Containers::StridedArrayView1D src{data, &data[0].src, 3, sizeof(Data)}; + Corrade::Containers::StridedArrayView1D dst{data, &data[0].dst, 3, sizeof(Data)}; + packInto(Corrade::Containers::arrayCast<2, Float>(src), + Corrade::Containers::arrayCast<2, Byte>(dst)); + CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expected), + Corrade::TestSuite::Compare::Container); + + /* Ensure the results are consistent with non-batch APIs */ + for(std::size_t i = 0; i != Corrade::Containers::arraySize(data); ++i) + CORRADE_COMPARE(Math::pack(data[i].src), data[i].dst); +} + +void PackingBatchTest::packSignedShort() { + /* Test data adapted from PackingTest */ + struct Data { + Vector2 src; + Vector2s dst; + } data[]{ + {{-1.0f, -0.33f}, {}}, + {{0.0f, 0.66f}, {}}, + {{1.0f, 0.9999999f}, {}} + }; + + constexpr Vector2s expected[] { + {-32767, -10813}, + {0, 21626}, + {32767, 32767} + }; + + Corrade::Containers::StridedArrayView1D src{data, &data[0].src, 3, sizeof(Data)}; + Corrade::Containers::StridedArrayView1D dst{data, &data[0].dst, 3, sizeof(Data)}; + packInto(Corrade::Containers::arrayCast<2, Float>(src), + Corrade::Containers::arrayCast<2, Short>(dst)); + CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expected), + Corrade::TestSuite::Compare::Container); + + /* Ensure the results are consistent with non-batch APIs */ + for(std::size_t i = 0; i != Corrade::Containers::arraySize(data); ++i) + CORRADE_COMPARE(Math::pack(data[i].src), data[i].dst); +} + +template void PackingBatchTest::castUnsigned() { + setTestCaseTemplateName(TypeTraits::name()); + + struct Data { + Math::Vector2 src; + Vector2 dst; + } data[]{ + {{0, 89}, {}}, + {{149, 22}, {}}, + {{13, 255}, {}} + }; + + constexpr Vector2 expectedFloat[] { + {0.0f, 89.0f}, + {149.0f, 22.0f}, + {13.0f, 255.0f} + }; + + constexpr Math::Vector2 expectedInteger[] { + {0, 89}, + {149, 22}, + {13, 255} + }; + + Corrade::Containers::StridedArrayView1D> src{data, &data[0].src, 3, sizeof(Data)}; + Corrade::Containers::StridedArrayView1D dst{data, &data[0].dst, 3, sizeof(Data)}; + castInto(Corrade::Containers::arrayCast<2, T>(src), + Corrade::Containers::arrayCast<2, Float>(dst)); + CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expectedFloat), + Corrade::TestSuite::Compare::Container); + + /* Test the other way around as well */ + castInto(Corrade::Containers::arrayCast<2, Float>(dst), + Corrade::Containers::arrayCast<2, T>(src)); + CORRADE_COMPARE_AS(src, Corrade::Containers::stridedArrayView(expectedInteger), + Corrade::TestSuite::Compare::Container); +} + +template void PackingBatchTest::castSigned() { + setTestCaseTemplateName(TypeTraits::name()); + + struct Data { + Math::Vector2 src; + Vector2 dst; + } data[]{ + {{0, -89}, {}}, + {{-119, 22}, {}}, + {{13, 127}, {}} + }; + + constexpr Vector2 expectedFloat[] { + {0.0f, -89.0f}, + {-119.0f, 22.0f}, + {13.0f, 127.0f} + }; + + constexpr Math::Vector2 expectedInteger[] { + {0, -89}, + {-119, 22}, + {13, 127} + }; + + Corrade::Containers::StridedArrayView1D> src{data, &data[0].src, 3, sizeof(Data)}; + Corrade::Containers::StridedArrayView1D dst{data, &data[0].dst, 3, sizeof(Data)}; + castInto(Corrade::Containers::arrayCast<2, T>(src), + Corrade::Containers::arrayCast<2, Float>(dst)); + CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expectedFloat), + Corrade::TestSuite::Compare::Container); + + /* Test the other way around as well */ + castInto(Corrade::Containers::arrayCast<2, Float>(dst), + Corrade::Containers::arrayCast<2, T>(src)); + CORRADE_COMPARE_AS(src, Corrade::Containers::stridedArrayView(expectedInteger), + Corrade::TestSuite::Compare::Container); +} + +template void PackingBatchTest::assertionsPackUnpack() { + Math::Vector2 data[2]{}; + Vector2 resultWrongCount[1]{}; + Vector3 resultWrongVectorSize[2]{}; + Vector4 resultNonContiguous[2]{}; + + auto src = Corrade::Containers::arrayCast<2, T>( + Corrade::Containers::arrayView(data)); + auto dstWrongCount = Corrade::Containers::arrayCast<2, Float>( + Corrade::Containers::arrayView(resultWrongCount)); + auto dstWrongVectorSize = Corrade::Containers::arrayCast<2, Float>( + Corrade::Containers::arrayView(resultWrongVectorSize)); + auto dstNotContiguous = Corrade::Containers::arrayCast<2, Float>( + Corrade::Containers::arrayView(resultNonContiguous)).every({1, 2}); + + std::ostringstream out; + Error redirectError{&out}; + unpackInto(src, dstWrongCount); + unpackInto(src, dstWrongVectorSize); + unpackInto(src, dstNotContiguous); + packInto(dstWrongCount, src); + packInto(dstWrongVectorSize, src); + packInto(dstNotContiguous, src); + CORRADE_COMPARE(out.str(), + "Math::unpackInto(): wrong destination size, got {1, 2} but expected {2, 2}\n" + "Math::unpackInto(): wrong destination size, got {2, 3} but expected {2, 2}\n" + "Math::unpackInto(): second view dimension is not contiguous\n" + "Math::packInto(): wrong destination size, got {2, 2} but expected {1, 2}\n" + "Math::packInto(): wrong destination size, got {2, 2} but expected {2, 3}\n" + "Math::packInto(): second view dimension is not contiguous\n"); +} + +template void PackingBatchTest::assertionsCast() { + Math::Vector2 data[2]{}; + Vector2 resultWrongCount[1]{}; + Vector3 resultWrongVectorSize[2]{}; + Vector4 resultNonContiguous[2]{}; + + auto src = Corrade::Containers::arrayCast<2, T>( + Corrade::Containers::arrayView(data)); + auto dstWrongCount = Corrade::Containers::arrayCast<2, Float>( + Corrade::Containers::arrayView(resultWrongCount)); + auto dstWrongVectorSize = Corrade::Containers::arrayCast<2, Float>( + Corrade::Containers::arrayView(resultWrongVectorSize)); + auto dstNotContiguous = Corrade::Containers::arrayCast<2, Float>( + Corrade::Containers::arrayView(resultNonContiguous)).every({1, 2}); + + std::ostringstream out; + Error redirectError{&out}; + castInto(src, dstWrongCount); + castInto(src, dstWrongVectorSize); + castInto(src, dstNotContiguous); + castInto(dstWrongCount, src); + castInto(dstWrongVectorSize, src); + castInto(dstNotContiguous, src); + CORRADE_COMPARE(out.str(), + "Math::castInto(): wrong destination size, got {1, 2} but expected {2, 2}\n" + "Math::castInto(): wrong destination size, got {2, 3} but expected {2, 2}\n" + "Math::castInto(): second view dimension is not contiguous\n" + "Math::castInto(): wrong destination size, got {2, 2} but expected {1, 2}\n" + "Math::castInto(): wrong destination size, got {2, 2} but expected {2, 3}\n" + "Math::castInto(): second view dimension is not contiguous\n"); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Math::Test::PackingBatchTest)