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 |