Most of the testing scaffolding here is a preparation for the actually complex formats like BC6/7 or ASTC. Also, it's great to be able to use Magnum from Python to prepare data for testing the C++ Magnum APIs.pull/620/head
@ -0,0 +1,201 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
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 "ColorBatch.h" |
||||||
|
|
||||||
|
#include <cstring> |
||||||
|
#include <Corrade/Containers/StridedArrayView.h> |
||||||
|
|
||||||
|
#include "Magnum/Magnum.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Math { |
||||||
|
|
||||||
|
namespace { |
||||||
|
|
||||||
|
inline void yFlipBc1BlockInPlace(char* const data) { |
||||||
|
/* The 64-bit block is laid out as follows:
|
||||||
|
|
||||||
|
- 2 bytes for first endpoint color |
||||||
|
- 2 bytes for second endpoint color |
||||||
|
- 4 bytes for 4x4 2-bit color indices in this order: |
||||||
|
|
||||||
|
a b c d |
||||||
|
e f g h |
||||||
|
i j k l |
||||||
|
m n o p |
||||||
|
|
||||||
|
Which means each row is one byte, so the Y-flip reduces down to a simple |
||||||
|
byte swap. See the official specification for details: |
||||||
|
https://learn.microsoft.com/cs-cz/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression#bc1 */
|
||||||
|
{ |
||||||
|
char tmp = data[4]; |
||||||
|
data[4] = data[7]; |
||||||
|
data[7] = tmp; |
||||||
|
} { |
||||||
|
char tmp = data[5]; |
||||||
|
data[5] = data[6]; |
||||||
|
data[6] = tmp; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline void yFlipBc4BlockInPlace(char* data) { |
||||||
|
/* The 64-bit block is laid out as follows:
|
||||||
|
|
||||||
|
- 1 byte for first endpoint color channel |
||||||
|
- 1 byte for second endpoint color channel |
||||||
|
- 6 bytes for 4x4 3-bit color indices + interpolation factors, same |
||||||
|
order as BC1 |
||||||
|
|
||||||
|
Compared to BC1, this means swapping groups of 12 bits instead of 8. |
||||||
|
https://learn.microsoft.com/cs-cz/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression#bc4 */
|
||||||
|
|
||||||
|
/* Load & byte-swap the whole block */ |
||||||
|
union { |
||||||
|
char bytes[8]; |
||||||
|
UnsignedLong value; |
||||||
|
} block; |
||||||
|
for(std::size_t i = 0; i != 8; ++i) { |
||||||
|
#ifndef CORRADE_TARGET_BIG_ENDIAN |
||||||
|
block.bytes[i] = data[i]; |
||||||
|
#else |
||||||
|
block.bytes[i] = data[7 - i]; |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
/* Flip the 12-bit groups */ |
||||||
|
block.value = |
||||||
|
(block.value & 0x000000000000ffffull) | |
||||||
|
(block.value & 0xfff0000000000000ull) >> 36 | |
||||||
|
(block.value & 0x000fff0000000000ull) >> 12 | |
||||||
|
(block.value & 0x000000fff0000000ull) << 12 | |
||||||
|
(block.value & 0x000000000fff0000ull) << 36; |
||||||
|
|
||||||
|
/* Byte-swap & store the block back */ |
||||||
|
for(std::size_t i = 0; i != 8; ++i) { |
||||||
|
#ifndef CORRADE_TARGET_BIG_ENDIAN |
||||||
|
data[i] = block.bytes[i]; |
||||||
|
#else |
||||||
|
data[i] = block.bytes[7 - i]; |
||||||
|
#endif |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline void yFlipBc3BlockInPlace(char* const data) { |
||||||
|
yFlipBc4BlockInPlace(data); |
||||||
|
yFlipBc1BlockInPlace(data + 8); |
||||||
|
} |
||||||
|
|
||||||
|
inline void yFlipBc5BlockInPlace(char* const data) { |
||||||
|
yFlipBc4BlockInPlace(data); |
||||||
|
yFlipBc4BlockInPlace(data + 8); |
||||||
|
} |
||||||
|
|
||||||
|
template<std::size_t blockSize, void(*flipBlock)(char*)> void yFlipBlocksInPlace(const Containers::StridedArrayView4D<char>& blocks |
||||||
|
#ifndef CORRADE_NO_ASSERT |
||||||
|
, const char* messagePrefix |
||||||
|
#endif |
||||||
|
) { |
||||||
|
const std::size_t* const size = blocks.size().begin(); |
||||||
|
CORRADE_ASSERT(size[3] == blockSize, |
||||||
|
messagePrefix << "expected last dimension to be" << blockSize << "bytes but got" << size[3], ); |
||||||
|
CORRADE_ASSERT(blocks.isContiguous<3>(), |
||||||
|
messagePrefix << "last dimension is not contiguous", ); |
||||||
|
|
||||||
|
/* The high-level logic is mostly a copy of Utility::flipInPlace() without
|
||||||
|
the "leftovers" part. It's however not calling that function directly |
||||||
|
because it'd mean going through memory twice, once for copying whole |
||||||
|
blocks and once for recalculating each block. */ |
||||||
|
|
||||||
|
CORRADE_INTERNAL_ASSERT(blocks.template isContiguous<3>()); |
||||||
|
|
||||||
|
char* const ptr = static_cast<char*>(blocks.data()); |
||||||
|
const std::ptrdiff_t* const stride = blocks.stride().begin(); |
||||||
|
for(std::size_t z = 0; z != size[0]; ++z) { |
||||||
|
char* const ptrZ = ptr + z*stride[0]; |
||||||
|
|
||||||
|
/* Go through half of the items in Y and swap them with the other
|
||||||
|
half, flipping their contents along the way */ |
||||||
|
for(std::size_t y = 0, yMax = size[1]/2; y != yMax; ++y) { |
||||||
|
char* const ptrYTop = ptrZ + y*stride[1]; |
||||||
|
char* const ptrYBottom = ptrZ + (size[1] - y - 1)*stride[1]; |
||||||
|
|
||||||
|
/* Copy a block at a time and flip its contents as well */ |
||||||
|
alignas(blockSize) char tmp[blockSize]; |
||||||
|
for(std::size_t x = 0; x != size[2]; ++x) { |
||||||
|
char* const ptrXTop = ptrYTop + x*stride[2]; |
||||||
|
char* const ptrXBottom = ptrYBottom + x*stride[2]; |
||||||
|
flipBlock(ptrXTop); |
||||||
|
flipBlock(ptrXBottom); |
||||||
|
std::memcpy(tmp, ptrXTop, blockSize); |
||||||
|
std::memcpy(ptrXTop, ptrXBottom, blockSize); |
||||||
|
std::memcpy(ptrXBottom, tmp, blockSize); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* If there was an odd number of rows, make sure to flip contents of
|
||||||
|
the middle row as well */ |
||||||
|
if(size[1] % 2) { |
||||||
|
char* const ptrYMid = ptrZ + (size[1]/2)*stride[1]; |
||||||
|
for(std::size_t x = 0; x != size[2]; ++x) |
||||||
|
flipBlock(ptrYMid + x*stride[2]); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void yFlipBc1InPlace(const Containers::StridedArrayView4D<char>& blocks) { |
||||||
|
yFlipBlocksInPlace<8, yFlipBc1BlockInPlace>(blocks |
||||||
|
#ifndef CORRADE_NO_ASSERT |
||||||
|
, "Math::yFlipBc1InPlace():" |
||||||
|
#endif |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
void yFlipBc3InPlace(const Containers::StridedArrayView4D<char>& blocks) { |
||||||
|
yFlipBlocksInPlace<16, yFlipBc3BlockInPlace>(blocks |
||||||
|
#ifndef CORRADE_NO_ASSERT |
||||||
|
, "Math::yFlipBc3InPlace():" |
||||||
|
#endif |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
void yFlipBc4InPlace(const Containers::StridedArrayView4D<char>& blocks) { |
||||||
|
yFlipBlocksInPlace<8, yFlipBc4BlockInPlace>(blocks |
||||||
|
#ifndef CORRADE_NO_ASSERT |
||||||
|
, "Math::yFlipBc4InPlace():" |
||||||
|
#endif |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
void yFlipBc5InPlace(const Containers::StridedArrayView4D<char>& blocks) { |
||||||
|
yFlipBlocksInPlace<16, yFlipBc5BlockInPlace>(blocks |
||||||
|
#ifndef CORRADE_NO_ASSERT |
||||||
|
, "Math::yFlipBc5InPlace():" |
||||||
|
#endif |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
}} |
||||||
@ -0,0 +1,126 @@ |
|||||||
|
#ifndef Magnum_Math_ColorBatch_h |
||||||
|
#define Magnum_Math_ColorBatch_h |
||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
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 Function @ref Magnum::Math::yFlipBc1InPlace(), @ref Magnum::Math::yFlipBc3InPlace(), @ref Magnum::Math::yFlipBc4InPlace(), @ref Magnum::Math::yFlipBc5InPlace() |
||||||
|
* @m_since_latest |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Corrade/Containers/Containers.h> |
||||||
|
|
||||||
|
#include "Magnum/visibility.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Math { |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Y-flip BC1 texture blocks in-place |
||||||
|
@m_since_latest |
||||||
|
|
||||||
|
Performs a Y flip of given 3D image by flipping block order and modifying |
||||||
|
internal block representation to encode the same information, just upside down. |
||||||
|
No decoding or re-encoding of the block data is performed, thus the operation |
||||||
|
is lossless. However note that this operation flips full blocks --- if size of |
||||||
|
the actual image isn't whole blocks, the flipped image will be shifted compared |
||||||
|
to the original, possibly with garbage data appearing in the first few rows. |
||||||
|
|
||||||
|
First dimension is expected to be image slices, second block rows, third |
||||||
|
2D blocks, fourth the 64-bit 4x4 block data, i.e. the last dimension is |
||||||
|
expected to be contiguous with size of 8. |
||||||
|
@see @ref CompressedPixelFormat::Bc1RGBUnorm, |
||||||
|
@ref CompressedPixelFormat::Bc1RGBSrgb, |
||||||
|
@ref CompressedPixelFormat::Bc1RGBAUnorm, |
||||||
|
@ref CompressedPixelFormat::Bc1RGBASrgb |
||||||
|
*/ |
||||||
|
MAGNUM_EXPORT void yFlipBc1InPlace(const Corrade::Containers::StridedArrayView4D<char>& blocks); |
||||||
|
|
||||||
|
/** @todo BC2, if used at all for anything */ |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Y-flip BC3 texture blocks in-place |
||||||
|
@m_since_latest |
||||||
|
|
||||||
|
Performs a Y flip of given 3D image by flipping block order and modifying |
||||||
|
internal block representation to encode the same information, just upside down. |
||||||
|
No decoding or re-encoding of the block data is performed, thus the operation |
||||||
|
is lossless. However note that this operation flips full blocks --- if size of |
||||||
|
the actual image isn't whole blocks, the flipped image will be shifted compared |
||||||
|
to the original, possibly with garbage data appearing in the first few rows. |
||||||
|
|
||||||
|
First dimension is expected to be image slices, second block rows, third |
||||||
|
2D blocks, fourth the 128-bit 4x4 block data, i.e. the last dimension is |
||||||
|
expected to be contiguous with size of 16. As BC3 is internally a 64-bit BC4 |
||||||
|
block for alpha followed by a 64-bit BC1 block for RGB, the operation is the |
||||||
|
same as performing @ref yFlipBc4InPlace() on the first half and |
||||||
|
@ref yFlipBc1InPlace() on second half of each block. |
||||||
|
@see @ref CompressedPixelFormat::Bc3RGBAUnorm, |
||||||
|
@ref CompressedPixelFormat::Bc3RGBASrgb |
||||||
|
*/ |
||||||
|
MAGNUM_EXPORT void yFlipBc3InPlace(const Corrade::Containers::StridedArrayView4D<char>& blocks); |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Y-flip BC4 texture blocks in-place |
||||||
|
@m_since_latest |
||||||
|
|
||||||
|
Performs a Y flip of given 3D image by flipping block order and modifying |
||||||
|
internal block representation to encode the same information, just upside down. |
||||||
|
No decoding or re-encoding of the block data is performed, thus the operation |
||||||
|
is lossless. However note that this operation flips full blocks --- if size of |
||||||
|
the actual image isn't whole blocks, the flipped image will be shifted compared |
||||||
|
to the original, possibly with garbage data appearing in the first few rows. |
||||||
|
|
||||||
|
First dimension is expected to be image slices, second block rows, third |
||||||
|
2D blocks, fourth the 64-bit 4x4 block data, i.e. the last dimension is |
||||||
|
expected to be contiguous with size of 8. |
||||||
|
@see @ref CompressedPixelFormat::Bc4RUnorm, |
||||||
|
@ref CompressedPixelFormat::Bc4RSnorm |
||||||
|
*/ |
||||||
|
MAGNUM_EXPORT void yFlipBc4InPlace(const Corrade::Containers::StridedArrayView4D<char>& blocks); |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Y-flip BC5 texture blocks in-place |
||||||
|
@m_since_latest |
||||||
|
|
||||||
|
Performs a Y flip of given 3D image by flipping block order and modifying |
||||||
|
internal block representation to encode the same information, just upside down. |
||||||
|
No decoding or re-encoding of the block data is performed, thus the operation |
||||||
|
is lossless. However note that this operation flips full blocks --- if size of |
||||||
|
the actual image isn't whole blocks, the flipped image will be shifted compared |
||||||
|
to the original, possibly with garbage data appearing in the first few rows. |
||||||
|
|
||||||
|
First dimension is expected to be image slices, second block rows, third |
||||||
|
2D blocks, fourth the 128-bit 4x4 block data, i.e. the last dimension is |
||||||
|
expected to be contiguous with size of 16. As BC5 is internally two 64-bit BC4 |
||||||
|
blocks, the operation is the same as performing @ref yFlipBc4InPlace() on both |
||||||
|
halves of each block. |
||||||
|
@see @ref CompressedPixelFormat::Bc5RGUnorm, |
||||||
|
@ref CompressedPixelFormat::Bc5RGSnorm |
||||||
|
*/ |
||||||
|
MAGNUM_EXPORT void yFlipBc5InPlace(const Corrade::Containers::StridedArrayView4D<char>& blocks); |
||||||
|
|
||||||
|
}} |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,353 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
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 <sstream> |
||||||
|
#include <Corrade/Containers/Optional.h> |
||||||
|
#include <Corrade/Containers/String.h> |
||||||
|
#include <Corrade/Containers/StridedArrayView.h> |
||||||
|
#include <Corrade/PluginManager/Manager.h> |
||||||
|
#include <Corrade/TestSuite/Tester.h> |
||||||
|
#include <Corrade/TestSuite/Compare/Container.h> |
||||||
|
#include <Corrade/Utility/Algorithms.h> |
||||||
|
#include <Corrade/Utility/DebugStl.h> /** @todo remove once Debug is stram-free */ |
||||||
|
#include <Corrade/Utility/Path.h> |
||||||
|
|
||||||
|
#include "Magnum/ImageView.h" |
||||||
|
#include "Magnum/PixelFormat.h" |
||||||
|
#include "Magnum/DebugTools/CompareImage.h" |
||||||
|
#include "Magnum/Math/ColorBatch.h" |
||||||
|
#include "Magnum/Math/Vector4.h" |
||||||
|
#include "Magnum/Trade/AbstractImageConverter.h" |
||||||
|
#include "Magnum/Trade/AbstractImporter.h" |
||||||
|
#include "Magnum/Trade/ImageData.h" |
||||||
|
|
||||||
|
#include "configure.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Math { namespace Test { namespace { |
||||||
|
|
||||||
|
struct ColorBatchTest: Corrade::TestSuite::Tester { |
||||||
|
explicit ColorBatchTest(); |
||||||
|
|
||||||
|
void yFlip(); |
||||||
|
void yFlip3D(); |
||||||
|
|
||||||
|
void yFlipInvalidLastDimension(); |
||||||
|
|
||||||
|
PluginManager::Manager<Trade::AbstractImageConverter> _converterManager{MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR}; |
||||||
|
PluginManager::Manager<Trade::AbstractImporter> _importerManager{MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR}; |
||||||
|
}; |
||||||
|
|
||||||
|
/* The expected arrays are formatted from the test failure output with
|
||||||
|
print(', '.join(["'\\x{:02x}'".format(i + 255 if i < 0 else i) for i in vals])) |
||||||
|
with vals being an array printed by the test */ |
||||||
|
/** @todo make something more convenient for binary data comparison in
|
||||||
|
TestSuite already, FFS */ |
||||||
|
|
||||||
|
/* Used by both yFlip() and yFlip3D() */ |
||||||
|
const char CheckerboardBC1[]{ |
||||||
|
/* ./format-block-data.py checkerboard.in.png checkerboard.png */ |
||||||
|
'\x5c', '\xa6', '\x54', '\x74', '\x00', '\x00', '\x40', '\x40', |
||||||
|
'\x3c', '\xa6', '\x95', '\x7c', '\x00', '\x00', '\x02', '\x01', |
||||||
|
'\x8c', '\x3e', '\x69', '\x33', '\x00', '\x00', '\x40', '\x40', |
||||||
|
'\x8c', '\x3e', '\x29', '\x33', '\x00', '\x00', '\x01', '\x01', |
||||||
|
'\x3c', '\xa6', '\x6f', '\x5b', '\x00', '\x40', '\x00', '\x00', |
||||||
|
'\x3c', '\xa6', '\xcc', '\x4a', '\x03', '\x01', '\x00', '\x00', |
||||||
|
'\x8c', '\x3e', '\xc9', '\x32', '\x40', '\xc0', '\x00', '\x00', |
||||||
|
'\x8c', '\x3e', '\x89', '\x33', '\x01', '\x01', '\x00', '\x00', |
||||||
|
'\x6c', '\x3e', '\xe9', '\x32', '\x00', '\x00', '\xc0', '\x40', |
||||||
|
'\x6c', '\x3e', '\x69', '\x33', '\x00', '\x00', '\x01', '\x01', |
||||||
|
'\x66', '\xc6', '\x67', '\x84', '\x00', '\x00', '\x40', '\x40', |
||||||
|
'\x66', '\xc6', '\xa6', '\x8c', '\x00', '\x00', '\x02', '\x01', |
||||||
|
'\x8c', '\x3e', '\xc9', '\x32', '\x40', '\xc0', '\x00', '\x00', |
||||||
|
'\x8c', '\x3e', '\x8a', '\x33', '\x03', '\x01', '\x00', '\x00', |
||||||
|
'\x66', '\xc6', '\x87', '\x63', '\x00', '\x40', '\x00', '\x00', |
||||||
|
'\x66', '\xc6', '\xc7', '\x52', '\x03', '\x01', '\x00', '\x00', |
||||||
|
'\x66', '\xc6', '\x87', '\x6b', '\x00', '\x00', '\x40', '\x00', |
||||||
|
'\x66', '\xc6', '\x67', '\x63', '\x00', '\x00', '\x01', '\x09', |
||||||
|
'\xa6', '\xc9', '\xc7', '\x51', '\x00', '\x00', '\xc0', '\x40', |
||||||
|
'\xa6', '\xc9', '\xa7', '\x61', '\x00', '\x00', '\x01', '\x01', |
||||||
|
'\x66', '\xc6', '\x67', '\x42', '\x40', '\xc0', '\x00', '\x00', |
||||||
|
'\x66', '\xc6', '\xc7', '\x73', '\x01', '\x01', '\x00', '\x00', |
||||||
|
'\xa6', '\xc9', '\xa7', '\x51', '\x40', '\xc0', '\x00', '\x00', |
||||||
|
'\xa6', '\xc9', '\xa7', '\x69', '\x03', '\x01', '\x00', '\x00', |
||||||
|
}; |
||||||
|
const char CheckerboardBC1Flipped[]{ |
||||||
|
'\x66', '\xc6', '\x67', '\x42', '\x00', '\x00', '\xc0', '\x40', |
||||||
|
'\x66', '\xc6', '\xc7', '\x73', '\x00', '\x00', '\x01', '\x01', |
||||||
|
'\xa6', '\xc9', '\xa7', '\x51', '\x00', '\x00', '\xc0', '\x40', |
||||||
|
'\xa6', '\xc9', '\xa7', '\x69', '\x00', '\x00', '\x01', '\x03', |
||||||
|
'\x66', '\xc6', '\x87', '\x6b', '\x00', '\x40', '\x00', '\x00', |
||||||
|
'\x66', '\xc6', '\x67', '\x63', '\x09', '\x01', '\x00', '\x00', |
||||||
|
'\xa6', '\xc9', '\xc7', '\x51', '\x40', '\xc0', '\x00', '\x00', |
||||||
|
'\xa6', '\xc9', '\xa7', '\x61', '\x01', '\x01', '\x00', '\x00', |
||||||
|
'\x8c', '\x3e', '\xc9', '\x32', '\x00', '\x00', '\xc0', '\x40', |
||||||
|
'\x8c', '\x3e', '\x8a', '\x33', '\x00', '\x00', '\x01', '\x03', |
||||||
|
'\x66', '\xc6', '\x87', '\x63', '\x00', '\x00', '\x40', '\x00', |
||||||
|
'\x66', '\xc6', '\xc7', '\x52', '\x00', '\x00', '\x01', '\x03', |
||||||
|
'\x6c', '\x3e', '\xe9', '\x32', '\x40', '\xc0', '\x00', '\x00', |
||||||
|
'\x6c', '\x3e', '\x69', '\x33', '\x01', '\x01', '\x00', '\x00', |
||||||
|
'\x66', '\xc6', '\x67', '\x84', '\x40', '\x40', '\x00', '\x00', |
||||||
|
'\x66', '\xc6', '\xa6', '\x8c', '\x01', '\x02', '\x00', '\x00', |
||||||
|
'\x3c', '\xa6', '\x6f', '\x5b', '\x00', '\x00', '\x40', '\x00', |
||||||
|
'\x3c', '\xa6', '\xcc', '\x4a', '\x00', '\x00', '\x01', '\x03', |
||||||
|
'\x8c', '\x3e', '\xc9', '\x32', '\x00', '\x00', '\xc0', '\x40', |
||||||
|
'\x8c', '\x3e', '\x89', '\x33', '\x00', '\x00', '\x01', '\x01', |
||||||
|
'\x5c', '\xa6', '\x54', '\x74', '\x40', '\x40', '\x00', '\x00', |
||||||
|
'\x3c', '\xa6', '\x95', '\x7c', '\x01', '\x02', '\x00', '\x00', |
||||||
|
'\x8c', '\x3e', '\x69', '\x33', '\x40', '\x40', '\x00', '\x00', |
||||||
|
'\x8c', '\x3e', '\x29', '\x33', '\x01', '\x01', '\x00', '\x00' |
||||||
|
}; |
||||||
|
|
||||||
|
const struct { |
||||||
|
const char* name; |
||||||
|
CompressedPixelFormat format; |
||||||
|
Vector2i blockCount; |
||||||
|
Containers::Array<char> input; |
||||||
|
void(*function)(const Containers::StridedArrayView4D<char>&); |
||||||
|
const char* file; |
||||||
|
Containers::Array<char> expected; |
||||||
|
} YFlipData[]{ |
||||||
|
/* The multi-block behavior is tested just for one format as it's internally
|
||||||
|
a shared template for all */ |
||||||
|
{"BC1, even block count", CompressedPixelFormat::Bc1RGBAUnorm, {4, 6}, |
||||||
|
Containers::Array<char>{const_cast<char*>(CheckerboardBC1), Containers::arraySize(CheckerboardBC1), [](char*, std::size_t){}}, |
||||||
|
yFlipBc1InPlace, "checkerboard.png", |
||||||
|
Containers::Array<char>{const_cast<char*>(CheckerboardBC1Flipped), Containers::arraySize(CheckerboardBC1Flipped), [](char*, std::size_t){}}}, |
||||||
|
{"BC1, odd block count", CompressedPixelFormat::Bc1RGBAUnorm, {6, 3}, {InPlaceInit, { |
||||||
|
/* ./format-block-data.py checkerboard-odd.in.png checkerboard-odd.png */ |
||||||
|
'\x8c', '\x3e', '\x8a', '\x33', '\x00', '\xa0', '\x50', '\x90', |
||||||
|
'\x66', '\xc6', '\x8c', '\x3e', '\x05', '\x05', '\x05', '\x05', |
||||||
|
'\x66', '\xc6', '\xe7', '\x73', '\x00', '\x02', '\x0b', '\x06', |
||||||
|
'\xa6', '\xc9', '\xa7', '\x71', '\x00', '\xa0', '\x50', '\x50', |
||||||
|
'\xa6', '\xc9', '\x19', '\x34', '\x50', '\x50', '\x50', '\x50', |
||||||
|
'\x19', '\x34', '\x32', '\x33', '\x00', '\x0f', '\x07', '\x05', |
||||||
|
'\x45', '\xc6', '\x2c', '\x3e', '\x55', '\x55', '\x00', '\xa0', |
||||||
|
'\x25', '\xe2', '\x8a', '\x77', '\xf5', '\xf5', '\x0f', '\x0f', |
||||||
|
'\x66', '\xbe', '\xa6', '\xb9', '\x0a', '\x00', '\x55', '\x55', |
||||||
|
'\xa6', '\xc1', '\x18', '\x34', '\x00', '\x00', '\x55', '\x55', |
||||||
|
'\xc5', '\xc9', '\x38', '\x34', '\x50', '\x50', '\xf5', '\xf5', |
||||||
|
'\x8e', '\x73', '\x19', '\x34', '\x57', '\x55', '\x00', '\x00', |
||||||
|
'\x66', '\xc6', '\x67', '\x63', '\x60', '\xd0', '\xa0', '\x00', |
||||||
|
'\xa6', '\xc9', '\x66', '\xc6', '\x05', '\x05', '\x05', '\x05', |
||||||
|
'\xa6', '\xc9', '\xa7', '\x71', '\x05', '\x0d', '\x0a', '\x00', |
||||||
|
'\x19', '\x34', '\xd0', '\x32', '\xb0', '\x60', '\xf0', '\x00', |
||||||
|
'\xae', '\x73', '\x19', '\x34', '\x05', '\x05', '\x05', '\x05', |
||||||
|
'\xae', '\x73', '\x8a', '\x4a', '\x05', '\x05', '\x0a', '\x00', |
||||||
|
}}, yFlipBc1InPlace, "checkerboard-odd.png", {InPlaceInit, { |
||||||
|
'\x66', '\xc6', '\x67', '\x63', '\x00', '\xa0', '\xd0', '\x60', |
||||||
|
'\xa6', '\xc9', '\x66', '\xc6', '\x05', '\x05', '\x05', '\x05', |
||||||
|
'\xa6', '\xc9', '\xa7', '\x71', '\x00', '\x0a', '\x0d', '\x05', |
||||||
|
'\x19', '\x34', '\xd0', '\x32', '\x00', '\xf0', '\x60', '\xb0', |
||||||
|
'\xae', '\x73', '\x19', '\x34', '\x05', '\x05', '\x05', '\x05', |
||||||
|
'\xae', '\x73', '\x8a', '\x4a', '\x00', '\x0a', '\x05', '\x05', |
||||||
|
'\x45', '\xc6', '\x2c', '\x3e', '\xa0', '\x00', '\x55', '\x55', |
||||||
|
'\x25', '\xe2', '\x8a', '\x77', '\x0f', '\x0f', '\xf5', '\xf5', |
||||||
|
'\x66', '\xbe', '\xa6', '\xb9', '\x55', '\x55', '\x00', '\x0a', |
||||||
|
'\xa6', '\xc1', '\x18', '\x34', '\x55', '\x55', '\x00', '\x00', |
||||||
|
'\xc5', '\xc9', '\x38', '\x34', '\xf5', '\xf5', '\x50', '\x50', |
||||||
|
'\x8e', '\x73', '\x19', '\x34', '\x00', '\x00', '\x55', '\x57', |
||||||
|
'\x8c', '\x3e', '\x8a', '\x33', '\x90', '\x50', '\xa0', '\x00', |
||||||
|
'\x66', '\xc6', '\x8c', '\x3e', '\x05', '\x05', '\x05', '\x05', |
||||||
|
'\x66', '\xc6', '\xe7', '\x73', '\x06', '\x0b', '\x02', '\x00', |
||||||
|
'\xa6', '\xc9', '\xa7', '\x71', '\x50', '\x50', '\xa0', '\x00', |
||||||
|
'\xa6', '\xc9', '\x19', '\x34', '\x50', '\x50', '\x50', '\x50', |
||||||
|
'\x19', '\x34', '\x32', '\x33', '\x05', '\x07', '\x0f', '\x00' |
||||||
|
}}}, |
||||||
|
{"BC1", CompressedPixelFormat::Bc1RGBUnorm, {1, 4}, {InPlaceInit, { |
||||||
|
/* ./extract-interesting-blocks.py kodim23_bc1.dds bc1.png --offset 139
|
||||||
|
(image taken from the bcdec repository test files) */ |
||||||
|
/* [50, 53], 1.792 */ |
||||||
|
'\x79', '\xd6', '\xa7', '\x39', '\x5c', '\x55', '\xd5', '\x35', |
||||||
|
/* [32, 46], 1.784 */ |
||||||
|
'\xdd', '\xff', '\xa8', '\x6b', '\x55', '\x95', '\x25', '\x09', |
||||||
|
/* [48, 61], 1.780 */ |
||||||
|
'\xba', '\xe6', '\x07', '\x52', '\x00', '\x00', '\x2a', '\xd5', |
||||||
|
/* [132, 47], 1.780 */ |
||||||
|
'\x7a', '\xfe', '\x46', '\x81', '\xe0', '\x78', '\xd7', '\x2d', |
||||||
|
}}, yFlipBc1InPlace, "bc1.png", {InPlaceInit, { |
||||||
|
'\x7a', '\xfe', '\x46', '\x81', '\x2d', '\xd7', '\x78', '\xe0', |
||||||
|
'\xba', '\xe6', '\x07', '\x52', '\xd5', '\x2a', '\x00', '\x00', |
||||||
|
'\xdd', '\xff', '\xa8', '\x6b', '\x09', '\x25', '\x95', '\x55', |
||||||
|
'\x79', '\xd6', '\xa7', '\x39', '\x35', '\xd5', '\x55', '\x5c' |
||||||
|
}}}, |
||||||
|
{"BC3", CompressedPixelFormat::Bc3RGBAUnorm, {1, 4}, {InPlaceInit, { |
||||||
|
/* ./extract-interesting-blocks.py dice_bc3.dds bc3.png --offset 148
|
||||||
|
(image taken from the bcdec repository test files) */ |
||||||
|
/* [105, 42], 2.392 */ |
||||||
|
'\x26', '\x98', '\xb6', '\x0d', '\x00', '\x23', '\x99', '\x24', |
||||||
|
'\x8e', '\xfb', '\x00', '\x18', '\x55', '\x00', '\x02', '\xaa', |
||||||
|
/* [121, 122], 2.388 */ |
||||||
|
'\x0d', '\x5f', '\x29', '\x57', '\x4e', '\x9c', '\x30', '\xc1', |
||||||
|
'\x8b', '\xd6', '\x00', '\x00', '\x00', '\x00', '\x00', '\x40', |
||||||
|
/* [160, 22], 2.388 */ |
||||||
|
'\x02', '\x4a', '\x1c', '\x5c', '\xca', '\xe5', '\x90', '\x52', |
||||||
|
'\x72', '\x97', '\x00', '\x00', '\x40', '\x40', '\x00', '\x00', |
||||||
|
/* [96, 131], 2.376 */ |
||||||
|
'\x03', '\x32', '\x49', '\xba', '\x6d', '\xb6', '\x6d', '\xdb', |
||||||
|
'\x0c', '\xe7', '\x00', '\x00', '\x00', '\x00', '\x51', '\x55', |
||||||
|
}}, yFlipBc3InPlace, "bc3.png", {InPlaceInit, { |
||||||
|
'\x03', '\x32', '\xb6', '\x6d', '\xdb', '\xdb', '\x96', '\xa4', |
||||||
|
'\x0c', '\xe7', '\x00', '\x00', '\x55', '\x51', '\x00', '\x00', |
||||||
|
'\x02', '\x4a', '\x29', '\x55', '\x0e', '\xa5', '\xcc', '\xc1', |
||||||
|
'\x72', '\x97', '\x00', '\x00', '\x00', '\x00', '\x40', '\x40', |
||||||
|
'\x0d', '\x5f', '\x13', '\xcc', '\x09', '\xe5', '\x94', '\x72', |
||||||
|
'\x8b', '\xd6', '\x00', '\x00', '\x40', '\x00', '\x00', '\x00', |
||||||
|
'\x26', '\x98', '\x49', '\x32', '\x92', '\x00', '\x60', '\xdb', |
||||||
|
'\x8e', '\xfb', '\x00', '\x18', '\xaa', '\x02', '\x00', '\x55' |
||||||
|
}}}, |
||||||
|
{"BC4", CompressedPixelFormat::Bc4RUnorm, {1, 4}, {InPlaceInit, { |
||||||
|
/* ./extract-interesting-blocks.py dice_bc4.dds bc4.png
|
||||||
|
(image taken from the bcdec repository test files) */ |
||||||
|
/* [88, 130], 1.000 */ |
||||||
|
'\xec', '\xed', '\x3e', '\x62', '\xdb', '\xb6', '\x6d', '\xdb', |
||||||
|
/* [87, 129], 1.000 */ |
||||||
|
'\xd9', '\xec', '\xa3', '\xd0', '\x70', '\x7e', '\x62', '\xfb', |
||||||
|
/* [82, 125], 1.000 */ |
||||||
|
'\xdd', '\xfc', '\xa7', '\xe0', '\x4c', '\x36', '\x67', '\x9b', |
||||||
|
/* [81, 124], 1.000 */ |
||||||
|
'\xe9', '\xf5', '\x76', '\x60', '\x7f', '\xb6', '\x67', '\xfb', |
||||||
|
}}, yFlipBc4InPlace, "bc4.png", {InPlaceInit, { |
||||||
|
'\xe9', '\xf5', '\xb6', '\x6f', '\x7b', '\xf6', '\x67', '\x07', |
||||||
|
'\xdd', '\xfc', '\xb6', '\x69', '\x73', '\xce', '\x74', '\x0a', |
||||||
|
'\xd9', '\xec', '\xb6', '\xef', '\x27', '\x0d', '\x37', '\x0a', |
||||||
|
'\xec', '\xed', '\xb6', '\x6d', '\xdb', '\xb6', '\xed', '\x23' |
||||||
|
}}}, |
||||||
|
{"BC5", CompressedPixelFormat::Bc5RGUnorm, {1, 4}, {InPlaceInit, { |
||||||
|
/* ./extract-interesting-blocks.py dice_bc5.dds bc5.png --offset 26
|
||||||
|
(image taken from the bcdec repository test files) */ |
||||||
|
/* [120, 124], 2.000 */ |
||||||
|
'\xd3', '\xdf', '\x58', '\xbf', '\xda', '\xb1', '\x7d', '\xdb', '\xd3', '\xdf', '\x58', '\xbf', '\xda', '\xb1', '\x7d', '\xdb', |
||||||
|
/* [81, 124], 2.000 */ |
||||||
|
'\xeb', '\xf6', '\x76', '\x60', '\x7f', '\xb6', '\x67', '\xfb', '\xeb', '\xf6', '\x76', '\x60', '\x7f', '\xb6', '\x67', '\xfb', |
||||||
|
/* [121, 123], 2.000 */ |
||||||
|
'\xd4', '\xe2', '\xc8', '\x1d', '\xdb', '\xb3', '\x6d', '\xdb', '\xd4', '\xe2', '\xc8', '\x1d', '\xdb', '\xb3', '\x6d', '\xdb', |
||||||
|
/* [81, 123], 2.000 */ |
||||||
|
'\xd7', '\xf3', '\x9d', '\x10', '\x4e', '\x2f', '\xe7', '\x77', '\xd7', '\xf3', '\x9d', '\x10', '\x4e', '\x2f', '\xe7', '\x77', |
||||||
|
}}, yFlipBc5InPlace, "bc5.png", {InPlaceInit, { |
||||||
|
'\xd7', '\xf3', '\x7e', '\xf7', '\x72', '\xe1', '\xd4', '\x09', |
||||||
|
'\xd7', '\xf3', '\x7e', '\xf7', '\x72', '\xe1', '\xd4', '\x09', |
||||||
|
'\xd4', '\xe2', '\xb6', '\x3d', '\xdb', '\xb1', '\x8d', '\xdc', |
||||||
|
'\xd4', '\xe2', '\xb6', '\x3d', '\xdb', '\xb1', '\x8d', '\xdc', |
||||||
|
'\xeb', '\xf6', '\xb6', '\x6f', '\x7b', '\xf6', '\x67', '\x07', |
||||||
|
'\xeb', '\xf6', '\xb6', '\x6f', '\x7b', '\xf6', '\x67', '\x07', |
||||||
|
'\xd3', '\xdf', '\xb7', '\x1d', '\xdb', '\xab', '\x8d', '\xf5', |
||||||
|
'\xd3', '\xdf', '\xb7', '\x1d', '\xdb', '\xab', '\x8d', '\xf5' |
||||||
|
}}}, |
||||||
|
}; |
||||||
|
|
||||||
|
ColorBatchTest::ColorBatchTest() { |
||||||
|
addInstancedTests({&ColorBatchTest::yFlip}, |
||||||
|
Containers::arraySize(YFlipData)); |
||||||
|
|
||||||
|
addTests({&ColorBatchTest::yFlip3D, |
||||||
|
|
||||||
|
&ColorBatchTest::yFlipInvalidLastDimension}); |
||||||
|
} |
||||||
|
|
||||||
|
void ColorBatchTest::yFlip() { |
||||||
|
auto&& data = YFlipData[testCaseInstanceId()]; |
||||||
|
setTestCaseDescription(data.name); |
||||||
|
|
||||||
|
/* Copy to a mutable array first to operate in-place */ |
||||||
|
Containers::Array<char> blocks{Corrade::NoInit, data.input.size()}; |
||||||
|
Utility::copy(data.input, blocks); |
||||||
|
/* Using expanded() instead of the constructor as it catches issues where
|
||||||
|
the size would be smaller than the actual data */ |
||||||
|
data.function(stridedArrayView(blocks).expanded<0>(Containers::Size3D{ |
||||||
|
std::size_t(data.blockCount.y()), |
||||||
|
std::size_t(data.blockCount.x()), |
||||||
|
compressedPixelFormatBlockDataSize(data.format)})); |
||||||
|
CORRADE_COMPARE_AS(blocks, |
||||||
|
data.expected, |
||||||
|
TestSuite::Compare::Container); |
||||||
|
|
||||||
|
/* Catch also ABI and interface mismatch errors */ |
||||||
|
if(!(_converterManager.load("BcDecImageConverter") & PluginManager::LoadState::Loaded)) |
||||||
|
CORRADE_SKIP("BcDecImageConverter plugin can't be loaded, cannot test decoded image equality."); |
||||||
|
if(!(_importerManager.load("AnyImageImporter") & PluginManager::LoadState::Loaded) || |
||||||
|
!(_importerManager.load("PngImporter") & PluginManager::LoadState::Loaded)) |
||||||
|
CORRADE_SKIP("AnyImageImporter / PngImporter plugin can't be loaded, cannot test decoded image equality."); |
||||||
|
|
||||||
|
/* The flipped output should be exactly the same after decoding as an
|
||||||
|
Y-flipped decoded input */ |
||||||
|
Containers::Pointer<Trade::AbstractImageConverter> decoder = _converterManager.loadAndInstantiate("BcDecImageConverter"); |
||||||
|
Containers::Optional<Trade::ImageData2D> decoded = decoder->convert(CompressedImageView2D{data.format, data.blockCount*compressedPixelFormatBlockSize(data.format).xy(), blocks}); |
||||||
|
CORRADE_VERIFY(decoded); |
||||||
|
|
||||||
|
if(decoded->format() == PixelFormat::RGBA8Unorm) |
||||||
|
CORRADE_COMPARE_WITH(decoded->pixels<Vector4ub>().flipped<0>(), |
||||||
|
Corrade::Utility::Path::join(COLORBATCH_TEST_DIR, data.file), |
||||||
|
(DebugTools::CompareImageToFile{_importerManager, _converterManager})); |
||||||
|
else if(decoded->format() == PixelFormat::RG8Unorm) |
||||||
|
CORRADE_COMPARE_WITH(decoded->pixels<Vector2ub>().flipped<0>(), |
||||||
|
Corrade::Utility::Path::join(COLORBATCH_TEST_DIR, data.file), |
||||||
|
(DebugTools::CompareImageToFile{_importerManager, _converterManager})); |
||||||
|
else if(decoded->format() == PixelFormat::R8Unorm) |
||||||
|
CORRADE_COMPARE_WITH(decoded->pixels<UnsignedByte>().flipped<0>(), |
||||||
|
Corrade::Utility::Path::join(COLORBATCH_TEST_DIR, data.file), |
||||||
|
(DebugTools::CompareImageToFile{_importerManager, _converterManager})); |
||||||
|
else CORRADE_FAIL("Unexpected format" << decoded->format()); |
||||||
|
} |
||||||
|
|
||||||
|
void ColorBatchTest::yFlip3D() { |
||||||
|
/* Copy to a mutable array first to operate in-place */ |
||||||
|
Containers::Array<char> blocks{Corrade::NoInit, Containers::arraySize(CheckerboardBC1)}; |
||||||
|
Utility::copy(CheckerboardBC1, blocks); |
||||||
|
|
||||||
|
/* The 2D 4x6 blocks image is turned into 4 slices of 1x6 blocks each.
|
||||||
|
Y flipping should result in the exact same result as in the 2D case. */ |
||||||
|
Containers::StridedArrayView4D<char> view{blocks, |
||||||
|
{4, 6, 1, 8}, |
||||||
|
{1*8, /* Each slice is 1 block wide */ |
||||||
|
4*8, /* Each row is still 4 blocks */ |
||||||
|
8, /* Each block is 8 bytes */ |
||||||
|
1} |
||||||
|
}; |
||||||
|
yFlipBc1InPlace(view); |
||||||
|
|
||||||
|
CORRADE_COMPARE_AS(blocks, |
||||||
|
Containers::arrayView(CheckerboardBC1Flipped), |
||||||
|
TestSuite::Compare::Container); |
||||||
|
} |
||||||
|
|
||||||
|
void ColorBatchTest::yFlipInvalidLastDimension() { |
||||||
|
CORRADE_SKIP_IF_NO_ASSERT(); |
||||||
|
|
||||||
|
/* All formats delegate to the same template code with the assertions so
|
||||||
|
it's enough to test just some */ |
||||||
|
char data[32]; |
||||||
|
|
||||||
|
std::ostringstream out; |
||||||
|
Error redirectError{&out}; |
||||||
|
yFlipBc3InPlace(Containers::stridedArrayView(data).expanded<0>(Containers::Size3D{1, 4, 8})); |
||||||
|
yFlipBc1InPlace(Containers::stridedArrayView(data).expanded<0>(Containers::Size3D{1, 2, 16}).every({1, 1, 2})); |
||||||
|
CORRADE_COMPARE(out.str(), |
||||||
|
"Math::yFlipBc3InPlace(): expected last dimension to be 16 bytes but got 8\n" |
||||||
|
"Math::yFlipBc1InPlace(): last dimension is not contiguous\n"); |
||||||
|
} |
||||||
|
|
||||||
|
}}}} |
||||||
|
|
||||||
|
CORRADE_TEST_MAIN(Magnum::Math::Test::ColorBatchTest) |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
The `checkerboard.png` is exported as `checkerboard.in.png` with Inkscape from |
||||||
|
`Shaders/Test/TestFiles/checkerboard.svg` with: |
||||||
|
|
||||||
|
- the non-alpha checkerboard layer enabled |
||||||
|
- the bold text layer enabled |
||||||
|
- the uneven top right area selected |
||||||
|
- DPI set to 3, resulting in a 32x24 image (8x6 blocks) |
||||||
|
|
||||||
|
The `checkerboard-odd.png` is exported as `checkerboard-odd.in.png` with the |
||||||
|
even top right area selected and the size set to 28x28, resulting in 7x7 |
||||||
|
blocks. |
||||||
|
|
||||||
|
Both are then passed through StbDxtImageConverter and subsequently decoded |
||||||
|
back to `checkerboard.png` and `checkerboard-odd.png` with |
||||||
|
`compress-format-blocks.py` to act as a ground truth, the output of the script |
||||||
|
and its full invocation is pasted to the `ColorBatchTest.cpp`. |
||||||
|
|
||||||
|
The remaining images are generated with `extract-interesting-blocks.py`, the |
||||||
|
output of that script and its full invocation is again in `ColorBatchTest.cpp`. |
||||||
|
|
||||||
|
Unlike in other tests, the images are PNGs and not TGAs. The full test process |
||||||
|
requires `BcDecImageConverter` from the plugins repo anyway, so there's no |
||||||
|
point in having larger TGAs. |
||||||
|
After Width: | Height: | Size: 232 B |
|
After Width: | Height: | Size: 189 B |
|
After Width: | Height: | Size: 113 B |
|
After Width: | Height: | Size: 146 B |
|
After Width: | Height: | Size: 460 B |
|
After Width: | Height: | Size: 412 B |
|
After Width: | Height: | Size: 542 B |
|
After Width: | Height: | Size: 355 B |
@ -0,0 +1,127 @@ |
|||||||
|
#!/usr/bin/python3 |
||||||
|
|
||||||
|
import argparse |
||||||
|
from typing import List, Tuple |
||||||
|
|
||||||
|
from corrade import containers |
||||||
|
from magnum import * |
||||||
|
from magnum import math, trade |
||||||
|
|
||||||
|
parser = argparse.ArgumentParser() |
||||||
|
parser.add_argument('input') |
||||||
|
parser.add_argument('output') |
||||||
|
parser.add_argument('--offset', type=int, default=0) |
||||||
|
parser.add_argument('--count', type=int, default=4) |
||||||
|
args = parser.parse_args() |
||||||
|
|
||||||
|
importer_manager = trade.ImporterManager() |
||||||
|
importer_manager.metadata('DdsImporter').configuration['assumeYUpZBackward'] = True |
||||||
|
# TODO something for KTX as well so it doesn't warn (or flip) |
||||||
|
importer = importer_manager.load_and_instantiate('AnyImageImporter') |
||||||
|
importer.open_file(args.input) |
||||||
|
|
||||||
|
image = importer.image2d(0) |
||||||
|
|
||||||
|
assert image.is_compressed, "expected a compressed image" |
||||||
|
|
||||||
|
print(f"format: {image.compressed_format}") |
||||||
|
|
||||||
|
block_size = image.compressed_format.block_size |
||||||
|
assert image.size % block_size.xy == Vector2i(0), "expected whole blocks" |
||||||
|
|
||||||
|
block_data_size = image.compressed_format.block_data_size |
||||||
|
# TODO use image.blocks once it exists |
||||||
|
blocks = containers.StridedArrayView1D(image.data).expanded(0, ( |
||||||
|
image.size.y//block_size.y, |
||||||
|
image.size.x//block_size.x, |
||||||
|
block_data_size |
||||||
|
)) |
||||||
|
|
||||||
|
if image.compressed_format.name.startswith('BC'): |
||||||
|
decoder_name = 'BcDecImageConverter' |
||||||
|
elif image.compressed_format.name.startswith('ETC') or \ |
||||||
|
image.compressed_format.name.startswith('EAC'): |
||||||
|
decoder_name = 'EtcDecImageConverter' |
||||||
|
else: assert False, f"unsupported format {image.compressed_format}" |
||||||
|
|
||||||
|
converter_manager = trade.ImageConverterManager() |
||||||
|
decoder = converter_manager.load_and_instantiate(decoder_name) |
||||||
|
decoded = decoder.convert(image) |
||||||
|
assert decoded.size == image.size |
||||||
|
# TODO some better way to get this directly from pixels? i.e., want to access |
||||||
|
# the bytes and not the high-level type |
||||||
|
# assert decoded.pixels.is_contiguous |
||||||
|
decoded_pixel_size = decoded.format.size |
||||||
|
decoded_pixel_data = containers.StridedArrayView1D(decoded.data).expanded(0, ( |
||||||
|
image.size.y, |
||||||
|
image.size.x, |
||||||
|
decoded_pixel_size |
||||||
|
)) |
||||||
|
|
||||||
|
block_count = decoded.size/block_size.xy |
||||||
|
|
||||||
|
pixels = decoded.pixels |
||||||
|
|
||||||
|
if decoded.format.channel_count == 1: |
||||||
|
default_min = math.inf |
||||||
|
default_max = -math.inf |
||||||
|
sum_ = lambda i: i |
||||||
|
elif decoded.format.channel_count == 2: |
||||||
|
default_min = Vector2(math.inf) |
||||||
|
default_max = Vector2(-math.inf) |
||||||
|
sum_ = lambda i: i.sum() |
||||||
|
elif decoded.format.channel_count == 3: |
||||||
|
default_min = Vector3(math.inf) |
||||||
|
default_max = Vector3(-math.inf) |
||||||
|
sum_ = lambda i: i.sum() |
||||||
|
elif decoded.format.channel_count == 4: |
||||||
|
default_min = Vector4(math.inf) |
||||||
|
default_max = Vector4(-math.inf) |
||||||
|
sum_ = lambda i: i.sum() |
||||||
|
|
||||||
|
block_ranges: List[Tuple[int, int, float]] = [] |
||||||
|
|
||||||
|
# This is extremely slow. Pybind11 ugh what are you doing?! |
||||||
|
for y in range(block_count.y): |
||||||
|
oy = y*block_size.y |
||||||
|
for x in range(block_count.x): |
||||||
|
ox = x*block_size.x |
||||||
|
min_ = default_min |
||||||
|
max_ = default_max |
||||||
|
for by in range(block_size.y): |
||||||
|
for bx in range(block_size.x): |
||||||
|
min_ = math.min(pixels[oy + by, ox + bx], min_) |
||||||
|
max_ = math.max(pixels[oy + by, ox + bx], max_) |
||||||
|
|
||||||
|
block_ranges += [(x, y, sum_(max_ - min_))] |
||||||
|
|
||||||
|
# Get most interesting blocks |
||||||
|
most_interesting_blocks = list(reversed(sorted(block_ranges, key=lambda block: block[2])))[args.offset:(args.offset + args.count)] |
||||||
|
|
||||||
|
print(" char blocks[]{") |
||||||
|
for x, y, range_ in most_interesting_blocks: |
||||||
|
print(" /* [{}, {}], {:1.3f} */\n {},".format( |
||||||
|
x, y, range_, |
||||||
|
', '.join(["'\\x{:02x}'".format(ord(blocks[y, x, i])) for i in range(block_data_size)]) |
||||||
|
)) |
||||||
|
print(" };") |
||||||
|
|
||||||
|
# Combine the decoded pixels from the most interesting blocks |
||||||
|
data = bytearray(b'\0'*(args.count*block_size.x*block_size.y*decoded_pixel_size)) |
||||||
|
offset = 0 |
||||||
|
for x, y, range_ in most_interesting_blocks: |
||||||
|
oy = y*block_size.y |
||||||
|
ox = x*block_size.x |
||||||
|
for by in range(block_size.y): |
||||||
|
for bx in range(block_size.x): |
||||||
|
for i in range(decoded_pixel_size): |
||||||
|
data[offset] = ord(decoded_pixel_data[oy + by, ox + bx, i]) |
||||||
|
offset += 1 |
||||||
|
assert offset == len(data) |
||||||
|
|
||||||
|
# Write them to a file |
||||||
|
converter = converter_manager.load_and_instantiate('AnyImageConverter') |
||||||
|
converter.convert_to_file(ImageView2D( |
||||||
|
decoded.format, |
||||||
|
(block_size.x, block_size.y*args.count), |
||||||
|
data), args.output) |
||||||
@ -0,0 +1,52 @@ |
|||||||
|
#!/usr/bin/python3 |
||||||
|
|
||||||
|
import argparse |
||||||
|
|
||||||
|
from corrade import containers |
||||||
|
from magnum import * |
||||||
|
from magnum import trade |
||||||
|
|
||||||
|
parser = argparse.ArgumentParser() |
||||||
|
parser.add_argument('input') |
||||||
|
parser.add_argument('output') |
||||||
|
args = parser.parse_args() |
||||||
|
|
||||||
|
importer_manager = trade.ImporterManager() |
||||||
|
importer = importer_manager.load_and_instantiate('StbImageImporter') |
||||||
|
# Don't want the alpha in order to have just 64-bit blocks |
||||||
|
importer.configuration['forceChannelCount'] = 3 |
||||||
|
importer.open_file(args.input) |
||||||
|
|
||||||
|
image = importer.image2d(0) |
||||||
|
print(f"format: {image.format}") |
||||||
|
|
||||||
|
converter_manager = trade.ImageConverterManager() |
||||||
|
dxt_encoder = converter_manager.load_and_instantiate('StbDxtImageConverter') |
||||||
|
dxt_encoder.configuration['highQuality'] = True |
||||||
|
|
||||||
|
compressed_image = dxt_encoder.convert(image) |
||||||
|
print(f"compressed format: {compressed_image.compressed_format}") |
||||||
|
|
||||||
|
block_size = compressed_image.compressed_format.block_size |
||||||
|
block_data_size = compressed_image.compressed_format.block_data_size |
||||||
|
assert compressed_image.size % block_size.xy == Vector2i(0), "expected whole blocks" |
||||||
|
# TODO use image.blocks once it exists |
||||||
|
blocks = containers.StridedArrayView1D(compressed_image.data).expanded(0, ( |
||||||
|
compressed_image.size.y//block_size.y, |
||||||
|
compressed_image.size.x//block_size.x, |
||||||
|
block_data_size |
||||||
|
)) |
||||||
|
|
||||||
|
print(" char blocks[]{") |
||||||
|
for y in range(compressed_image.size.y//block_size.y): |
||||||
|
for x in range(compressed_image.size.x//block_size.x): |
||||||
|
print(" {},".format( |
||||||
|
', '.join(["'\\x{:02x}'".format(ord(blocks[y, x, i])) for i in range(block_data_size)]) |
||||||
|
)) |
||||||
|
print(" };") |
||||||
|
|
||||||
|
decoder = converter_manager.load_and_instantiate('BcDecImageConverter') |
||||||
|
decoded_image = decoder.convert(compressed_image) |
||||||
|
|
||||||
|
png_converter = converter_manager.load_and_instantiate('AnyImageConverter') |
||||||
|
png_converter.convert_to_file(decoded_image, args.output) |
||||||
@ -0,0 +1,48 @@ |
|||||||
|
/* |
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
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. |
||||||
|
*/ |
||||||
|
|
||||||
|
#define COLORBATCH_TEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ColorBatchTestFiles" |
||||||
|
|
||||||
|
#ifdef CORRADE_TARGET_WINDOWS |
||||||
|
#ifdef CORRADE_IS_DEBUG_BUILD |
||||||
|
#define MAGNUM_PLUGINS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}" |
||||||
|
#define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_DEBUG_BINARY_INSTALL_DIR}" |
||||||
|
#define MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_BINARY_INSTALL_DIR}" |
||||||
|
#else |
||||||
|
#define MAGNUM_PLUGINS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}" |
||||||
|
#define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_RELEASE_BINARY_INSTALL_DIR}" |
||||||
|
#define MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_BINARY_INSTALL_DIR}" |
||||||
|
#endif |
||||||
|
#else |
||||||
|
#ifdef CORRADE_IS_DEBUG_BUILD |
||||||
|
#define MAGNUM_PLUGINS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR}" |
||||||
|
#define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_DEBUG_LIBRARY_INSTALL_DIR}" |
||||||
|
#define MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_LIBRARY_INSTALL_DIR}" |
||||||
|
#else |
||||||
|
#define MAGNUM_PLUGINS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}" |
||||||
|
#define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_RELEASE_LIBRARY_INSTALL_DIR}" |
||||||
|
#define MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_LIBRARY_INSTALL_DIR}" |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
Before Width: | Height: | Size: 266 KiB After Width: | Height: | Size: 365 KiB |