From afad144b8d26c912057c032728cf4ac233c5e8a3 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 14 Jul 2021 23:37:27 +0200 Subject: [PATCH 01/95] Trade: KtxImporter WIP and missing tests --- CMakeLists.txt | 1 + src/MagnumPlugins/CMakeLists.txt | 4 + src/MagnumPlugins/KtxImporter/CMakeLists.txt | 70 +++ src/MagnumPlugins/KtxImporter/KtxHeader.h | 81 +++ .../KtxImporter/KtxImporter.conf | 0 src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 553 ++++++++++++++++++ src/MagnumPlugins/KtxImporter/KtxImporter.h | 92 +++ .../KtxImporter/configure.h.cmake | 26 + .../KtxImporter/importStaticPlugin.cpp | 35 ++ 9 files changed, 862 insertions(+) create mode 100644 src/MagnumPlugins/KtxImporter/CMakeLists.txt create mode 100644 src/MagnumPlugins/KtxImporter/KtxHeader.h create mode 100644 src/MagnumPlugins/KtxImporter/KtxImporter.conf create mode 100644 src/MagnumPlugins/KtxImporter/KtxImporter.cpp create mode 100644 src/MagnumPlugins/KtxImporter/KtxImporter.h create mode 100644 src/MagnumPlugins/KtxImporter/configure.h.cmake create mode 100644 src/MagnumPlugins/KtxImporter/importStaticPlugin.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 795e1409b..238dd234b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,7 @@ option(WITH_HARFBUZZFONT "Build HarfBuzzFont plugin" OFF) option(WITH_ICOIMPORTER "Build IcoImporter plugin" OFF) option(WITH_JPEGIMAGECONVERTER "Build JpegImageConverter plugin" OFF) option(WITH_JPEGIMPORTER "Build JpegImporter plugin" OFF) +option(WITH_KTXIMPORTER "Build KtxImporter plugin" OFF) option(WITH_MESHOPTIMIZERSCENECONVERTER "Build MeshOptimizerSceneConverter plugin" OFF) option(WITH_MINIEXRIMAGECONVERTER "Build MiniExrImageConverter plugin" OFF) cmake_dependent_option(WITH_OPENDDL "Build OpenDdl library" OFF "NOT WITH_OPENGEXIMPORTER" ON) diff --git a/src/MagnumPlugins/CMakeLists.txt b/src/MagnumPlugins/CMakeLists.txt index fe4f5da87..6b50e6b9b 100644 --- a/src/MagnumPlugins/CMakeLists.txt +++ b/src/MagnumPlugins/CMakeLists.txt @@ -95,6 +95,10 @@ if(WITH_JPEGIMPORTER) add_subdirectory(JpegImporter) endif() +if(WITH_KTXIMPORTER) + add_subdirectory(KtxImporter) +endif() + if(WITH_MESHOPTIMIZERSCENECONVERTER) add_subdirectory(MeshOptimizerSceneConverter) endif() diff --git a/src/MagnumPlugins/KtxImporter/CMakeLists.txt b/src/MagnumPlugins/KtxImporter/CMakeLists.txt new file mode 100644 index 000000000..cc265a4b5 --- /dev/null +++ b/src/MagnumPlugins/KtxImporter/CMakeLists.txt @@ -0,0 +1,70 @@ +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, +# 2020, 2021 Vladimír Vondruš +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# + +find_package(Magnum REQUIRED Trade Vk) + +if(BUILD_PLUGINS_STATIC AND NOT DEFINED MAGNUM_KTXIMPORTER_BUILD_STATIC) + set(MAGNUM_KTXIMPORTER_BUILD_STATIC 1) +endif() + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/configure.h) + +# KtxImporter plugin +add_plugin(KtxImporter + "${MAGNUM_PLUGINS_IMPORTER_DEBUG_BINARY_INSTALL_DIR};${MAGNUM_PLUGINS_IMPORTER_DEBUG_LIBRARY_INSTALL_DIR}" + "${MAGNUM_PLUGINS_IMPORTER_RELEASE_BINARY_INSTALL_DIR};${MAGNUM_PLUGINS_IMPORTER_RELEASE_LIBRARY_INSTALL_DIR}" + KtxImporter.conf + KtxImporter.cpp + KtxImporter.h + KtxHeader.h) +if(MAGNUM_KTXIMPORTER_BUILD_STATIC AND BUILD_STATIC_PIC) + set_target_properties(KtxImporter PROPERTIES POSITION_INDEPENDENT_CODE ON) +endif() +target_include_directories(KtxImporter PUBLIC + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_BINARY_DIR}/src) +target_link_libraries(KtxImporter PUBLIC + Magnum::Trade + Magnum::Vk) +# Modify output location only if all are set, otherwise it makes no sense +if(CMAKE_RUNTIME_OUTPUT_DIRECTORY AND CMAKE_LIBRARY_OUTPUT_DIRECTORY AND CMAKE_ARCHIVE_OUTPUT_DIRECTORY) + set_target_properties(KtxImporter PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/magnum$<$:-d>/importers + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/magnum$<$:-d>/importers + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/magnum$<$:-d>/importers) +endif() + +install(FILES KtxImporter.h ${CMAKE_CURRENT_BINARY_DIR}/configure.h + DESTINATION ${MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR}/KtxImporter) + +# Automatic static plugin import +if(MAGNUM_KTXIMPORTER_BUILD_STATIC) + install(FILES importStaticPlugin.cpp DESTINATION ${MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR}/KtxImporter) + target_sources(KtxImporter INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/importStaticPlugin.cpp) +endif() + +# MagnumPlugins KtxImporter target alias for superprojects +add_library(MagnumPlugins::KtxImporter ALIAS KtxImporter) diff --git a/src/MagnumPlugins/KtxImporter/KtxHeader.h b/src/MagnumPlugins/KtxImporter/KtxHeader.h new file mode 100644 index 000000000..cdf128602 --- /dev/null +++ b/src/MagnumPlugins/KtxImporter/KtxHeader.h @@ -0,0 +1,81 @@ +#ifndef Magnum_Trade_KtxHeader_h +#define Magnum_Trade_KtxHeader_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "Magnum/Types.h" + +/* Used by both KtxImporter and KtxImageConverter, which is why it isn't + directly inside KtxImporter.cpp. OTOH it doesn't need to be exposed + publicly, which is why it has no docblocks. */ + +namespace Magnum { namespace Trade { namespace Implementation { + +#pragma pack(1) + +/* KTX2 file header */ +struct KtxHeader { + UnsignedByte identifier[12]; /* File identifier */ + UnsignedInt vkFormat; /* VkFormat enum value, VK_FORMAT_UNDEFINED = custom */ + UnsignedInt typeSize; /* Size of data type in bytes */ + UnsignedInt pixelWidth; /* Image level 0 width */ + UnsignedInt pixelHeight; /* Image level 0 height */ + UnsignedInt pixelDepth; /* Image level 0 depth */ + UnsignedInt layerCount; /* Number of array elements */ + UnsignedInt faceCount; /* Number of cubemap faces */ + UnsignedInt levelCount; /* Number of mip levels */ + UnsignedInt supercompressionScheme; + /* Index */ + UnsignedInt dfdByteOffset; /* Offset of Data Format Descriptor */ + UnsignedInt dfdByteLength; /* Length of Data Format Descriptor */ + UnsignedInt kvdByteOffset; /* Offset of Key/Value Data */ + UnsignedInt kvdByteLength; /* Length of Key/Value Data */ + UnsignedLong sgdByteOffset; /* Offset of Supercompression Global Data */ + UnsignedLong sgdByteLength; /* Length of Supercompression Global Data */ +}; + +static_assert(sizeof(KtxHeader) == 80, "Improper size of KtxHeader struct"); + +/* KTX2 mip level index element */ +struct KtxLevel { + UnsignedLong byteOffset; /* Offset of first byte of image data */ + UnsignedLong byteLength; /* Total size of image data */ + UnsignedLong uncompressedByteLength; /* Total size of image data before supercompression */ +}; + +static_assert(sizeof(KtxLevel) == 24, "Improper size of KtxLevel struct"); + +#pragma pack() + +constexpr UnsignedByte KtxFileIdentifier[12]{ + /* "«KTX 20»\r\n\x1A\n" */ + 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A +}; + +static_assert(sizeof(KtxFileIdentifier) == sizeof(KtxHeader::identifier), "Improper size of KtxFileIdentifier data"); + +}}} + +#endif diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.conf b/src/MagnumPlugins/KtxImporter/KtxImporter.conf new file mode 100644 index 000000000..e69de29bb diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp new file mode 100644 index 000000000..1c034fd2a --- /dev/null +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -0,0 +1,553 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "KtxImporter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "MagnumPlugins/KtxImporter/KtxHeader.h" + +namespace Magnum { namespace Trade { + +namespace { + +std::size_t imageLength(const Vector3i& size, PixelFormat format) { + return size.product()*pixelSize(format); +} + +/** @todo Use CompressedPixelStorage::dataProperties for this */ +std::size_t imageLength(const Vector3i& size, CompressedPixelFormat format) { + const Vector3i blockSize = compressedBlockSize(format); + const Vector3i blockCount = (size + (blockSize - Vector3i{1})) / blockSize; + return blockCount.product()*compressedBlockDataSize(format); +} + +void endianSwap(Containers::ArrayView data, UnsignedInt typeSize) { + switch(typeSize) { + case 1: + /* Single-byte or block-compressed format, nothing to do */ + break; + case 2: + Utility::Endianness::littleEndianInPlace(Containers::arrayCast(data)); + break; + case 4: + Utility::Endianness::littleEndianInPlace(Containers::arrayCast(data)); + break; + case 8: + Utility::Endianness::littleEndianInPlace(Containers::arrayCast(data)); + break; + default: + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + break; + } +} + +void swizzlePixels(const PixelFormat format, Containers::Array& data, const char* verbosePrefix) { + if(format == PixelFormat::RGB8Unorm) { + if(verbosePrefix) Debug{} << verbosePrefix << "converting from BGR to RGB"; + auto pixels = reinterpret_cast*>(data.data()); + std::transform(pixels, pixels + data.size()/sizeof(Math::Vector3), pixels, + [](Math::Vector3 pixel) { return Math::gather<'b', 'g', 'r'>(pixel); }); + } else if(format == PixelFormat::RGBA8Unorm) { + if(verbosePrefix) Debug{} << verbosePrefix << "converting from BGRA to RGBA"; + auto pixels = reinterpret_cast*>(data.data()); + std::transform(pixels, pixels + data.size()/sizeof(Math::Vector4), pixels, + [](Math::Vector4 pixel) { return Math::gather<'b', 'g', 'r', 'a'>(pixel); }); + + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ +} + +/** @todo Swizzle channels if necessary. Vk::PixelFormat doesn't contain any + of the swizzled formats like B8G8R8A8. */ +/** @todo Support all Vulkan formats allowed by the KTX spec. Create custom + PixelFormat with pixelFormatWrap and manually fill PixelStorage/ + CompressedPixelStorage. We can take all the necessary info from + https://github.com/KhronosGroup/KTX-Specification/blob/master/formats.json + Do we also need this for the KtxImageConverter? This would allow + users to pass in images with implementation-specific PixelFormat + using the Vulkan format enum directly. */ +PixelFormat pixelFormat(Vk::PixelFormat format) { + #define _c(val) case Vk::PixelFormat:: ## val: return PixelFormat:: ## val; + switch(format) { + _c(R8Unorm) + _c(RG8Unorm) + _c(RGB8Unorm) + _c(RGBA8Unorm) + _c(R8Snorm) + _c(RG8Snorm) + _c(RGB8Snorm) + _c(RGBA8Snorm) + _c(R8Srgb) + _c(RG8Srgb) + _c(RGB8Srgb) + _c(RGBA8Srgb) + _c(R8UI) + _c(RG8UI) + _c(RGB8UI) + _c(RGBA8UI) + _c(R8I) + _c(RG8I) + _c(RGB8I) + _c(RGBA8I) + _c(R16Unorm) + _c(RG16Unorm) + _c(RGB16Unorm) + _c(RGBA16Unorm) + _c(R16Snorm) + _c(RG16Snorm) + _c(RGB16Snorm) + _c(RGBA16Snorm) + _c(R16UI) + _c(RG16UI) + _c(RGB16UI) + _c(RGBA16UI) + _c(R16I) + _c(RG16I) + _c(RGB16I) + _c(RGBA16I) + _c(R32UI) + _c(RG32UI) + _c(RGB32UI) + _c(RGBA32UI) + _c(R32I) + _c(RG32I) + _c(RGB32I) + _c(RGBA32I) + _c(R16F) + _c(RG16F) + _c(RGB16F) + _c(RGBA16F) + _c(R32F) + _c(RG32F) + _c(RGB32F) + _c(RGBA32F) + _c(Depth16Unorm) + _c(Depth24Unorm) + _c(Depth32F) + _c(Stencil8UI) + _c(Depth16UnormStencil8UI) + _c(Depth24UnormStencil8UI) + _c(Depth32FStencil8UI) + default: + return {}; + } + #undef _c +} + +CompressedPixelFormat compressedPixelFormat(Vk::PixelFormat format) { + #define _c(val) case Vk::PixelFormat::Compressed ## val: return CompressedPixelFormat:: ## val; + switch(format) { + _c(Bc1RGBUnorm) + _c(Bc1RGBSrgb) + _c(Bc1RGBAUnorm) + _c(Bc1RGBASrgb) + _c(Bc2RGBAUnorm) + _c(Bc2RGBASrgb) + _c(Bc3RGBAUnorm) + _c(Bc3RGBASrgb) + _c(Bc4RUnorm) + _c(Bc4RSnorm) + _c(Bc5RGUnorm) + _c(Bc5RGSnorm) + _c(Bc6hRGBUfloat) + _c(Bc6hRGBSfloat) + _c(Bc7RGBAUnorm) + _c(Bc7RGBASrgb) + _c(EacR11Unorm) + _c(EacR11Snorm) + _c(EacRG11Unorm) + _c(EacRG11Snorm) + _c(Etc2RGB8Unorm) + _c(Etc2RGB8Srgb) + _c(Etc2RGB8A1Unorm) + _c(Etc2RGB8A1Srgb) + _c(Etc2RGBA8Unorm) + _c(Etc2RGBA8Srgb) + _c(Astc4x4RGBAUnorm) + _c(Astc4x4RGBASrgb) + _c(Astc4x4RGBAF) + _c(Astc5x4RGBAUnorm) + _c(Astc5x4RGBASrgb) + _c(Astc5x4RGBAF) + _c(Astc5x5RGBAUnorm) + _c(Astc5x5RGBASrgb) + _c(Astc5x5RGBAF) + _c(Astc6x5RGBAUnorm) + _c(Astc6x5RGBASrgb) + _c(Astc6x5RGBAF) + _c(Astc6x6RGBAUnorm) + _c(Astc6x6RGBASrgb) + _c(Astc6x6RGBAF) + _c(Astc8x5RGBAUnorm) + _c(Astc8x5RGBASrgb) + _c(Astc8x5RGBAF) + _c(Astc8x6RGBAUnorm) + _c(Astc8x6RGBASrgb) + _c(Astc8x6RGBAF) + _c(Astc8x8RGBAUnorm) + _c(Astc8x8RGBASrgb) + _c(Astc8x8RGBAF) + _c(Astc10x5RGBAUnorm) + _c(Astc10x5RGBASrgb) + _c(Astc10x5RGBAF) + _c(Astc10x6RGBAUnorm) + _c(Astc10x6RGBASrgb) + _c(Astc10x6RGBAF) + _c(Astc10x8RGBAUnorm) + _c(Astc10x8RGBASrgb) + _c(Astc10x8RGBAF) + _c(Astc10x10RGBAUnorm) + _c(Astc10x10RGBASrgb) + _c(Astc10x10RGBAF) + _c(Astc12x10RGBAUnorm) + _c(Astc12x10RGBASrgb) + _c(Astc12x10RGBAF) + _c(Astc12x12RGBAUnorm) + _c(Astc12x12RGBASrgb) + _c(Astc12x12RGBAF) + _c(PvrtcRGBA2bppUnorm) + _c(PvrtcRGBA2bppSrgb) + _c(PvrtcRGBA4bppUnorm) + _c(PvrtcRGBA4bppSrgb) + /* CompressedPixelFormat has no Pvrtc2 */ + /* + _c(Pvrtc2RGBA2bppUnorm) + _c(Pvrtc2RGBA2bppSrgb) + _c(Pvrtc2RGBA4bppUnorm) + _c(Pvrtc2RGBA4bppSrgb) + */ + default: + return {}; + } + #undef _c +} + +} + +struct KtxImporter::File { + struct LevelData { + Vector3i size; + Containers::ArrayView data; + }; + + Containers::Array in; + + UnsignedByte dimensions; + bool needsSwizzle; + + union { + PixelFormat uncompressed; + CompressedPixelFormat compressed; + } pixelFormat; + bool compressed; + UnsignedInt typeSize; + + /* Each array layer is an image with faces and mipmaps as levels */ + Containers::Array> imageData; +}; + +KtxImporter::KtxImporter() = default; + +KtxImporter::KtxImporter(PluginManager::AbstractManager& manager, const std::string& plugin): AbstractImporter{manager, plugin} {} + +KtxImporter::~KtxImporter() = default; + +ImporterFeatures KtxImporter::doFeatures() const { return ImporterFeature::OpenData; } + +bool KtxImporter::doIsOpened() const { return !!_f; } + +void KtxImporter::doClose() { _f = nullptr; } + +void KtxImporter::doOpenData(const Containers::ArrayView data) { + /* Check if the file is long enough */ + if(data.size() < sizeof(Implementation::KtxHeader)) { + Error{} << "Trade::KtxImporter::openData(): file header too short, expected at least" << sizeof(Implementation::KtxHeader) << "bytes but got" << data.size(); + return; + } + + Implementation::KtxHeader header = *reinterpret_cast(data.data()); + std::size_t offset = sizeof(Implementation::KtxHeader); + + /* Members after supercompressionScheme need endian swapping as well, + but we don't need them for now. */ + Utility::Endianness::littleEndianInPlace( + header.vkFormat, header.typeSize, + header.pixelWidth, header.pixelHeight, header.pixelDepth, + header.layerCount, header.faceCount, header.levelCount, + header.supercompressionScheme); + + /* Check magic string to verify this is a KTX file */ + if(std::memcmp(&header.identifier, Implementation::KtxFileIdentifier, sizeof(header.identifier)) != 0) { + Error() << "Trade::KtxImporter::openData(): wrong file signature"; + return; + } + + Containers::Pointer f{new File}; + f->in = Containers::Array{NoInit, data.size()}; + Utility::copy(data, f->in); + + /** @todo Support Basis compression */ + const PixelFormat format = pixelFormat(Vk::PixelFormat(header.vkFormat)); + if(format == PixelFormat{}) { + const CompressedPixelFormat compressedFormat = compressedPixelFormat(Vk::PixelFormat(header.vkFormat)); + if(compressedFormat == CompressedPixelFormat{}) { + Error{} << "Trade::KtxImporter::openData(): unsupported format" << header.vkFormat; + return; + } + f->pixelFormat.compressed = compressedFormat; + f->compressed = true; + } else { + f->pixelFormat.uncompressed = format; + f->compressed = false; + } + + /** @todo Determine if format needs swizzling */ + f->needsSwizzle = false; + + /* typeSize is the size of the format's underlying type, not the texel + size, e.g. 2 for RG16F. For any sane format it should be a + power-of-two between 1 and 8. */ + if(header.typeSize < 1 || header.typeSize > 8 || + (header.typeSize & (header.typeSize - 1))) + { + Error{} << "Trade::KtxImporter::openData(): unsupported type size" << header.typeSize; + return; + } + /* Block-compressed formats must have typeSize set to 1 */ + CORRADE_INTERNAL_ASSERT(!f->compressed || header.typeSize == 1); + f->typeSize = header.typeSize; + + /** @todo Support supercompression */ + if(header.supercompressionScheme != 0) { + Error{} << "Trade::KtxImporter::openData(): supercompression is not supported"; + return; + } + + const Vector3ui originalSize{header.pixelWidth, header.pixelHeight, header.pixelDepth}; + CORRADE_INTERNAL_ASSERT(originalSize.x() > 0); + + if(originalSize.z() > 0) { + CORRADE_INTERNAL_ASSERT(originalSize.y() > 0); + f->dimensions = 3; + } else if(originalSize.y() > 0) + f->dimensions = 2; + else + f->dimensions = 1; + + /** @todo Assert that 3D images can't have depth format */ + + /* Make size in each dimension at least 1 so we don't choke on size + calculations using product(). */ + const Vector3i size = Math::max(Vector3i{originalSize}, Vector3i{1}); + + /* Number of array layers, imported as separate images */ + const UnsignedInt numLayers = Math::max(header.layerCount, 1u); + + /* Cubemap faces, either 1 or 6. KTX allows incomplete cubemaps but those + have to be array layers with extra metadata inside key/value data. */ + const UnsignedInt numFaces = header.faceCount; + if(numFaces != 1) { + CORRADE_INTERNAL_ASSERT(numFaces == 6); + /* Cubemaps must be 2D and square */ + CORRADE_INTERNAL_ASSERT(f->dimensions == 2); + CORRADE_INTERNAL_ASSERT(size.x() == size.y()); + } + + const UnsignedInt numMipmaps = Math::max(header.levelCount, 1u); + + /* KTX stores byte ranges for each mipmap, from largest to smallest. The + actual pixel data is usually arranged in reverse order for streaming + but we only need the ranges. */ + const std::size_t levelIndexSize = numMipmaps * sizeof(Implementation::KtxLevel); + if(f->in.suffix(offset).size() < levelIndexSize) { + Error{} << "Trade::KtxImporter::openData(): image header too short, expected at least" << offset + levelIndexSize << "bytes but got" << data.size(); + return; + } + const auto levelIndex = Containers::arrayCast(f->in.slice(offset, offset + levelIndexSize)); + for(Implementation::KtxLevel& level: levelIndex) { + Utility::Endianness::littleEndianInPlace(level.byteOffset, level.byteLength, level.uncompressedByteLength); + CORRADE_INTERNAL_ASSERT(header.supercompressionScheme != 0 || level.byteLength == level.uncompressedByteLength); + CORRADE_INTERNAL_ASSERT(level.uncompressedByteLength % (numFaces * numLayers) == 0); + if(data.size() < level.byteOffset + level.byteLength) { + Error{} << "Trade::KtxImporter::openData(): mip data too short, expected at least" << level.byteOffset + level.byteLength << "bytes but got" << data.size(); + return; + } + } + + f->imageData = Containers::Array>{numLayers}; + + for(UnsignedInt layer = 0; layer != numLayers; ++layer) { + f->imageData[layer] = Containers::Array{numFaces * numMipmaps}; + /* This matches the image order of DdsImporter: faces, mipmaps */ + std::size_t currentLevel = 0; + for(UnsignedInt face = 0; face != numFaces; ++face) { + Vector3i mipSize{size}; + + /* Load all mipmaps for current face */ + for(const Implementation::KtxLevel& level: levelIndex) { + const std::size_t length = f->compressed + ? imageLength(mipSize, f->pixelFormat.compressed) + : imageLength(mipSize, f->pixelFormat.uncompressed); + + /* Each mipmap byte range contains tightly packed images, + ordered as follows: + layers, faces, (slices, rows, columns) */ + const std::size_t imageOffset = (layer * numFaces + face) * length; + if(level.byteLength < imageOffset + length) { + Error{} << "Trade::KtxImporter::openData(): image data too short, expected at least" << imageOffset + length << "bytes but got" << level.byteLength; + return; + } + + f->imageData[layer][currentLevel++] = {mipSize, f->in.suffix(level.byteOffset).slice(imageOffset, imageOffset + length)}; + + /* Shrink to next power of 2 */ + mipSize = Math::max(mipSize >> 1, Vector3i{1}); + } + } + } + + /** @todo Read KTXorientation and flip image? What is the default? */ + /** @todo Read KTXswizzle and apply it. Can't do that for block-compressed formats, just ignore in that case? */ + /** @todo Read KTXanimData and expose frame time between images through + doImporterState (same as StbImageImporter). What if we need + doImporterState for something else, like other API format + enum values (KTXdxgiFormat__ etc.)? */ + + _f = std::move(f); +} + +UnsignedInt KtxImporter::doImage1DCount() const { return (_f->dimensions == 1) ? _f->imageData.size() : 0; } + +UnsignedInt KtxImporter::doImage1DLevelCount(UnsignedInt id) { return _f->imageData[id].size(); } + +Containers::Optional KtxImporter::doImage1D(UnsignedInt id, UnsignedInt level) { + const File::LevelData& levelData = _f->imageData[id][level]; + + /* Copy image data */ + Containers::Array data{NoInit, levelData.data.size()}; + Utility::copy(levelData.data, data); + + /* Endian-swap if necessary */ + endianSwap(data, _f->typeSize); + + /* Compressed image */ + if(_f->compressed) { + /* Using default CompressedPixelStorage, need explicit one once we + support all Vulkan formats */ + return ImageData1D(_f->pixelFormat.compressed, levelData.size.x(), std::move(data)); + } + + /* Uncompressed */ + if(_f->needsSwizzle) swizzlePixels(_f->pixelFormat.uncompressed, data, + flags() & ImporterFlag::Verbose ? "Trade::KtxImporter::image2D():" : nullptr); + + /* Rows are tightly packed */ + PixelStorage storage; + storage.setAlignment(1); + + return ImageData1D{storage, _f->pixelFormat.uncompressed, levelData.size.x(), std::move(data)}; +} + +UnsignedInt KtxImporter::doImage2DCount() const { return (_f->dimensions == 2) ? _f->imageData.size() : 0; } + +UnsignedInt KtxImporter::doImage2DLevelCount(UnsignedInt id) { return _f->imageData[id].size(); } + +Containers::Optional KtxImporter::doImage2D(UnsignedInt id, UnsignedInt level) { + const File::LevelData& levelData = _f->imageData[id][level]; + + /* Copy image data */ + Containers::Array data{NoInit, levelData.data.size()}; + Utility::copy(levelData.data, data); + + /* Endian-swap if necessary */ + endianSwap(data, _f->typeSize); + + /* Compressed image */ + if(_f->compressed) { + /* Using default CompressedPixelStorage, need explicit one once we + support all Vulkan formats */ + return ImageData2D(_f->pixelFormat.compressed, levelData.size.xy(), std::move(data)); + } + + /* Uncompressed */ + if(_f->needsSwizzle) swizzlePixels(_f->pixelFormat.uncompressed, data, + flags() & ImporterFlag::Verbose ? "Trade::KtxImporter::image2D():" : nullptr); + + /* Rows are tightly packed */ + PixelStorage storage; + storage.setAlignment(1); + + return ImageData2D{storage, _f->pixelFormat.uncompressed, levelData.size.xy(), std::move(data)}; +} + +UnsignedInt KtxImporter::doImage3DCount() const { return (_f->dimensions == 3) ? _f->imageData.size() : 0; } + +UnsignedInt KtxImporter::doImage3DLevelCount(UnsignedInt id) { return _f->imageData[id].size(); } + +Containers::Optional KtxImporter::doImage3D(UnsignedInt id, const UnsignedInt level) { + const File::LevelData& levelData = _f->imageData[id][level]; + + /* Copy image data */ + Containers::Array data{NoInit, levelData.data.size()}; + Utility::copy(levelData.data, data); + + /* Endian-swap if necessary */ + endianSwap(data, _f->typeSize); + + /* Compressed image */ + if(_f->compressed) { + /* Using default CompressedPixelStorage, need explicit one once we + support all Vulkan formats */ + return ImageData3D(_f->pixelFormat.compressed, levelData.size, std::move(data)); + } + + /* Uncompressed */ + if(_f->needsSwizzle) swizzlePixels(_f->pixelFormat.uncompressed, data, + flags() & ImporterFlag::Verbose ? "Trade::KtxImporter::image3D():" : nullptr); + + /* Rows are tightly packed */ + PixelStorage storage; + storage.setAlignment(1); + + return ImageData3D{storage, _f->pixelFormat.uncompressed, levelData.size, std::move(data)}; +} + +}} + +CORRADE_PLUGIN_REGISTER(KtxImporter, Magnum::Trade::KtxImporter, + "cz.mosra.magnum.Trade.AbstractImporter/0.3.3") diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.h b/src/MagnumPlugins/KtxImporter/KtxImporter.h new file mode 100644 index 000000000..1aa717947 --- /dev/null +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.h @@ -0,0 +1,92 @@ +#ifndef Magnum_Trade_KtxImporter_h +#define Magnum_Trade_KtxImporter_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Class @ref Magnum::Trade::KtxImporter + */ + +#include +#include + +#include "Magnum/Trade/AbstractImporter.h" + +#include "MagnumPlugins/KtxImporter/configure.h" + +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_KTXIMPORTER_BUILD_STATIC + #ifdef KtxImporter_EXPORTS + #define MAGNUM_KTXIMPORTER_EXPORT CORRADE_VISIBILITY_EXPORT + #else + #define MAGNUM_KTXIMPORTER_EXPORT CORRADE_VISIBILITY_IMPORT + #endif +#else + #define MAGNUM_KTXIMPORTER_EXPORT CORRADE_VISIBILITY_STATIC +#endif +#define MAGNUM_KTXIMPORTER_LOCAL CORRADE_VISIBILITY_LOCAL +#else +#define MAGNUM_KTXIMPORTER_EXPORT +#define MAGNUM_KTXIMPORTER_LOCAL +#endif + +namespace Magnum { namespace Trade { + +class MAGNUM_KTXIMPORTER_EXPORT KtxImporter: public AbstractImporter { + public: + /** @brief Default constructor */ + explicit KtxImporter(); + + /** @brief Plugin manager constructor */ + explicit KtxImporter(PluginManager::AbstractManager& manager, const std::string& plugin); + + ~KtxImporter(); + + private: + MAGNUM_KTXIMPORTER_LOCAL ImporterFeatures doFeatures() const override; + MAGNUM_KTXIMPORTER_LOCAL bool doIsOpened() const override; + MAGNUM_KTXIMPORTER_LOCAL void doClose() override; + MAGNUM_KTXIMPORTER_LOCAL void doOpenData(Containers::ArrayView data) override; + + MAGNUM_KTXIMPORTER_LOCAL UnsignedInt doImage1DCount() const override; + MAGNUM_KTXIMPORTER_LOCAL UnsignedInt doImage1DLevelCount(UnsignedInt id) override; + MAGNUM_KTXIMPORTER_LOCAL Containers::Optional doImage1D(UnsignedInt id, UnsignedInt level) override; + + MAGNUM_KTXIMPORTER_LOCAL UnsignedInt doImage2DCount() const override; + MAGNUM_KTXIMPORTER_LOCAL UnsignedInt doImage2DLevelCount(UnsignedInt id) override; + MAGNUM_KTXIMPORTER_LOCAL Containers::Optional doImage2D(UnsignedInt id, UnsignedInt level) override; + + MAGNUM_KTXIMPORTER_LOCAL UnsignedInt doImage3DCount() const override; + MAGNUM_KTXIMPORTER_LOCAL UnsignedInt doImage3DLevelCount(UnsignedInt id) override; + MAGNUM_KTXIMPORTER_LOCAL Containers::Optional doImage3D(UnsignedInt id, UnsignedInt level) override; + + private: + struct File; + Containers::Pointer _f; +}; + +}} + +#endif diff --git a/src/MagnumPlugins/KtxImporter/configure.h.cmake b/src/MagnumPlugins/KtxImporter/configure.h.cmake new file mode 100644 index 000000000..5a370ec09 --- /dev/null +++ b/src/MagnumPlugins/KtxImporter/configure.h.cmake @@ -0,0 +1,26 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#cmakedefine MAGNUM_KTXIMPORTER_BUILD_STATIC diff --git a/src/MagnumPlugins/KtxImporter/importStaticPlugin.cpp b/src/MagnumPlugins/KtxImporter/importStaticPlugin.cpp new file mode 100644 index 000000000..cbda6632f --- /dev/null +++ b/src/MagnumPlugins/KtxImporter/importStaticPlugin.cpp @@ -0,0 +1,35 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "MagnumPlugins/KtxImporter/configure.h" + +#ifdef MAGNUM_KTXIMPORTER_BUILD_STATIC +#include + +static int magnumKtxImporterStaticImporter() { + CORRADE_PLUGIN_IMPORT(KtxImporter) + return 1; +} CORRADE_AUTOMATIC_INITIALIZER(magnumKtxImporterStaticImporter) +#endif From 5b5cfd3e837be0f2581d11252eb37304cfa403b2 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Thu, 15 Jul 2021 22:40:20 +0200 Subject: [PATCH 02/95] KtxImporter: clean up header struct --- src/MagnumPlugins/KtxImporter/KtxHeader.h | 45 +++++++++---------- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 13 +++--- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxHeader.h b/src/MagnumPlugins/KtxImporter/KtxHeader.h index cdf128602..8da29d52a 100644 --- a/src/MagnumPlugins/KtxImporter/KtxHeader.h +++ b/src/MagnumPlugins/KtxImporter/KtxHeader.h @@ -25,7 +25,7 @@ DEALINGS IN THE SOFTWARE. */ -#include "Magnum/Types.h" +#include /* Used by both KtxImporter and KtxImageConverter, which is why it isn't directly inside KtxImporter.cpp. OTOH it doesn't need to be exposed @@ -33,27 +33,23 @@ namespace Magnum { namespace Trade { namespace Implementation { -#pragma pack(1) - /* KTX2 file header */ struct KtxHeader { - UnsignedByte identifier[12]; /* File identifier */ - UnsignedInt vkFormat; /* VkFormat enum value, VK_FORMAT_UNDEFINED = custom */ - UnsignedInt typeSize; /* Size of data type in bytes */ - UnsignedInt pixelWidth; /* Image level 0 width */ - UnsignedInt pixelHeight; /* Image level 0 height */ - UnsignedInt pixelDepth; /* Image level 0 depth */ - UnsignedInt layerCount; /* Number of array elements */ - UnsignedInt faceCount; /* Number of cubemap faces */ - UnsignedInt levelCount; /* Number of mip levels */ - UnsignedInt supercompressionScheme; + char identifier[12]; /* File identifier */ + UnsignedInt vkFormat; /* VkFormat enum value, VK_FORMAT_UNDEFINED = custom */ + UnsignedInt typeSize; /* Size of data type in bytes */ + Vector3ui pixelSize; /* Image level 0 size */ + UnsignedInt layerCount; /* Number of array elements */ + UnsignedInt faceCount; /* Number of cubemap faces */ + UnsignedInt levelCount; /* Number of mip levels */ + UnsignedInt supercompressionScheme; /* Index */ - UnsignedInt dfdByteOffset; /* Offset of Data Format Descriptor */ - UnsignedInt dfdByteLength; /* Length of Data Format Descriptor */ - UnsignedInt kvdByteOffset; /* Offset of Key/Value Data */ - UnsignedInt kvdByteLength; /* Length of Key/Value Data */ - UnsignedLong sgdByteOffset; /* Offset of Supercompression Global Data */ - UnsignedLong sgdByteLength; /* Length of Supercompression Global Data */ + UnsignedInt dfdByteOffset; /* Offset of Data Format Descriptor */ + UnsignedInt dfdByteLength; /* Length of Data Format Descriptor */ + UnsignedInt kvdByteOffset; /* Offset of Key/Value Data */ + UnsignedInt kvdByteLength; /* Length of Key/Value Data */ + UnsignedLong sgdByteOffset; /* Offset of Supercompression Global Data */ + UnsignedLong sgdByteLength; /* Length of Supercompression Global Data */ }; static_assert(sizeof(KtxHeader) == 80, "Improper size of KtxHeader struct"); @@ -67,15 +63,16 @@ struct KtxLevel { static_assert(sizeof(KtxLevel) == 24, "Improper size of KtxLevel struct"); -#pragma pack() - -constexpr UnsignedByte KtxFileIdentifier[12]{ - /* "«KTX 20»\r\n\x1A\n" */ - 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A +constexpr char KtxFileIdentifier[12]{ + /* https://github.khronos.org/KTX-Specification/#_identifier */ + '\xab', 'K', 'T', 'X', ' ', '2', '0', '\xbb', '\r', '\n', '\x1a', '\n' }; static_assert(sizeof(KtxFileIdentifier) == sizeof(KtxHeader::identifier), "Improper size of KtxFileIdentifier data"); +constexpr std::size_t KtxFileVersionOffset = 5; +static_assert(KtxFileVersionOffset < sizeof(KtxFileIdentifier), "KtxFileVersionOffset out of bounds"); + }}} #endif diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 1c034fd2a..76353fe50 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -307,7 +307,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { but we don't need them for now. */ Utility::Endianness::littleEndianInPlace( header.vkFormat, header.typeSize, - header.pixelWidth, header.pixelHeight, header.pixelDepth, + header.pixelSize[0], header.pixelSize[1], header.pixelSize[2], header.layerCount, header.faceCount, header.levelCount, header.supercompressionScheme); @@ -358,13 +358,12 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { return; } - const Vector3ui originalSize{header.pixelWidth, header.pixelHeight, header.pixelDepth}; - CORRADE_INTERNAL_ASSERT(originalSize.x() > 0); + CORRADE_INTERNAL_ASSERT(header.pixelSize.x() > 0); - if(originalSize.z() > 0) { - CORRADE_INTERNAL_ASSERT(originalSize.y() > 0); + if(header.pixelSize.z() > 0) { + CORRADE_INTERNAL_ASSERT(header.pixelSize.y() > 0); f->dimensions = 3; - } else if(originalSize.y() > 0) + } else if(header.pixelSize.y() > 0) f->dimensions = 2; else f->dimensions = 1; @@ -373,7 +372,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { /* Make size in each dimension at least 1 so we don't choke on size calculations using product(). */ - const Vector3i size = Math::max(Vector3i{originalSize}, Vector3i{1}); + const Vector3i size = Math::max(Vector3i{header.pixelSize}, Vector3i{1}); /* Number of array layers, imported as separate images */ const UnsignedInt numLayers = Math::max(header.layerCount, 1u); From 19fc1b5c95aab3db41cbbb6e5658a92c5af53d03 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Thu, 15 Jul 2021 22:48:19 +0200 Subject: [PATCH 03/95] KtxImporter: print useful error for unsupported KTX versions --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 76353fe50..27743731a 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -311,9 +312,16 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { header.layerCount, header.faceCount, header.levelCount, header.supercompressionScheme); - /* Check magic string to verify this is a KTX file */ - if(std::memcmp(&header.identifier, Implementation::KtxFileIdentifier, sizeof(header.identifier)) != 0) { - Error() << "Trade::KtxImporter::openData(): wrong file signature"; + /* Check magic string to verify this is a KTX2 file */ + const auto identifier = Containers::StringView{header.identifier, sizeof(header.identifier)}; + if(identifier != Implementation::KtxFileIdentifier) { + /* Print a useful error if this is a KTX file with an unsupported + version. KTX1 uses the same identifier but different version string. */ + if(std::memcmp(identifier.data(), Implementation::KtxFileIdentifier, Implementation::KtxFileVersionOffset) == 0) + Error() << "Trade::KtxImporter::openData(): unsupported KTX version, expected 20 but got" << + identifier.suffix(Implementation::KtxFileVersionOffset).prefix(2); + else + Error() << "Trade::KtxImporter::openData(): wrong file signature"; return; } From 8f77e64bb33f3255372b6ad2434b4cff4574fdb8 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Thu, 15 Jul 2021 22:58:40 +0200 Subject: [PATCH 04/95] KtxImporter: read key/value data --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 59 ++++++++++++++++++- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 27743731a..6b2e77ff2 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -26,6 +26,7 @@ #include "KtxImporter.h" #include +#include #include #include #include @@ -304,13 +305,15 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { Implementation::KtxHeader header = *reinterpret_cast(data.data()); std::size_t offset = sizeof(Implementation::KtxHeader); - /* Members after supercompressionScheme need endian swapping as well, + /* Members after kvdByteLength need endian swapping as well, but we don't need them for now. */ Utility::Endianness::littleEndianInPlace( header.vkFormat, header.typeSize, header.pixelSize[0], header.pixelSize[1], header.pixelSize[2], header.layerCount, header.faceCount, header.levelCount, - header.supercompressionScheme); + header.supercompressionScheme, + header.dfdByteOffset, header.dfdByteLength, + header.kvdByteOffset, header.kvdByteLength); /* Check magic string to verify this is a KTX2 file */ const auto identifier = Containers::StringView{header.identifier, sizeof(header.identifier)}; @@ -416,6 +419,58 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } } + CORRADE_INTERNAL_ASSERT(header.kvdByteLength > 0 || header.kvdByteOffset == 0); + CORRADE_INTERNAL_ASSERT(header.dfdByteLength > 0 || header.dfdByteOffset == 0); + + /** @todo Read Data Format Descriptor */ + if(header.dfdByteLength > 0) { + if(data.size() < header.dfdByteOffset + header.dfdByteLength) { + Error{} << "Trade::KtxImporter::openData(): data format descriptor too short, expected at least" << + header.dfdByteOffset + header.dfdByteLength << "bytes but got" << data.size(); + return; + } + const auto DataFormatDescriptorData = f->in.suffix(header.dfdByteOffset).prefix(header.dfdByteLength); + } + + /* Read Key/Value Data, optional */ + std::map> keyValueMap; + if(header.kvdByteLength > 0) { + if(data.size() < header.kvdByteOffset + header.kvdByteLength) { + Error{} << "Trade::KtxImporter::openData(): key/value data too short, expected at least" << + header.kvdByteOffset + header.kvdByteLength << "bytes but got" << data.size(); + return; + } + auto keyValueData = f->in.suffix(header.kvdByteOffset).prefix(header.kvdByteLength); + UnsignedInt current = 0; + while(current + sizeof(UnsignedInt) < keyValueData.size()) { + const UnsignedInt length = *reinterpret_cast(keyValueData.suffix(current).data()); + current += sizeof(length); + if(current + length < keyValueData.size()) { + const auto entry = keyValueData.suffix(current).prefix(length); + /* Key is zero-terminated, value is any remaining bytes. Value + alignment must be implicitly done through key length, hence + the funny KTX keys with multiple underscores. Any multi-byte + numbers in values must be endian-swapped later. */ + const std::size_t keyLength = entry.end() - std::find(entry.begin(), entry.end(), '\0'); + if(keyLength == 0 || keyLength >= entry.size()) { + Warning{} << "Trade::KtxImporter::openData(): invalid key/value entry, skipping"; + } else { + const Containers::StringView key{entry.data()}; + CORRADE_INTERNAL_ASSERT(key.size() == keyLength); + CORRADE_INTERNAL_ASSERT(keyValueMap.count(key) == 0); + keyValueMap[key] = entry.suffix(keyLength + 1); + } + } + /* Length value is dword-aligned, guaranteed for the first length + by the file layout. Padding is not included in the length. */ + current += (length + 3)/4*4; + } + CORRADE_INTERNAL_ASSERT(current == keyValueData.size()); + } + + /** @todo Determine if format needs swizzling */ + f->needsSwizzle = false; + f->imageData = Containers::Array>{numLayers}; for(UnsignedInt layer = 0; layer != numLayers; ++layer) { From c4f5b4d8ac372bb4616ca7e0c6f4d3d710d2f23e Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 16 Jul 2021 02:51:34 +0200 Subject: [PATCH 05/95] KtxImporter: cleanup --- src/MagnumPlugins/KtxImporter/KtxImporter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.h b/src/MagnumPlugins/KtxImporter/KtxImporter.h index 1aa717947..c02e324d7 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.h +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.h @@ -32,7 +32,7 @@ #include #include -#include "Magnum/Trade/AbstractImporter.h" +#include #include "MagnumPlugins/KtxImporter/configure.h" From bd7dbbaf7d0693721e5725b3fd99aac811b95408 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 16 Jul 2021 02:54:32 +0200 Subject: [PATCH 06/95] KtxImporter: replace asserts with errors and warnings --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 274 +++++++++++------- 1 file changed, 177 insertions(+), 97 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 6b2e77ff2..e3c048b87 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -94,6 +94,108 @@ void swizzlePixels(const PixelFormat format, Containers::Array& data, cons } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } +bool validateHeader(const Implementation::KtxHeader& header, std::size_t fileSize, const char* prefix) { + /* Check magic string to verify this is a KTX2 file */ + const auto identifier = Containers::StringView{header.identifier, sizeof(header.identifier)}; + if(identifier != Implementation::KtxFileIdentifier) { + /* Print a useful error if this is a KTX file with an unsupported + version. KTX1 uses the same identifier but different version string. */ + if(std::memcmp(identifier.data(), Implementation::KtxFileIdentifier, Implementation::KtxFileVersionOffset) == 0) + Error() << prefix << "unsupported KTX version, expected 20 but got" << + identifier.suffix(Implementation::KtxFileVersionOffset).prefix(2); + else + Error() << prefix << "wrong file signature"; + return false; + } + + /* typeSize is the size of the format's underlying type, not the texel + size, e.g. 2 for RG16F. For any sane format it should be a + power-of-two between 1 and 8. */ + if(header.typeSize < 1 || header.typeSize > 8 || + (header.typeSize & (header.typeSize - 1))) + { + Error{} << prefix << "unsupported type size" << header.typeSize; + return false; + } + + if(header.pixelSize.x() == 0) { + Error{} << prefix << "invalid image size, width is 0"; + return false; + } + if(header.pixelSize.y() == 0 && header.pixelSize.z() > 0) { + Error{} << prefix << "invalid image size, depth is" << header.pixelSize.z() << "but height is 0"; + return false; + } + + if(header.faceCount != 1) { + if(header.faceCount != 6) { + Error{} << prefix << "invalid cubemap face count, expected 1 or 6 but got" << header.faceCount; + return false; + } + if(header.pixelSize.z() > 0 ||header.pixelSize.x() != header.pixelSize.y()) { + Error{} << prefix << "invalid cubemap dimensions, must be 2D and square, but got" << header.pixelSize; + return false; + } + } + + const UnsignedInt maxLevelCount = Math::log2(header.pixelSize.max()) + 1; + if(header.levelCount > maxLevelCount) { + Error{} << prefix << "too many mipmap levels, expected at most" << + maxLevelCount << "but got" << header.levelCount; + return false; + } + + const std::size_t levelIndexEnd = sizeof(header) + Math::max(header.levelCount, 1u)*sizeof(Implementation::KtxLevel); + if(fileSize < levelIndexEnd) { + Error{} << prefix << "level index too short, expected at least" << + levelIndexEnd << "bytes but got" << fileSize; + return false; + } + + const std::size_t dfdEnd = header.dfdByteOffset + header.dfdByteLength; + if(fileSize < dfdEnd) { + Error{} << prefix << "data format descriptor out of bounds, expected at least" << + dfdEnd << "bytes but got" << fileSize; + return false; + } + + const std::size_t kvdEnd = header.kvdByteOffset + header.kvdByteLength; + if(fileSize < kvdEnd) { + Error{} << prefix << "key/value data out of bounds, expected at least" << + kvdEnd << "bytes but got" << fileSize; + return false; + } + + return true; +} + +bool validateLevel(const Implementation::KtxHeader& header, std::size_t fileSize, const Implementation::KtxLevel& level, std::size_t imageLength, const char* prefix) { + /* Both lengths should be equal without supercompression. Be lenient + here and only emit a warning in case some shitty exporter gets this + wrong. */ + if(header.supercompressionScheme == 0 && level.byteLength != level.uncompressedByteLength) { + Warning{} << prefix << "mismatching image data sizes, both compressed " + "and uncompressed should be equal but got" << + level.byteLength << "and" << level.uncompressedByteLength; + } + + const std::size_t levelEnd = level.byteOffset + level.byteLength; + if(fileSize < levelEnd) { + Error{} << prefix << "level data out of bounds, expected at least" << + levelEnd << "bytes but got" << fileSize; + return false; + } + + const std::size_t totalLength = imageLength*Math::max(header.layerCount, 1u)*header.faceCount; + if(level.byteLength < totalLength) { + Error{} << prefix << "level data too short, expected at least" << + totalLength << "bytes but got" << level.byteLength; + return false; + } + + return true; +} + /** @todo Swizzle channels if necessary. Vk::PixelFormat doesn't contain any of the swizzled formats like B8G8R8A8. */ /** @todo Support all Vulkan formats allowed by the KTX spec. Create custom @@ -296,7 +398,7 @@ bool KtxImporter::doIsOpened() const { return !!_f; } void KtxImporter::doClose() { _f = nullptr; } void KtxImporter::doOpenData(const Containers::ArrayView data) { - /* Check if the file is long enough */ + /* Check if the file is long enough for the header */ if(data.size() < sizeof(Implementation::KtxHeader)) { Error{} << "Trade::KtxImporter::openData(): file header too short, expected at least" << sizeof(Implementation::KtxHeader) << "bytes but got" << data.size(); return; @@ -305,28 +407,21 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { Implementation::KtxHeader header = *reinterpret_cast(data.data()); std::size_t offset = sizeof(Implementation::KtxHeader); - /* Members after kvdByteLength need endian swapping as well, - but we don't need them for now. */ + /* KTX2 uses little-endian everywhere */ Utility::Endianness::littleEndianInPlace( header.vkFormat, header.typeSize, header.pixelSize[0], header.pixelSize[1], header.pixelSize[2], header.layerCount, header.faceCount, header.levelCount, header.supercompressionScheme, header.dfdByteOffset, header.dfdByteLength, - header.kvdByteOffset, header.kvdByteLength); + header.kvdByteOffset, header.kvdByteLength, + header.sgdByteOffset, header.sgdByteLength); - /* Check magic string to verify this is a KTX2 file */ - const auto identifier = Containers::StringView{header.identifier, sizeof(header.identifier)}; - if(identifier != Implementation::KtxFileIdentifier) { - /* Print a useful error if this is a KTX file with an unsupported - version. KTX1 uses the same identifier but different version string. */ - if(std::memcmp(identifier.data(), Implementation::KtxFileIdentifier, Implementation::KtxFileVersionOffset) == 0) - Error() << "Trade::KtxImporter::openData(): unsupported KTX version, expected 20 but got" << - identifier.suffix(Implementation::KtxFileVersionOffset).prefix(2); - else - Error() << "Trade::KtxImporter::openData(): wrong file signature"; + /* Perform some sanity checks on header data and check if + there is enough space for the level index, data format + descriptor and key/value data */ + if(!validateHeader(header, data.size(), "Trade::KtxImporter::openData():")) return; - } Containers::Pointer f{new File}; f->in = Containers::Array{NoInit, data.size()}; @@ -347,20 +442,10 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { f->compressed = false; } - /** @todo Determine if format needs swizzling */ - f->needsSwizzle = false; - - /* typeSize is the size of the format's underlying type, not the texel - size, e.g. 2 for RG16F. For any sane format it should be a - power-of-two between 1 and 8. */ - if(header.typeSize < 1 || header.typeSize > 8 || - (header.typeSize & (header.typeSize - 1))) - { - Error{} << "Trade::KtxImporter::openData(): unsupported type size" << header.typeSize; + if(f->compressed && header.typeSize != 1) { + Error{} << "Trade::KtxImporter::openData(): invalid type size for compressed format, expected 1 but got" << header.typeSize; return; } - /* Block-compressed formats must have typeSize set to 1 */ - CORRADE_INTERNAL_ASSERT(!f->compressed || header.typeSize == 1); f->typeSize = header.typeSize; /** @todo Support supercompression */ @@ -369,15 +454,8 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { return; } - CORRADE_INTERNAL_ASSERT(header.pixelSize.x() > 0); - - if(header.pixelSize.z() > 0) { - CORRADE_INTERNAL_ASSERT(header.pixelSize.y() > 0); - f->dimensions = 3; - } else if(header.pixelSize.y() > 0) - f->dimensions = 2; - else - f->dimensions = 1; + f->dimensions = Math::min(header.pixelSize, Vector3ui{1}).sum(); + CORRADE_INTERNAL_ASSERT(f->dimensions >= 1 && f->dimensions <= 3); /** @todo Assert that 3D images can't have depth format */ @@ -391,81 +469,88 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { /* Cubemap faces, either 1 or 6. KTX allows incomplete cubemaps but those have to be array layers with extra metadata inside key/value data. */ const UnsignedInt numFaces = header.faceCount; - if(numFaces != 1) { - CORRADE_INTERNAL_ASSERT(numFaces == 6); - /* Cubemaps must be 2D and square */ - CORRADE_INTERNAL_ASSERT(f->dimensions == 2); - CORRADE_INTERNAL_ASSERT(size.x() == size.y()); - } + /* levelCount of 0 indicates to the user to generate mipmaps, not allowed + for block-compressed formats. We don't really care either way + and treat 0 as 1. */ const UnsignedInt numMipmaps = Math::max(header.levelCount, 1u); - /* KTX stores byte ranges for each mipmap, from largest to smallest. The - actual pixel data is usually arranged in reverse order for streaming - but we only need the ranges. */ - const std::size_t levelIndexSize = numMipmaps * sizeof(Implementation::KtxLevel); - if(f->in.suffix(offset).size() < levelIndexSize) { - Error{} << "Trade::KtxImporter::openData(): image header too short, expected at least" << offset + levelIndexSize << "bytes but got" << data.size(); - return; - } - const auto levelIndex = Containers::arrayCast(f->in.slice(offset, offset + levelIndexSize)); - for(Implementation::KtxLevel& level: levelIndex) { - Utility::Endianness::littleEndianInPlace(level.byteOffset, level.byteLength, level.uncompressedByteLength); - CORRADE_INTERNAL_ASSERT(header.supercompressionScheme != 0 || level.byteLength == level.uncompressedByteLength); - CORRADE_INTERNAL_ASSERT(level.uncompressedByteLength % (numFaces * numLayers) == 0); - if(data.size() < level.byteOffset + level.byteLength) { - Error{} << "Trade::KtxImporter::openData(): mip data too short, expected at least" << level.byteOffset + level.byteLength << "bytes but got" << data.size(); + /* KTX stores byte ranges for each mipmap in the level index, from largest + to smallest. The actual pixel data is usually arranged in reverse order + for streaming but we only need the ranges. */ + const std::size_t levelIndexSize = numMipmaps*sizeof(Implementation::KtxLevel); + const auto levelIndex = Containers::arrayCast( + f->in.suffix(offset).prefix(levelIndexSize)); + + /* We later loop over all levels multiple times, so prepare and validate + a few things up-front. */ + Containers::Array levelLengths{numMipmaps}; + Vector3i mipSize{size}; + for(UnsignedInt i = 0; i != levelIndex.size(); ++i) { + auto& level = levelIndex[i]; + Utility::Endianness::littleEndianInPlace(level.byteOffset, + level.byteLength, level.uncompressedByteLength); + + levelLengths[i] = f->compressed + ? imageLength(mipSize, f->pixelFormat.compressed) + : imageLength(mipSize, f->pixelFormat.uncompressed); + + if(!validateLevel(header, data.size(), level, levelLengths[i], "Trade::KtxImporter::openData():")) return; - } - } - CORRADE_INTERNAL_ASSERT(header.kvdByteLength > 0 || header.kvdByteOffset == 0); - CORRADE_INTERNAL_ASSERT(header.dfdByteLength > 0 || header.dfdByteOffset == 0); + /* Shrink to next power of 2 */ + mipSize = Math::max(mipSize >> 1, Vector3i{1}); + } - /** @todo Read Data Format Descriptor */ + /** @todo Read data format descriptor */ if(header.dfdByteLength > 0) { - if(data.size() < header.dfdByteOffset + header.dfdByteLength) { - Error{} << "Trade::KtxImporter::openData(): data format descriptor too short, expected at least" << - header.dfdByteOffset + header.dfdByteLength << "bytes but got" << data.size(); - return; - } const auto DataFormatDescriptorData = f->in.suffix(header.dfdByteOffset).prefix(header.dfdByteLength); } - /* Read Key/Value Data, optional */ + /* Read key/value data, optional */ std::map> keyValueMap; if(header.kvdByteLength > 0) { - if(data.size() < header.kvdByteOffset + header.kvdByteLength) { - Error{} << "Trade::KtxImporter::openData(): key/value data too short, expected at least" << - header.kvdByteOffset + header.kvdByteLength << "bytes but got" << data.size(); - return; - } auto keyValueData = f->in.suffix(header.kvdByteOffset).prefix(header.kvdByteLength); + /* Loop through entries, each one consisting of: + + UnsignedInt length + Byte data[length] + Byte padding[...] + + data begins with a zero-terminated key, the rest of the bytes is the + value content. Value alignment must be implicitly done through key + length, hence the funny KTX keys with multiple underscores. Any + multi-byte numbers in values must be endian-swapped later. */ UnsignedInt current = 0; while(current + sizeof(UnsignedInt) < keyValueData.size()) { + /* Length without padding */ const UnsignedInt length = *reinterpret_cast(keyValueData.suffix(current).data()); current += sizeof(length); + if(current + length < keyValueData.size()) { const auto entry = keyValueData.suffix(current).prefix(length); - /* Key is zero-terminated, value is any remaining bytes. Value - alignment must be implicitly done through key length, hence - the funny KTX keys with multiple underscores. Any multi-byte - numbers in values must be endian-swapped later. */ + + /* Split at zero terminating the key string */ const std::size_t keyLength = entry.end() - std::find(entry.begin(), entry.end(), '\0'); - if(keyLength == 0 || keyLength >= entry.size()) { + if(keyLength == 0 || keyLength >= entry.size()) Warning{} << "Trade::KtxImporter::openData(): invalid key/value entry, skipping"; - } else { + else { const Containers::StringView key{entry.data()}; CORRADE_INTERNAL_ASSERT(key.size() == keyLength); - CORRADE_INTERNAL_ASSERT(keyValueMap.count(key) == 0); - keyValueMap[key] = entry.suffix(keyLength + 1); + + if(keyValueMap.count(key) > 0) + keyValueMap[key] = entry.suffix(keyLength + 1); + else + Warning{} << "Trade::KtxImporter::openData(): key" << + key << "already set, skipping"; } } /* Length value is dword-aligned, guaranteed for the first length - by the file layout. Padding is not included in the length. */ + by the file layout */ current += (length + 3)/4*4; } - CORRADE_INTERNAL_ASSERT(current == keyValueData.size()); + /** @todo Remove after testing */ + CORRADE_INTERNAL_ASSERT(current <= keyValueData.size()); } /** @todo Determine if format needs swizzling */ @@ -474,28 +559,23 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { f->imageData = Containers::Array>{numLayers}; for(UnsignedInt layer = 0; layer != numLayers; ++layer) { - f->imageData[layer] = Containers::Array{numFaces * numMipmaps}; - /* This matches the image order of DdsImporter: faces, mipmaps */ + f->imageData[layer] = Containers::Array{numFaces*numMipmaps}; + /* This matches the level order of DdsImporter: faces, mipmaps */ std::size_t currentLevel = 0; for(UnsignedInt face = 0; face != numFaces; ++face) { - Vector3i mipSize{size}; + mipSize = size; /* Load all mipmaps for current face */ - for(const Implementation::KtxLevel& level: levelIndex) { - const std::size_t length = f->compressed - ? imageLength(mipSize, f->pixelFormat.compressed) - : imageLength(mipSize, f->pixelFormat.uncompressed); - + for(UnsignedInt i = 0; i != levelIndex.size(); ++i) { /* Each mipmap byte range contains tightly packed images, ordered as follows: layers, faces, (slices, rows, columns) */ - const std::size_t imageOffset = (layer * numFaces + face) * length; - if(level.byteLength < imageOffset + length) { - Error{} << "Trade::KtxImporter::openData(): image data too short, expected at least" << imageOffset + length << "bytes but got" << level.byteLength; - return; - } + const std::size_t length = levelLengths[i]; + const std::size_t imageOffset = (layer*numFaces + face)*length; - f->imageData[layer][currentLevel++] = {mipSize, f->in.suffix(level.byteOffset).slice(imageOffset, imageOffset + length)}; + /* validateLevel already checked if the level data is large + enough for all images */ + f->imageData[layer][currentLevel++] = {mipSize, f->in.suffix(levelIndex[i].byteOffset).suffix(imageOffset).prefix(length)}; /* Shrink to next power of 2 */ mipSize = Math::max(mipSize >> 1, Vector3i{1}); From 6590f2a69d765302f383b31acb01c69322c3371c Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Sat, 17 Jul 2021 04:39:17 +0200 Subject: [PATCH 07/95] KtxImporter: cleanup --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index e3c048b87..3e8148180 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -122,6 +122,7 @@ bool validateHeader(const Implementation::KtxHeader& header, std::size_t fileSiz Error{} << prefix << "invalid image size, width is 0"; return false; } + if(header.pixelSize.y() == 0 && header.pixelSize.z() > 0) { Error{} << prefix << "invalid image size, depth is" << header.pixelSize.z() << "but height is 0"; return false; @@ -132,7 +133,8 @@ bool validateHeader(const Implementation::KtxHeader& header, std::size_t fileSiz Error{} << prefix << "invalid cubemap face count, expected 1 or 6 but got" << header.faceCount; return false; } - if(header.pixelSize.z() > 0 ||header.pixelSize.x() != header.pixelSize.y()) { + + if(header.pixelSize.z() > 0 || header.pixelSize.x() != header.pixelSize.y()) { Error{} << prefix << "invalid cubemap dimensions, must be 2D and square, but got" << header.pixelSize; return false; } @@ -170,9 +172,11 @@ bool validateHeader(const Implementation::KtxHeader& header, std::size_t fileSiz } bool validateLevel(const Implementation::KtxHeader& header, std::size_t fileSize, const Implementation::KtxLevel& level, std::size_t imageLength, const char* prefix) { + CORRADE_INTERNAL_ASSERT(imageLength > 0); + /* Both lengths should be equal without supercompression. Be lenient - here and only emit a warning in case some shitty exporter gets this - wrong. */ + here and only emit a warning in case some shitty exporter gets this + wrong. */ if(header.supercompressionScheme == 0 && level.byteLength != level.uncompressedByteLength) { Warning{} << prefix << "mismatching image data sizes, both compressed " "and uncompressed should be equal but got" << @@ -450,7 +454,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { /** @todo Support supercompression */ if(header.supercompressionScheme != 0) { - Error{} << "Trade::KtxImporter::openData(): supercompression is not supported"; + Error{} << "Trade::KtxImporter::openData(): supercompression is currently not supported"; return; } @@ -472,18 +476,18 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { /* levelCount of 0 indicates to the user to generate mipmaps, not allowed for block-compressed formats. We don't really care either way - and treat 0 as 1. */ + and treat 0 and 1 the same. */ const UnsignedInt numMipmaps = Math::max(header.levelCount, 1u); /* KTX stores byte ranges for each mipmap in the level index, from largest - to smallest. The actual pixel data is usually arranged in reverse order + to smallest. The actual pixel data is usually arranged in reverse order for streaming but we only need the ranges. */ const std::size_t levelIndexSize = numMipmaps*sizeof(Implementation::KtxLevel); const auto levelIndex = Containers::arrayCast( f->in.suffix(offset).prefix(levelIndexSize)); /* We later loop over all levels multiple times, so prepare and validate - a few things up-front. */ + a few things up-front */ Containers::Array levelLengths{numMipmaps}; Vector3i mipSize{size}; for(UnsignedInt i = 0; i != levelIndex.size(); ++i) { @@ -530,8 +534,8 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { if(current + length < keyValueData.size()) { const auto entry = keyValueData.suffix(current).prefix(length); - /* Split at zero terminating the key string */ - const std::size_t keyLength = entry.end() - std::find(entry.begin(), entry.end(), '\0'); + /* Split at zero separating the key string and value */ + const std::size_t keyLength = std::find(entry.begin(), entry.end(), '\0') - entry.begin(); if(keyLength == 0 || keyLength >= entry.size()) Warning{} << "Trade::KtxImporter::openData(): invalid key/value entry, skipping"; else { @@ -549,8 +553,6 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { by the file layout */ current += (length + 3)/4*4; } - /** @todo Remove after testing */ - CORRADE_INTERNAL_ASSERT(current <= keyValueData.size()); } /** @todo Determine if format needs swizzling */ @@ -558,10 +560,13 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { f->imageData = Containers::Array>{numLayers}; + /** @todo Export cubemaps as 3D images instead of levels */ + for(UnsignedInt layer = 0; layer != numLayers; ++layer) { f->imageData[layer] = Containers::Array{numFaces*numMipmaps}; /* This matches the level order of DdsImporter: faces, mipmaps */ std::size_t currentLevel = 0; + /* +X, -X, +Y, -Y, +Z, -Z */ for(UnsignedInt face = 0; face != numFaces; ++face) { mipSize = size; From f2765d0173919c3a0c8c9b64b89c93373b961ada Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Sat, 17 Jul 2021 17:43:15 +0200 Subject: [PATCH 08/95] KtxImporter: add copyright notice --- src/MagnumPlugins/KtxImporter/CMakeLists.txt | 1 + src/MagnumPlugins/KtxImporter/KtxHeader.h | 1 + src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 1 + src/MagnumPlugins/KtxImporter/KtxImporter.h | 1 + src/MagnumPlugins/KtxImporter/configure.h.cmake | 1 + src/MagnumPlugins/KtxImporter/importStaticPlugin.cpp | 1 + 6 files changed, 6 insertions(+) diff --git a/src/MagnumPlugins/KtxImporter/CMakeLists.txt b/src/MagnumPlugins/KtxImporter/CMakeLists.txt index cc265a4b5..c4564de29 100644 --- a/src/MagnumPlugins/KtxImporter/CMakeLists.txt +++ b/src/MagnumPlugins/KtxImporter/CMakeLists.txt @@ -3,6 +3,7 @@ # # Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, # 2020, 2021 Vladimír Vondruš +# Copyright © 2021 Pablo Escobar # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), diff --git a/src/MagnumPlugins/KtxImporter/KtxHeader.h b/src/MagnumPlugins/KtxImporter/KtxHeader.h index 8da29d52a..cb63cea90 100644 --- a/src/MagnumPlugins/KtxImporter/KtxHeader.h +++ b/src/MagnumPlugins/KtxImporter/KtxHeader.h @@ -5,6 +5,7 @@ Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Vladimír Vondruš + Copyright © 2021 Pablo Escobar Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 3e8148180..9c94c09cf 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -3,6 +3,7 @@ Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Vladimír Vondruš + Copyright © 2021 Pablo Escobar Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.h b/src/MagnumPlugins/KtxImporter/KtxImporter.h index c02e324d7..72a1fb186 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.h +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.h @@ -5,6 +5,7 @@ Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Vladimír Vondruš + Copyright © 2021 Pablo Escobar Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/src/MagnumPlugins/KtxImporter/configure.h.cmake b/src/MagnumPlugins/KtxImporter/configure.h.cmake index 5a370ec09..537f9cca3 100644 --- a/src/MagnumPlugins/KtxImporter/configure.h.cmake +++ b/src/MagnumPlugins/KtxImporter/configure.h.cmake @@ -3,6 +3,7 @@ Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Vladimír Vondruš + Copyright © 2021 Pablo Escobar Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/src/MagnumPlugins/KtxImporter/importStaticPlugin.cpp b/src/MagnumPlugins/KtxImporter/importStaticPlugin.cpp index cbda6632f..0e6a31365 100644 --- a/src/MagnumPlugins/KtxImporter/importStaticPlugin.cpp +++ b/src/MagnumPlugins/KtxImporter/importStaticPlugin.cpp @@ -3,6 +3,7 @@ Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Vladimír Vondruš + Copyright © 2021 Pablo Escobar Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), From 9662fff3265f3abdf4fbbc51f35b203ee533f092 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Sat, 17 Jul 2021 17:50:44 +0200 Subject: [PATCH 09/95] KtxImporter: decode Vulkan format with Magnum::Vk --- src/MagnumPlugins/KtxImporter/CMakeLists.txt | 13 +- src/MagnumPlugins/KtxImporter/KtxHeader.h | 255 ++++++++++++- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 338 +++++++----------- .../KtxImporter/formatMapping.hpp | 163 +++++++++ .../KtxImporter/formatMapping.py | 114 ++++++ 5 files changed, 670 insertions(+), 213 deletions(-) create mode 100644 src/MagnumPlugins/KtxImporter/formatMapping.hpp create mode 100644 src/MagnumPlugins/KtxImporter/formatMapping.py diff --git a/src/MagnumPlugins/KtxImporter/CMakeLists.txt b/src/MagnumPlugins/KtxImporter/CMakeLists.txt index c4564de29..69c382e9d 100644 --- a/src/MagnumPlugins/KtxImporter/CMakeLists.txt +++ b/src/MagnumPlugins/KtxImporter/CMakeLists.txt @@ -24,7 +24,7 @@ # DEALINGS IN THE SOFTWARE. # -find_package(Magnum REQUIRED Trade Vk) +find_package(Magnum REQUIRED Trade) if(BUILD_PLUGINS_STATIC AND NOT DEFINED MAGNUM_KTXIMPORTER_BUILD_STATIC) set(MAGNUM_KTXIMPORTER_BUILD_STATIC 1) @@ -40,16 +40,15 @@ add_plugin(KtxImporter KtxImporter.conf KtxImporter.cpp KtxImporter.h - KtxHeader.h) + KtxHeader.h + formatMapping.hpp) if(MAGNUM_KTXIMPORTER_BUILD_STATIC AND BUILD_STATIC_PIC) set_target_properties(KtxImporter PROPERTIES POSITION_INDEPENDENT_CODE ON) endif() target_include_directories(KtxImporter PUBLIC ${PROJECT_SOURCE_DIR}/src ${PROJECT_BINARY_DIR}/src) -target_link_libraries(KtxImporter PUBLIC - Magnum::Trade - Magnum::Vk) +target_link_libraries(KtxImporter PUBLIC Magnum::Trade) # Modify output location only if all are set, otherwise it makes no sense if(CMAKE_RUNTIME_OUTPUT_DIRECTORY AND CMAKE_LIBRARY_OUTPUT_DIRECTORY AND CMAKE_ARCHIVE_OUTPUT_DIRECTORY) set_target_properties(KtxImporter PROPERTIES @@ -67,5 +66,9 @@ if(MAGNUM_KTXIMPORTER_BUILD_STATIC) target_sources(KtxImporter INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/importStaticPlugin.cpp) endif() +if(BUILD_TESTS) + add_subdirectory(Test) +endif() + # MagnumPlugins KtxImporter target alias for superprojects add_library(MagnumPlugins::KtxImporter ALIAS KtxImporter) diff --git a/src/MagnumPlugins/KtxImporter/KtxHeader.h b/src/MagnumPlugins/KtxImporter/KtxHeader.h index cb63cea90..1c3a7c15f 100644 --- a/src/MagnumPlugins/KtxImporter/KtxHeader.h +++ b/src/MagnumPlugins/KtxImporter/KtxHeader.h @@ -34,16 +34,265 @@ namespace Magnum { namespace Trade { namespace Implementation { +/* Taken from flextVk.h, with KHR aliases removed. + Contains all formats from: + - 1.2 core + - EXT_texture_compression_astc_hdr + - IMG_format_pvrtc */ +enum VkFormat : UnsignedInt { + VK_FORMAT_UNDEFINED = 0, + VK_FORMAT_R4G4_UNORM_PACK8 = 1, + VK_FORMAT_R4G4B4A4_UNORM_PACK16 = 2, + VK_FORMAT_B4G4R4A4_UNORM_PACK16 = 3, + VK_FORMAT_R5G6B5_UNORM_PACK16 = 4, + VK_FORMAT_B5G6R5_UNORM_PACK16 = 5, + VK_FORMAT_R5G5B5A1_UNORM_PACK16 = 6, + VK_FORMAT_B5G5R5A1_UNORM_PACK16 = 7, + VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8, + VK_FORMAT_R8_UNORM = 9, + VK_FORMAT_R8_SNORM = 10, + VK_FORMAT_R8_USCALED = 11, + VK_FORMAT_R8_SSCALED = 12, + VK_FORMAT_R8_UINT = 13, + VK_FORMAT_R8_SINT = 14, + VK_FORMAT_R8_SRGB = 15, + VK_FORMAT_R8G8_UNORM = 16, + VK_FORMAT_R8G8_SNORM = 17, + VK_FORMAT_R8G8_USCALED = 18, + VK_FORMAT_R8G8_SSCALED = 19, + VK_FORMAT_R8G8_UINT = 20, + VK_FORMAT_R8G8_SINT = 21, + VK_FORMAT_R8G8_SRGB = 22, + VK_FORMAT_R8G8B8_UNORM = 23, + VK_FORMAT_R8G8B8_SNORM = 24, + VK_FORMAT_R8G8B8_USCALED = 25, + VK_FORMAT_R8G8B8_SSCALED = 26, + VK_FORMAT_R8G8B8_UINT = 27, + VK_FORMAT_R8G8B8_SINT = 28, + VK_FORMAT_R8G8B8_SRGB = 29, + VK_FORMAT_B8G8R8_UNORM = 30, + VK_FORMAT_B8G8R8_SNORM = 31, + VK_FORMAT_B8G8R8_USCALED = 32, + VK_FORMAT_B8G8R8_SSCALED = 33, + VK_FORMAT_B8G8R8_UINT = 34, + VK_FORMAT_B8G8R8_SINT = 35, + VK_FORMAT_B8G8R8_SRGB = 36, + VK_FORMAT_R8G8B8A8_UNORM = 37, + VK_FORMAT_R8G8B8A8_SNORM = 38, + VK_FORMAT_R8G8B8A8_USCALED = 39, + VK_FORMAT_R8G8B8A8_SSCALED = 40, + VK_FORMAT_R8G8B8A8_UINT = 41, + VK_FORMAT_R8G8B8A8_SINT = 42, + VK_FORMAT_R8G8B8A8_SRGB = 43, + VK_FORMAT_B8G8R8A8_UNORM = 44, + VK_FORMAT_B8G8R8A8_SNORM = 45, + VK_FORMAT_B8G8R8A8_USCALED = 46, + VK_FORMAT_B8G8R8A8_SSCALED = 47, + VK_FORMAT_B8G8R8A8_UINT = 48, + VK_FORMAT_B8G8R8A8_SINT = 49, + VK_FORMAT_B8G8R8A8_SRGB = 50, + VK_FORMAT_A8B8G8R8_UNORM_PACK32 = 51, + VK_FORMAT_A8B8G8R8_SNORM_PACK32 = 52, + VK_FORMAT_A8B8G8R8_USCALED_PACK32 = 53, + VK_FORMAT_A8B8G8R8_SSCALED_PACK32 = 54, + VK_FORMAT_A8B8G8R8_UINT_PACK32 = 55, + VK_FORMAT_A8B8G8R8_SINT_PACK32 = 56, + VK_FORMAT_A8B8G8R8_SRGB_PACK32 = 57, + VK_FORMAT_A2R10G10B10_UNORM_PACK32 = 58, + VK_FORMAT_A2R10G10B10_SNORM_PACK32 = 59, + VK_FORMAT_A2R10G10B10_USCALED_PACK32 = 60, + VK_FORMAT_A2R10G10B10_SSCALED_PACK32 = 61, + VK_FORMAT_A2R10G10B10_UINT_PACK32 = 62, + VK_FORMAT_A2R10G10B10_SINT_PACK32 = 63, + VK_FORMAT_A2B10G10R10_UNORM_PACK32 = 64, + VK_FORMAT_A2B10G10R10_SNORM_PACK32 = 65, + VK_FORMAT_A2B10G10R10_USCALED_PACK32 = 66, + VK_FORMAT_A2B10G10R10_SSCALED_PACK32 = 67, + VK_FORMAT_A2B10G10R10_UINT_PACK32 = 68, + VK_FORMAT_A2B10G10R10_SINT_PACK32 = 69, + VK_FORMAT_R16_UNORM = 70, + VK_FORMAT_R16_SNORM = 71, + VK_FORMAT_R16_USCALED = 72, + VK_FORMAT_R16_SSCALED = 73, + VK_FORMAT_R16_UINT = 74, + VK_FORMAT_R16_SINT = 75, + VK_FORMAT_R16_SFLOAT = 76, + VK_FORMAT_R16G16_UNORM = 77, + VK_FORMAT_R16G16_SNORM = 78, + VK_FORMAT_R16G16_USCALED = 79, + VK_FORMAT_R16G16_SSCALED = 80, + VK_FORMAT_R16G16_UINT = 81, + VK_FORMAT_R16G16_SINT = 82, + VK_FORMAT_R16G16_SFLOAT = 83, + VK_FORMAT_R16G16B16_UNORM = 84, + VK_FORMAT_R16G16B16_SNORM = 85, + VK_FORMAT_R16G16B16_USCALED = 86, + VK_FORMAT_R16G16B16_SSCALED = 87, + VK_FORMAT_R16G16B16_UINT = 88, + VK_FORMAT_R16G16B16_SINT = 89, + VK_FORMAT_R16G16B16_SFLOAT = 90, + VK_FORMAT_R16G16B16A16_UNORM = 91, + VK_FORMAT_R16G16B16A16_SNORM = 92, + VK_FORMAT_R16G16B16A16_USCALED = 93, + VK_FORMAT_R16G16B16A16_SSCALED = 94, + VK_FORMAT_R16G16B16A16_UINT = 95, + VK_FORMAT_R16G16B16A16_SINT = 96, + VK_FORMAT_R16G16B16A16_SFLOAT = 97, + VK_FORMAT_R32_UINT = 98, + VK_FORMAT_R32_SINT = 99, + VK_FORMAT_R32_SFLOAT = 100, + VK_FORMAT_R32G32_UINT = 101, + VK_FORMAT_R32G32_SINT = 102, + VK_FORMAT_R32G32_SFLOAT = 103, + VK_FORMAT_R32G32B32_UINT = 104, + VK_FORMAT_R32G32B32_SINT = 105, + VK_FORMAT_R32G32B32_SFLOAT = 106, + VK_FORMAT_R32G32B32A32_UINT = 107, + VK_FORMAT_R32G32B32A32_SINT = 108, + VK_FORMAT_R32G32B32A32_SFLOAT = 109, + VK_FORMAT_R64_UINT = 110, + VK_FORMAT_R64_SINT = 111, + VK_FORMAT_R64_SFLOAT = 112, + VK_FORMAT_R64G64_UINT = 113, + VK_FORMAT_R64G64_SINT = 114, + VK_FORMAT_R64G64_SFLOAT = 115, + VK_FORMAT_R64G64B64_UINT = 116, + VK_FORMAT_R64G64B64_SINT = 117, + VK_FORMAT_R64G64B64_SFLOAT = 118, + VK_FORMAT_R64G64B64A64_UINT = 119, + VK_FORMAT_R64G64B64A64_SINT = 120, + VK_FORMAT_R64G64B64A64_SFLOAT = 121, + VK_FORMAT_B10G11R11_UFLOAT_PACK32 = 122, + VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123, + VK_FORMAT_D16_UNORM = 124, + VK_FORMAT_X8_D24_UNORM_PACK32 = 125, + VK_FORMAT_D32_SFLOAT = 126, + VK_FORMAT_S8_UINT = 127, + VK_FORMAT_D16_UNORM_S8_UINT = 128, + VK_FORMAT_D24_UNORM_S8_UINT = 129, + VK_FORMAT_D32_SFLOAT_S8_UINT = 130, + VK_FORMAT_BC1_RGB_UNORM_BLOCK = 131, + VK_FORMAT_BC1_RGB_SRGB_BLOCK = 132, + VK_FORMAT_BC1_RGBA_UNORM_BLOCK = 133, + VK_FORMAT_BC1_RGBA_SRGB_BLOCK = 134, + VK_FORMAT_BC2_UNORM_BLOCK = 135, + VK_FORMAT_BC2_SRGB_BLOCK = 136, + VK_FORMAT_BC3_UNORM_BLOCK = 137, + VK_FORMAT_BC3_SRGB_BLOCK = 138, + VK_FORMAT_BC4_UNORM_BLOCK = 139, + VK_FORMAT_BC4_SNORM_BLOCK = 140, + VK_FORMAT_BC5_UNORM_BLOCK = 141, + VK_FORMAT_BC5_SNORM_BLOCK = 142, + VK_FORMAT_BC6H_UFLOAT_BLOCK = 143, + VK_FORMAT_BC6H_SFLOAT_BLOCK = 144, + VK_FORMAT_BC7_UNORM_BLOCK = 145, + VK_FORMAT_BC7_SRGB_BLOCK = 146, + VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK = 147, + VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK = 148, + VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK = 149, + VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK = 150, + VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK = 151, + VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK = 152, + VK_FORMAT_EAC_R11_UNORM_BLOCK = 153, + VK_FORMAT_EAC_R11_SNORM_BLOCK = 154, + VK_FORMAT_EAC_R11G11_UNORM_BLOCK = 155, + VK_FORMAT_EAC_R11G11_SNORM_BLOCK = 156, + VK_FORMAT_ASTC_4x4_UNORM_BLOCK = 157, + VK_FORMAT_ASTC_4x4_SRGB_BLOCK = 158, + VK_FORMAT_ASTC_5x4_UNORM_BLOCK = 159, + VK_FORMAT_ASTC_5x4_SRGB_BLOCK = 160, + VK_FORMAT_ASTC_5x5_UNORM_BLOCK = 161, + VK_FORMAT_ASTC_5x5_SRGB_BLOCK = 162, + VK_FORMAT_ASTC_6x5_UNORM_BLOCK = 163, + VK_FORMAT_ASTC_6x5_SRGB_BLOCK = 164, + VK_FORMAT_ASTC_6x6_UNORM_BLOCK = 165, + VK_FORMAT_ASTC_6x6_SRGB_BLOCK = 166, + VK_FORMAT_ASTC_8x5_UNORM_BLOCK = 167, + VK_FORMAT_ASTC_8x5_SRGB_BLOCK = 168, + VK_FORMAT_ASTC_8x6_UNORM_BLOCK = 169, + VK_FORMAT_ASTC_8x6_SRGB_BLOCK = 170, + VK_FORMAT_ASTC_8x8_UNORM_BLOCK = 171, + VK_FORMAT_ASTC_8x8_SRGB_BLOCK = 172, + VK_FORMAT_ASTC_10x5_UNORM_BLOCK = 173, + VK_FORMAT_ASTC_10x5_SRGB_BLOCK = 174, + VK_FORMAT_ASTC_10x6_UNORM_BLOCK = 175, + VK_FORMAT_ASTC_10x6_SRGB_BLOCK = 176, + VK_FORMAT_ASTC_10x8_UNORM_BLOCK = 177, + VK_FORMAT_ASTC_10x8_SRGB_BLOCK = 178, + VK_FORMAT_ASTC_10x10_UNORM_BLOCK = 179, + VK_FORMAT_ASTC_10x10_SRGB_BLOCK = 180, + VK_FORMAT_ASTC_12x10_UNORM_BLOCK = 181, + VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182, + VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183, + VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184, + VK_FORMAT_G8B8G8R8_422_UNORM = 1000156000, + VK_FORMAT_B8G8R8G8_422_UNORM = 1000156001, + VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM = 1000156002, + VK_FORMAT_G8_B8R8_2PLANE_420_UNORM = 1000156003, + VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM = 1000156004, + VK_FORMAT_G8_B8R8_2PLANE_422_UNORM = 1000156005, + VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM = 1000156006, + VK_FORMAT_R10X6_UNORM_PACK16 = 1000156007, + VK_FORMAT_R10X6G10X6_UNORM_2PACK16 = 1000156008, + VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16 = 1000156009, + VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16 = 1000156010, + VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16 = 1000156011, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16 = 1000156012, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16 = 1000156013, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16 = 1000156014, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16 = 1000156015, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16 = 1000156016, + VK_FORMAT_R12X4_UNORM_PACK16 = 1000156017, + VK_FORMAT_R12X4G12X4_UNORM_2PACK16 = 1000156018, + VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16 = 1000156019, + VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16 = 1000156020, + VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16 = 1000156021, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16 = 1000156022, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16 = 1000156023, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16 = 1000156024, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16 = 1000156025, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16 = 1000156026, + VK_FORMAT_G16B16G16R16_422_UNORM = 1000156027, + VK_FORMAT_B16G16R16G16_422_UNORM = 1000156028, + VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM = 1000156029, + VK_FORMAT_G16_B16R16_2PLANE_420_UNORM = 1000156030, + VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM = 1000156031, + VK_FORMAT_G16_B16R16_2PLANE_422_UNORM = 1000156032, + VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM = 1000156033, + VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT = 1000066000, + VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT = 1000066001, + VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT = 1000066002, + VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT = 1000066003, + VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT = 1000066004, + VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT = 1000066005, + VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT = 1000066006, + VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT = 1000066007, + VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT = 1000066008, + VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT = 1000066009, + VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT = 1000066010, + VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT = 1000066011, + VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT = 1000066012, + VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT = 1000066013, + VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000, + VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001, + VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002, + VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003, + VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004, + VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005, + VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006, + VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007 +}; + /* KTX2 file header */ struct KtxHeader { char identifier[12]; /* File identifier */ - UnsignedInt vkFormat; /* VkFormat enum value, VK_FORMAT_UNDEFINED = custom */ - UnsignedInt typeSize; /* Size of data type in bytes */ + VkFormat vkFormat; /* Texel format, VK_FORMAT_UNDEFINED = custom */ + UnsignedInt typeSize; /* Size of channel data type, in bytes */ Vector3ui pixelSize; /* Image level 0 size */ UnsignedInt layerCount; /* Number of array elements */ UnsignedInt faceCount; /* Number of cubemap faces */ UnsignedInt levelCount; /* Number of mip levels */ - UnsignedInt supercompressionScheme; + UnsignedInt supercompressionScheme; /* Index */ UnsignedInt dfdByteOffset; /* Offset of Data Format Descriptor */ UnsignedInt dfdByteLength; /* Length of Data Format Descriptor */ diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 9c94c09cf..0522f9c70 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -93,6 +93,11 @@ void swizzlePixels(const PixelFormat format, Containers::Array& data, cons [](Math::Vector4 pixel) { return Math::gather<'b', 'g', 'r', 'a'>(pixel); }); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ +enum SwizzleType : UnsignedByte { + None = 0, + BGR, + BGRA +}; } bool validateHeader(const Implementation::KtxHeader& header, std::size_t fileSize, const char* prefix) { @@ -201,171 +206,6 @@ bool validateLevel(const Implementation::KtxHeader& header, std::size_t fileSize return true; } -/** @todo Swizzle channels if necessary. Vk::PixelFormat doesn't contain any - of the swizzled formats like B8G8R8A8. */ -/** @todo Support all Vulkan formats allowed by the KTX spec. Create custom - PixelFormat with pixelFormatWrap and manually fill PixelStorage/ - CompressedPixelStorage. We can take all the necessary info from - https://github.com/KhronosGroup/KTX-Specification/blob/master/formats.json - Do we also need this for the KtxImageConverter? This would allow - users to pass in images with implementation-specific PixelFormat - using the Vulkan format enum directly. */ -PixelFormat pixelFormat(Vk::PixelFormat format) { - #define _c(val) case Vk::PixelFormat:: ## val: return PixelFormat:: ## val; - switch(format) { - _c(R8Unorm) - _c(RG8Unorm) - _c(RGB8Unorm) - _c(RGBA8Unorm) - _c(R8Snorm) - _c(RG8Snorm) - _c(RGB8Snorm) - _c(RGBA8Snorm) - _c(R8Srgb) - _c(RG8Srgb) - _c(RGB8Srgb) - _c(RGBA8Srgb) - _c(R8UI) - _c(RG8UI) - _c(RGB8UI) - _c(RGBA8UI) - _c(R8I) - _c(RG8I) - _c(RGB8I) - _c(RGBA8I) - _c(R16Unorm) - _c(RG16Unorm) - _c(RGB16Unorm) - _c(RGBA16Unorm) - _c(R16Snorm) - _c(RG16Snorm) - _c(RGB16Snorm) - _c(RGBA16Snorm) - _c(R16UI) - _c(RG16UI) - _c(RGB16UI) - _c(RGBA16UI) - _c(R16I) - _c(RG16I) - _c(RGB16I) - _c(RGBA16I) - _c(R32UI) - _c(RG32UI) - _c(RGB32UI) - _c(RGBA32UI) - _c(R32I) - _c(RG32I) - _c(RGB32I) - _c(RGBA32I) - _c(R16F) - _c(RG16F) - _c(RGB16F) - _c(RGBA16F) - _c(R32F) - _c(RG32F) - _c(RGB32F) - _c(RGBA32F) - _c(Depth16Unorm) - _c(Depth24Unorm) - _c(Depth32F) - _c(Stencil8UI) - _c(Depth16UnormStencil8UI) - _c(Depth24UnormStencil8UI) - _c(Depth32FStencil8UI) - default: - return {}; - } - #undef _c -} - -CompressedPixelFormat compressedPixelFormat(Vk::PixelFormat format) { - #define _c(val) case Vk::PixelFormat::Compressed ## val: return CompressedPixelFormat:: ## val; - switch(format) { - _c(Bc1RGBUnorm) - _c(Bc1RGBSrgb) - _c(Bc1RGBAUnorm) - _c(Bc1RGBASrgb) - _c(Bc2RGBAUnorm) - _c(Bc2RGBASrgb) - _c(Bc3RGBAUnorm) - _c(Bc3RGBASrgb) - _c(Bc4RUnorm) - _c(Bc4RSnorm) - _c(Bc5RGUnorm) - _c(Bc5RGSnorm) - _c(Bc6hRGBUfloat) - _c(Bc6hRGBSfloat) - _c(Bc7RGBAUnorm) - _c(Bc7RGBASrgb) - _c(EacR11Unorm) - _c(EacR11Snorm) - _c(EacRG11Unorm) - _c(EacRG11Snorm) - _c(Etc2RGB8Unorm) - _c(Etc2RGB8Srgb) - _c(Etc2RGB8A1Unorm) - _c(Etc2RGB8A1Srgb) - _c(Etc2RGBA8Unorm) - _c(Etc2RGBA8Srgb) - _c(Astc4x4RGBAUnorm) - _c(Astc4x4RGBASrgb) - _c(Astc4x4RGBAF) - _c(Astc5x4RGBAUnorm) - _c(Astc5x4RGBASrgb) - _c(Astc5x4RGBAF) - _c(Astc5x5RGBAUnorm) - _c(Astc5x5RGBASrgb) - _c(Astc5x5RGBAF) - _c(Astc6x5RGBAUnorm) - _c(Astc6x5RGBASrgb) - _c(Astc6x5RGBAF) - _c(Astc6x6RGBAUnorm) - _c(Astc6x6RGBASrgb) - _c(Astc6x6RGBAF) - _c(Astc8x5RGBAUnorm) - _c(Astc8x5RGBASrgb) - _c(Astc8x5RGBAF) - _c(Astc8x6RGBAUnorm) - _c(Astc8x6RGBASrgb) - _c(Astc8x6RGBAF) - _c(Astc8x8RGBAUnorm) - _c(Astc8x8RGBASrgb) - _c(Astc8x8RGBAF) - _c(Astc10x5RGBAUnorm) - _c(Astc10x5RGBASrgb) - _c(Astc10x5RGBAF) - _c(Astc10x6RGBAUnorm) - _c(Astc10x6RGBASrgb) - _c(Astc10x6RGBAF) - _c(Astc10x8RGBAUnorm) - _c(Astc10x8RGBASrgb) - _c(Astc10x8RGBAF) - _c(Astc10x10RGBAUnorm) - _c(Astc10x10RGBASrgb) - _c(Astc10x10RGBAF) - _c(Astc12x10RGBAUnorm) - _c(Astc12x10RGBASrgb) - _c(Astc12x10RGBAF) - _c(Astc12x12RGBAUnorm) - _c(Astc12x12RGBASrgb) - _c(Astc12x12RGBAF) - _c(PvrtcRGBA2bppUnorm) - _c(PvrtcRGBA2bppSrgb) - _c(PvrtcRGBA4bppUnorm) - _c(PvrtcRGBA4bppSrgb) - /* CompressedPixelFormat has no Pvrtc2 */ - /* - _c(Pvrtc2RGBA2bppUnorm) - _c(Pvrtc2RGBA2bppSrgb) - _c(Pvrtc2RGBA4bppUnorm) - _c(Pvrtc2RGBA4bppSrgb) - */ - default: - return {}; - } - #undef _c -} - } struct KtxImporter::File { @@ -377,19 +217,115 @@ struct KtxImporter::File { Containers::Array in; UnsignedByte dimensions; - bool needsSwizzle; - union { - PixelFormat uncompressed; - CompressedPixelFormat compressed; + struct Format { + bool decode(Implementation::VkFormat vkFormat); + + union { + PixelFormat uncompressed; + CompressedPixelFormat compressed; + }; + + bool isCompressed; + bool isDepth; + UnsignedInt typeSize; + SwizzleType swizzle; } pixelFormat; - bool compressed; - UnsignedInt typeSize; /* Each array layer is an image with faces and mipmaps as levels */ Containers::Array> imageData; }; +bool KtxImporter::File::Format::decode(Implementation::VkFormat vkFormat) { + isCompressed = false; + isDepth = false; + swizzle = SwizzleType::None; + + /* Find uncompressed pixel format */ + PixelFormat format{}; + switch(vkFormat) { + #define _p(vulkan, magnum) case Implementation::VK_FORMAT_ ## vulkan: format = PixelFormat:: ## magnum; break; + #include "MagnumPlugins/KtxImporter/formatMapping.hpp" + #undef _p + default: + break; + } + + /* PixelFormat doesn't contain any of the swizzled formats. Figure it out + from the Vulkan format and remember that we need to swizzle in doImage. */ + if(format == PixelFormat{}) { + switch(vkFormat) { + case Implementation::VK_FORMAT_B8G8R8_UNORM: format = PixelFormat::RGB8Unorm; break; + case Implementation::VK_FORMAT_B8G8R8_SNORM: format = PixelFormat::RGB8Snorm; break; + case Implementation::VK_FORMAT_B8G8R8_UINT: format = PixelFormat::RGB8UI; break; + case Implementation::VK_FORMAT_B8G8R8_SINT: format = PixelFormat::RGB8I; break; + case Implementation::VK_FORMAT_B8G8R8_SRGB: format = PixelFormat::RGB8Srgb; break; + case Implementation::VK_FORMAT_B8G8R8A8_UNORM: format = PixelFormat::RGBA8Unorm; break; + case Implementation::VK_FORMAT_B8G8R8A8_SNORM: format = PixelFormat::RGBA8Snorm; break; + case Implementation::VK_FORMAT_B8G8R8A8_UINT: format = PixelFormat::RGBA8UI; break; + case Implementation::VK_FORMAT_B8G8R8A8_SINT: format = PixelFormat::RGBA8I; break; + case Implementation::VK_FORMAT_B8G8R8A8_SRGB: format = PixelFormat::RGBA8Srgb; break; + default: + break; + } + + if(format != PixelFormat{}) { + const UnsignedInt size = pixelSize(format); + CORRADE_INTERNAL_ASSERT(size == 3 || size == 4); + swizzle = size == 3 ? SwizzleType::BGR : SwizzleType::BGRA; + } + } + + if(format != PixelFormat{}) { + /* Depth formats are allowed by KTX. We only really use isDepth for + validation. */ + switch(format) { + case PixelFormat::Depth16Unorm: + case PixelFormat::Depth24Unorm: + case PixelFormat::Depth32F: + case PixelFormat::Stencil8UI: + case PixelFormat::Depth16UnormStencil8UI: + case PixelFormat::Depth24UnormStencil8UI: + case PixelFormat::Depth32FStencil8UI: + isDepth = true; + default: + /* PixelFormat covers all of Vulkan's depth formats */ + break; + } + + uncompressed = format; + return true; + } + + /* Find block-compressed pixel format. No swizzling necessary (or possible!). */ + CompressedPixelFormat compressedFormat{}; + switch(vkFormat) { + #define _c(vulkan, magnum) case Implementation::VK_FORMAT_ ## vulkan: compressedFormat = CompressedPixelFormat:: ## magnum; break; + #include "MagnumPlugins/KtxImporter/formatMapping.hpp" + #undef _c + default: + break; + } + + if(compressedFormat != CompressedPixelFormat{}) { + compressed = compressedFormat; + isCompressed = true; + return true; + } + + /** @todo Support all Vulkan formats allowed by the KTX spec. Create custom + PixelFormat with pixelFormatWrap and manually fill PixelStorage/ + CompressedPixelStorage. We can take all the necessary info from + https://github.com/KhronosGroup/KTX-Specification/blob/master/formats.json + Do we also need this for the KtxImageConverter? This would allow + users to pass in images with implementation-specific PixelFormat + using the Vulkan format enum directly. + Is this actually worth the effort? Which Vulkan formats are not + supported by PixelFormat? */ + + return false; +} + KtxImporter::KtxImporter() = default; KtxImporter::KtxImporter(PluginManager::AbstractManager& manager, const std::string& plugin): AbstractImporter{manager, plugin} {} @@ -433,25 +369,25 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { Utility::copy(data, f->in); /** @todo Support Basis compression */ - const PixelFormat format = pixelFormat(Vk::PixelFormat(header.vkFormat)); - if(format == PixelFormat{}) { - const CompressedPixelFormat compressedFormat = compressedPixelFormat(Vk::PixelFormat(header.vkFormat)); - if(compressedFormat == CompressedPixelFormat{}) { - Error{} << "Trade::KtxImporter::openData(): unsupported format" << header.vkFormat; - return; - } - f->pixelFormat.compressed = compressedFormat; - f->compressed = true; - } else { - f->pixelFormat.uncompressed = format; - f->compressed = false; + if(header.vkFormat == Implementation::VK_FORMAT_UNDEFINED) { + Error{} << "Trade::KtxImporter::openData(): custom formats are not supported"; + return; + } + + /* Get generic format info from Vulkan format */ + if(!f->pixelFormat.decode(header.vkFormat)) { + Error{} << "Trade::KtxImporter::openData(): unsupported format" << header.vkFormat; + return; } - if(f->compressed && header.typeSize != 1) { + /* There is no block-compressed format we can swizzle */ + CORRADE_INTERNAL_ASSERT(!f->pixelFormat.isCompressed || f->pixelFormat.swizzle == SwizzleType::None); + + if(f->pixelFormat.isCompressed && header.typeSize != 1) { Error{} << "Trade::KtxImporter::openData(): invalid type size for compressed format, expected 1 but got" << header.typeSize; return; } - f->typeSize = header.typeSize; + f->pixelFormat.typeSize = header.typeSize; /** @todo Support supercompression */ if(header.supercompressionScheme != 0) { @@ -462,7 +398,10 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { f->dimensions = Math::min(header.pixelSize, Vector3ui{1}).sum(); CORRADE_INTERNAL_ASSERT(f->dimensions >= 1 && f->dimensions <= 3); - /** @todo Assert that 3D images can't have depth format */ + if(f->dimensions == 3 && f->pixelFormat.isDepth) { + Error{} << "Trade::KtxImporter::openData(): 3D images can't have depth/stencil format"; + return; + } /* Make size in each dimension at least 1 so we don't choke on size calculations using product(). */ @@ -496,7 +435,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { Utility::Endianness::littleEndianInPlace(level.byteOffset, level.byteLength, level.uncompressedByteLength); - levelLengths[i] = f->compressed + levelLengths[i] = f->pixelFormat.isCompressed ? imageLength(mipSize, f->pixelFormat.compressed) : imageLength(mipSize, f->pixelFormat.uncompressed); @@ -517,7 +456,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { if(header.kvdByteLength > 0) { auto keyValueData = f->in.suffix(header.kvdByteOffset).prefix(header.kvdByteLength); /* Loop through entries, each one consisting of: - + UnsignedInt length Byte data[length] Byte padding[...] @@ -556,8 +495,6 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } } - /** @todo Determine if format needs swizzling */ - f->needsSwizzle = false; f->imageData = Containers::Array>{numLayers}; @@ -611,14 +548,11 @@ Containers::Optional KtxImporter::doImage1D(UnsignedInt id, Unsigne Utility::copy(levelData.data, data); /* Endian-swap if necessary */ - endianSwap(data, _f->typeSize); + endianSwap(data, _f->pixelFormat.typeSize); /* Compressed image */ - if(_f->compressed) { - /* Using default CompressedPixelStorage, need explicit one once we - support all Vulkan formats */ + if(_f->pixelFormat.isCompressed) return ImageData1D(_f->pixelFormat.compressed, levelData.size.x(), std::move(data)); - } /* Uncompressed */ if(_f->needsSwizzle) swizzlePixels(_f->pixelFormat.uncompressed, data, @@ -643,14 +577,11 @@ Containers::Optional KtxImporter::doImage2D(UnsignedInt id, Unsigne Utility::copy(levelData.data, data); /* Endian-swap if necessary */ - endianSwap(data, _f->typeSize); + endianSwap(data, _f->pixelFormat.typeSize); /* Compressed image */ - if(_f->compressed) { - /* Using default CompressedPixelStorage, need explicit one once we - support all Vulkan formats */ + if(_f->pixelFormat.isCompressed) return ImageData2D(_f->pixelFormat.compressed, levelData.size.xy(), std::move(data)); - } /* Uncompressed */ if(_f->needsSwizzle) swizzlePixels(_f->pixelFormat.uncompressed, data, @@ -675,14 +606,11 @@ Containers::Optional KtxImporter::doImage3D(UnsignedInt id, const U Utility::copy(levelData.data, data); /* Endian-swap if necessary */ - endianSwap(data, _f->typeSize); + endianSwap(data, _f->pixelFormat.typeSize); /* Compressed image */ - if(_f->compressed) { - /* Using default CompressedPixelStorage, need explicit one once we - support all Vulkan formats */ + if(_f->pixelFormat.isCompressed) return ImageData3D(_f->pixelFormat.compressed, levelData.size, std::move(data)); - } /* Uncompressed */ if(_f->needsSwizzle) swizzlePixels(_f->pixelFormat.uncompressed, data, diff --git a/src/MagnumPlugins/KtxImporter/formatMapping.hpp b/src/MagnumPlugins/KtxImporter/formatMapping.hpp new file mode 100644 index 000000000..94d4bb464 --- /dev/null +++ b/src/MagnumPlugins/KtxImporter/formatMapping.hpp @@ -0,0 +1,163 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + Copyright © 2021 Pablo Escobar + + 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. +*/ + +/* Autogenerated from formatMapping.py! Do not edit! */ + +#ifdef _p /* PixelFormat */ +_p(R8_UNORM, R8Unorm) +_p(R8G8_UNORM, RG8Unorm) +_p(R8G8B8_UNORM, RGB8Unorm) +_p(R8G8B8A8_UNORM, RGBA8Unorm) +_p(R8_SNORM, R8Snorm) +_p(R8G8_SNORM, RG8Snorm) +_p(R8G8B8_SNORM, RGB8Snorm) +_p(R8G8B8A8_SNORM, RGBA8Snorm) +_p(R8_SRGB, R8Srgb) +_p(R8G8_SRGB, RG8Srgb) +_p(R8G8B8_SRGB, RGB8Srgb) +_p(R8G8B8A8_SRGB, RGBA8Srgb) +_p(R8_UINT, R8UI) +_p(R8G8_UINT, RG8UI) +_p(R8G8B8_UINT, RGB8UI) +_p(R8G8B8A8_UINT, RGBA8UI) +_p(R8_SINT, R8I) +_p(R8G8_SINT, RG8I) +_p(R8G8B8_SINT, RGB8I) +_p(R8G8B8A8_SINT, RGBA8I) +_p(R16_UNORM, R16Unorm) +_p(R16G16_UNORM, RG16Unorm) +_p(R16G16B16_UNORM, RGB16Unorm) +_p(R16G16B16A16_UNORM, RGBA16Unorm) +_p(R16_SNORM, R16Snorm) +_p(R16G16_SNORM, RG16Snorm) +_p(R16G16B16_SNORM, RGB16Snorm) +_p(R16G16B16A16_SNORM, RGBA16Snorm) +_p(R16_UINT, R16UI) +_p(R16G16_UINT, RG16UI) +_p(R16G16B16_UINT, RGB16UI) +_p(R16G16B16A16_UINT, RGBA16UI) +_p(R16_SINT, R16I) +_p(R16G16_SINT, RG16I) +_p(R16G16B16_SINT, RGB16I) +_p(R16G16B16A16_SINT, RGBA16I) +_p(R32_UINT, R32UI) +_p(R32G32_UINT, RG32UI) +_p(R32G32B32_UINT, RGB32UI) +_p(R32G32B32A32_UINT, RGBA32UI) +_p(R32_SINT, R32I) +_p(R32G32_SINT, RG32I) +_p(R32G32B32_SINT, RGB32I) +_p(R32G32B32A32_SINT, RGBA32I) +_p(R16_SFLOAT, R16F) +_p(R16G16_SFLOAT, RG16F) +_p(R16G16B16_SFLOAT, RGB16F) +_p(R16G16B16A16_SFLOAT, RGBA16F) +_p(R32_SFLOAT, R32F) +_p(R32G32_SFLOAT, RG32F) +_p(R32G32B32_SFLOAT, RGB32F) +_p(R32G32B32A32_SFLOAT, RGBA32F) +_p(D16_UNORM, Depth16Unorm) +_p(X8_D24_UNORM_PACK32, Depth24Unorm) +_p(D32_SFLOAT, Depth32F) +_p(S8_UINT, Stencil8UI) +_p(D16_UNORM_S8_UINT, Depth16UnormStencil8UI) +_p(D24_UNORM_S8_UINT, Depth24UnormStencil8UI) +_p(D32_SFLOAT_S8_UINT, Depth32FStencil8UI) +#endif +#ifdef _c /* CompressedPixelFormat */ +_c(BC1_RGB_UNORM_BLOCK, Bc1RGBUnorm) +_c(BC1_RGB_SRGB_BLOCK, Bc1RGBSrgb) +_c(BC1_RGBA_UNORM_BLOCK, Bc1RGBAUnorm) +_c(BC1_RGBA_SRGB_BLOCK, Bc1RGBASrgb) +_c(BC2_UNORM_BLOCK, Bc2RGBAUnorm) +_c(BC2_SRGB_BLOCK, Bc2RGBASrgb) +_c(BC3_UNORM_BLOCK, Bc3RGBAUnorm) +_c(BC3_SRGB_BLOCK, Bc3RGBASrgb) +_c(BC4_UNORM_BLOCK, Bc4RUnorm) +_c(BC4_SNORM_BLOCK, Bc4RSnorm) +_c(BC5_UNORM_BLOCK, Bc5RGUnorm) +_c(BC5_SNORM_BLOCK, Bc5RGSnorm) +_c(BC6H_UFLOAT_BLOCK, Bc6hRGBUfloat) +_c(BC6H_SFLOAT_BLOCK, Bc6hRGBSfloat) +_c(BC7_UNORM_BLOCK, Bc7RGBAUnorm) +_c(BC7_SRGB_BLOCK, Bc7RGBASrgb) +_c(EAC_R11_UNORM_BLOCK, EacR11Unorm) +_c(EAC_R11_SNORM_BLOCK, EacR11Snorm) +_c(EAC_R11G11_UNORM_BLOCK, EacRG11Unorm) +_c(EAC_R11G11_SNORM_BLOCK, EacRG11Snorm) +_c(ETC2_R8G8B8_UNORM_BLOCK, Etc2RGB8Unorm) +_c(ETC2_R8G8B8_SRGB_BLOCK, Etc2RGB8Srgb) +_c(ETC2_R8G8B8A1_UNORM_BLOCK, Etc2RGB8A1Unorm) +_c(ETC2_R8G8B8A1_SRGB_BLOCK, Etc2RGB8A1Srgb) +_c(ETC2_R8G8B8A8_UNORM_BLOCK, Etc2RGBA8Unorm) +_c(ETC2_R8G8B8A8_SRGB_BLOCK, Etc2RGBA8Srgb) +_c(ASTC_4x4_UNORM_BLOCK, Astc4x4RGBAUnorm) +_c(ASTC_4x4_SRGB_BLOCK, Astc4x4RGBASrgb) +_c(ASTC_4x4_SFLOAT_BLOCK_EXT, Astc4x4RGBAF) +_c(ASTC_5x4_UNORM_BLOCK, Astc5x4RGBAUnorm) +_c(ASTC_5x4_SRGB_BLOCK, Astc5x4RGBASrgb) +_c(ASTC_5x4_SFLOAT_BLOCK_EXT, Astc5x4RGBAF) +_c(ASTC_5x5_UNORM_BLOCK, Astc5x5RGBAUnorm) +_c(ASTC_5x5_SRGB_BLOCK, Astc5x5RGBASrgb) +_c(ASTC_5x5_SFLOAT_BLOCK_EXT, Astc5x5RGBAF) +_c(ASTC_6x5_UNORM_BLOCK, Astc6x5RGBAUnorm) +_c(ASTC_6x5_SRGB_BLOCK, Astc6x5RGBASrgb) +_c(ASTC_6x5_SFLOAT_BLOCK_EXT, Astc6x5RGBAF) +_c(ASTC_6x6_UNORM_BLOCK, Astc6x6RGBAUnorm) +_c(ASTC_6x6_SRGB_BLOCK, Astc6x6RGBASrgb) +_c(ASTC_6x6_SFLOAT_BLOCK_EXT, Astc6x6RGBAF) +_c(ASTC_8x5_UNORM_BLOCK, Astc8x5RGBAUnorm) +_c(ASTC_8x5_SRGB_BLOCK, Astc8x5RGBASrgb) +_c(ASTC_8x5_SFLOAT_BLOCK_EXT, Astc8x5RGBAF) +_c(ASTC_8x6_UNORM_BLOCK, Astc8x6RGBAUnorm) +_c(ASTC_8x6_SRGB_BLOCK, Astc8x6RGBASrgb) +_c(ASTC_8x6_SFLOAT_BLOCK_EXT, Astc8x6RGBAF) +_c(ASTC_8x8_UNORM_BLOCK, Astc8x8RGBAUnorm) +_c(ASTC_8x8_SRGB_BLOCK, Astc8x8RGBASrgb) +_c(ASTC_8x8_SFLOAT_BLOCK_EXT, Astc8x8RGBAF) +_c(ASTC_10x5_UNORM_BLOCK, Astc10x5RGBAUnorm) +_c(ASTC_10x5_SRGB_BLOCK, Astc10x5RGBASrgb) +_c(ASTC_10x5_SFLOAT_BLOCK_EXT, Astc10x5RGBAF) +_c(ASTC_10x6_UNORM_BLOCK, Astc10x6RGBAUnorm) +_c(ASTC_10x6_SRGB_BLOCK, Astc10x6RGBASrgb) +_c(ASTC_10x6_SFLOAT_BLOCK_EXT, Astc10x6RGBAF) +_c(ASTC_10x8_UNORM_BLOCK, Astc10x8RGBAUnorm) +_c(ASTC_10x8_SRGB_BLOCK, Astc10x8RGBASrgb) +_c(ASTC_10x8_SFLOAT_BLOCK_EXT, Astc10x8RGBAF) +_c(ASTC_10x10_UNORM_BLOCK, Astc10x10RGBAUnorm) +_c(ASTC_10x10_SRGB_BLOCK, Astc10x10RGBASrgb) +_c(ASTC_10x10_SFLOAT_BLOCK_EXT, Astc10x10RGBAF) +_c(ASTC_12x10_UNORM_BLOCK, Astc12x10RGBAUnorm) +_c(ASTC_12x10_SRGB_BLOCK, Astc12x10RGBASrgb) +_c(ASTC_12x10_SFLOAT_BLOCK_EXT, Astc12x10RGBAF) +_c(ASTC_12x12_UNORM_BLOCK, Astc12x12RGBAUnorm) +_c(ASTC_12x12_SRGB_BLOCK, Astc12x12RGBASrgb) +_c(ASTC_12x12_SFLOAT_BLOCK_EXT, Astc12x12RGBAF) +_c(PVRTC1_2BPP_UNORM_BLOCK_IMG, PvrtcRGBA2bppUnorm) +_c(PVRTC1_2BPP_SRGB_BLOCK_IMG, PvrtcRGBA2bppSrgb) +_c(PVRTC1_4BPP_UNORM_BLOCK_IMG, PvrtcRGBA4bppUnorm) +_c(PVRTC1_4BPP_SRGB_BLOCK_IMG, PvrtcRGBA4bppSrgb) +#endif diff --git a/src/MagnumPlugins/KtxImporter/formatMapping.py b/src/MagnumPlugins/KtxImporter/formatMapping.py new file mode 100644 index 000000000..204c69398 --- /dev/null +++ b/src/MagnumPlugins/KtxImporter/formatMapping.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 + +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, +# 2020, 2021 Vladimír Vondruš +# Copyright © 2021 Pablo Escobar +# +# 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. +# + +import argparse +from collections import namedtuple +import itertools +import os +import re + +parser = argparse.ArgumentParser() +parser.add_argument('magnum_source') +parser.add_argument('-o', '--output', default='formatMapping.hpp') +args = parser.parse_args() + +magnum_dir = args.magnum_source +file_out = args.output + +print('Writing to', file_out) + +Format = namedtuple('Format', 'compressed magnum vulkan') +formats = [] + +file_in = os.path.join(magnum_dir, 'src/Magnum/Vk/PixelFormat.h') + +with open(file_in, encoding='utf-8') as f: + lines = f.readlines() + for line in lines: + # Get mapping from VkFormat to Magnum::Vk::PixelFormat + # PixelFormat and Vk::PixelFormat names are identical + match = re.search('^\s+(Compressed)?(\w+) = VK_FORMAT_(\w+),?$', line) + if match: + formats.append(Format(match.group(1) != None, match.group(2), match.group(3))) + +if len(formats) != 135: + print('Unexpected number of formats') + +# https://docs.python.org/dev/library/itertools.html#itertools-recipes +def partition(pred, iterable): + t1, t2 = itertools.tee(iterable) + return itertools.filterfalse(pred, t1), filter(pred, t2) + +# There's no PVRTC2 in Magnum::PixelFormat +formats = [f for f in formats if not f.magnum.startswith('Pvrtc2')] + +header = '''/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + Copyright © 2021 Pablo Escobar + + 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. +*/ + +/* Autogenerated from formatMapping.py! Do not edit! */ + +''' + +with open(file_out, 'w', encoding='utf-8') as outfile: + compressed = lambda f : f.compressed + formats, compressed_formats = partition(compressed, formats) + + outfile.write(header) + + outfile.write('#ifdef _p /* PixelFormat */\n') + for format in formats: + outfile.write('_p(' + format.vulkan + ', ' + format.magnum + ')\n') + outfile.write('#endif\n') + + outfile.write('#ifdef _c /* CompressedPixelFormat */\n') + for format in compressed_formats: + outfile.write('_c(' + format.vulkan + ', ' + format.magnum + ')\n') + outfile.write('#endif\n') From e1b667dd850f15a8f0593cd6edfa4bf7f2e450ce Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Sat, 17 Jul 2021 17:53:47 +0200 Subject: [PATCH 10/95] KtxImporter: check KTXswizzle metadata if the swizzle is already supported because it matches one of BGR or BGRA, we toggle it, otherwise error out --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 118 ++++++++++++++---- 1 file changed, 95 insertions(+), 23 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 0522f9c70..016d3df0a 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -60,19 +60,27 @@ std::size_t imageLength(const Vector3i& size, CompressedPixelFormat format) { return blockCount.product()*compressedBlockDataSize(format); } +template struct TypeForSize {}; +template<> struct TypeForSize<1> { typedef UnsignedByte Type; }; +template<> struct TypeForSize<2> { typedef UnsignedShort Type; }; +template<> struct TypeForSize<4> { typedef UnsignedInt Type; }; +template<> struct TypeForSize<8> { typedef UnsignedLong Type; }; + +/** @todo Can we perform endian-swap together with the swizzle? Might get messy + and it'll be untested... */ void endianSwap(Containers::ArrayView data, UnsignedInt typeSize) { switch(typeSize) { case 1: /* Single-byte or block-compressed format, nothing to do */ break; case 2: - Utility::Endianness::littleEndianInPlace(Containers::arrayCast(data)); + Utility::Endianness::littleEndianInPlace(Containers::arrayCast::Type>(data)); break; case 4: - Utility::Endianness::littleEndianInPlace(Containers::arrayCast(data)); + Utility::Endianness::littleEndianInPlace(Containers::arrayCast::Type>(data)); break; case 8: - Utility::Endianness::littleEndianInPlace(Containers::arrayCast(data)); + Utility::Endianness::littleEndianInPlace(Containers::arrayCast::Type>(data)); break; default: CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ @@ -80,24 +88,48 @@ void endianSwap(Containers::ArrayView data, UnsignedInt typeSize) { } } -void swizzlePixels(const PixelFormat format, Containers::Array& data, const char* verbosePrefix) { - if(format == PixelFormat::RGB8Unorm) { - if(verbosePrefix) Debug{} << verbosePrefix << "converting from BGR to RGB"; - auto pixels = reinterpret_cast*>(data.data()); - std::transform(pixels, pixels + data.size()/sizeof(Math::Vector3), pixels, - [](Math::Vector3 pixel) { return Math::gather<'b', 'g', 'r'>(pixel); }); - } else if(format == PixelFormat::RGBA8Unorm) { - if(verbosePrefix) Debug{} << verbosePrefix << "converting from BGRA to RGBA"; - auto pixels = reinterpret_cast*>(data.data()); - std::transform(pixels, pixels + data.size()/sizeof(Math::Vector4), pixels, - [](Math::Vector4 pixel) { return Math::gather<'b', 'g', 'r', 'a'>(pixel); }); - - } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ enum SwizzleType : UnsignedByte { None = 0, BGR, BGRA }; + +inline SwizzleType& operator ^=(SwizzleType& a, SwizzleType b) +{ + /* This is meant to toggle single enum values, make sure it's not being + used for other bit-fiddling crimes */ + CORRADE_INTERNAL_ASSERT(a == SwizzleType::None || a == b); + return a = SwizzleType(a ^ b); +} + +template void swizzlePixels(SwizzleType type, Containers::ArrayView data) { + if(type == SwizzleType::BGR) { + for(auto& pixel: Containers::arrayCast>(data)) + pixel = Math::gather<'b', 'g', 'r'>(pixel); + } else if(type == SwizzleType::BGRA) { + for(auto& pixel: Containers::arrayCast>(data)) + pixel = Math::gather<'b', 'g', 'r', 'a'>(pixel); + } +} + +void swizzlePixels(SwizzleType type, UnsignedInt typeSize, Containers::ArrayView data) { + switch(typeSize) { + case 1: + swizzlePixels::Type>(type, data); + break; + case 2: + swizzlePixels::Type>(type, data); + break; + case 4: + swizzlePixels::Type>(type, data); + break; + case 8: + swizzlePixels::Type>(type, data); + break; + default: + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + break; + } } bool validateHeader(const Implementation::KtxHeader& header, std::size_t fileSize, const char* prefix) { @@ -495,6 +527,44 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } } + { + const auto found = keyValueMap.find("KTXswizzle"_s); + if(found != keyValueMap.end()) { + const Containers::StringView swizzle{found->second.prefix(Math::min(f->dimensions, found->second.size()))}; + if(swizzle != "rgba"_s.prefix(f->dimensions)) { + bool handled = false; + /* Special cases already supported for 8-bit Vulkan formats */ + if(!f->pixelFormat.isCompressed) { + if(swizzle == "bgr"_s) { + f->pixelFormat.swizzle ^= SwizzleType::BGR; + handled = true; + } else if(swizzle == "bgra"_s) { + f->pixelFormat.swizzle ^= SwizzleType::BGRA; + handled = true; + } + } + if(!handled) { + Error{} << "Trade::KtxImporter::openData(): unsupported channel " + "mapping:" << swizzle; + return; + } + } + } + } + + /** @todo More verbose output */ + if(flags() & ImporterFlag::Verbose) { + switch(f->pixelFormat.swizzle) { + case SwizzleType::BGR: + Debug{} << "Trade::KtxImporter::openData(): format requires conversion from BGR to RGB"; + break; + case SwizzleType::BGRA: + Debug{} << "Trade::KtxImporter::openData(): format requires conversion from BGRA to RGBA"; + break; + default: + break; + } + } f->imageData = Containers::Array>{numLayers}; @@ -527,7 +597,6 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } /** @todo Read KTXorientation and flip image? What is the default? */ - /** @todo Read KTXswizzle and apply it. Can't do that for block-compressed formats, just ignore in that case? */ /** @todo Read KTXanimData and expose frame time between images through doImporterState (same as StbImageImporter). What if we need doImporterState for something else, like other API format @@ -555,8 +624,9 @@ Containers::Optional KtxImporter::doImage1D(UnsignedInt id, Unsigne return ImageData1D(_f->pixelFormat.compressed, levelData.size.x(), std::move(data)); /* Uncompressed */ - if(_f->needsSwizzle) swizzlePixels(_f->pixelFormat.uncompressed, data, - flags() & ImporterFlag::Verbose ? "Trade::KtxImporter::image2D():" : nullptr); + + /* Swizzle BGR(A) if necessary */ + swizzlePixels(_f->pixelFormat.swizzle, _f->pixelFormat.typeSize, data); /* Rows are tightly packed */ PixelStorage storage; @@ -584,8 +654,9 @@ Containers::Optional KtxImporter::doImage2D(UnsignedInt id, Unsigne return ImageData2D(_f->pixelFormat.compressed, levelData.size.xy(), std::move(data)); /* Uncompressed */ - if(_f->needsSwizzle) swizzlePixels(_f->pixelFormat.uncompressed, data, - flags() & ImporterFlag::Verbose ? "Trade::KtxImporter::image2D():" : nullptr); + + /* Swizzle BGR(A) if necessary */ + swizzlePixels(_f->pixelFormat.swizzle, _f->pixelFormat.typeSize, data); /* Rows are tightly packed */ PixelStorage storage; @@ -613,8 +684,9 @@ Containers::Optional KtxImporter::doImage3D(UnsignedInt id, const U return ImageData3D(_f->pixelFormat.compressed, levelData.size, std::move(data)); /* Uncompressed */ - if(_f->needsSwizzle) swizzlePixels(_f->pixelFormat.uncompressed, data, - flags() & ImporterFlag::Verbose ? "Trade::KtxImporter::image3D():" : nullptr); + + /* Swizzle BGR(A) if necessary */ + swizzlePixels(_f->pixelFormat.swizzle, _f->pixelFormat.typeSize, data); /* Rows are tightly packed */ PixelStorage storage; From bec3e8b0009967a1bfd9262cceff2916dd786129 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Sat, 17 Jul 2021 17:55:03 +0200 Subject: [PATCH 11/95] KtxImporter: read orientation still needs code for actually flipping the data --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 83 ++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 016d3df0a..578b4c6d1 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -249,6 +249,7 @@ struct KtxImporter::File { Containers::Array in; UnsignedByte dimensions; + BoolVector3 flip; struct Format { bool decode(Implementation::VkFormat vkFormat); @@ -527,6 +528,82 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } } + /** @todo Remove, only for debugging */ + Debug{} << "Key/value data:"; + for(const auto& entry : keyValueMap) { + if(entry.first == "KTXorientation" || + entry.first == "KTXswizzle" || + entry.first == "KTXcubemapIncomplete") + Debug{} << entry.first << ":" << Containers::StringView{entry.second}; + } + + using namespace Containers::Literals; + + /* Read image orientation so we can flip if needed. + + l/r = left/right + u/d = up/down + o/i = out of/into screen + + Default is assumed to be rdi, Magnum expects ruo. */ + /** @todo Is it really ruo? I can't find any info on z texture origin in GL. */ + { + constexpr auto targetOrientation = "ruo"_s; + constexpr auto defaultOrientation = "rdi"_s; + + bool useDefaultOrientation = true; + const auto found = keyValueMap.find("KTXorientation"_s); + if(found != keyValueMap.end()) { + const Containers::StringView orientation{found->second}; + if(orientation.size() >= f->dimensions) { + constexpr Containers::StringView validOrientations[3]{"rl"_s, "du"_s, "io"_s}; + for(UnsignedByte i = 0; i != f->dimensions; ++i) { + useDefaultOrientation = !validOrientations[i].contains(orientation[i]); + if(useDefaultOrientation) break; + f->flip.set(i, orientation[i] != targetOrientation[i]); + } + } + } + + if(useDefaultOrientation) { + Warning{} << "Trade::KtxImporter::openData(): missing or invalid " + "orientation, falling back to" << defaultOrientation; + f->flip = Math::Vector3::from(defaultOrientation.data()) != + Math::Vector3::from(targetOrientation.data()); + } + } + + /* We can't reasonably perform axis flips on block-compressed data. + Emit a warning and pretend there is no flipping necessary. */ + if(f->pixelFormat.isCompressed && f->flip.any()) { + f->flip = BoolVector3{}; + Warning{} << "Trade::KtxImporter::openData(): block-compressed image " + "was encoded with non-default axis orientations, imported data " + "will have wrong orientation"; + } + + /** @todo KTX spec seems to really insist on rd for cubemaps but the + wording is odd, I can't tell if they're saying it's mandatory or + not: https://github.khronos.org/KTX-Specification/#cubemapOrientation + Face orientation (+X, -X, etc.) is based on a left-handed y-up + coordinate system, but neither GL nor Vulkan have that. The + appendix implies that both need coordinate transformations. Do we + have to do anything here? Flip faces/axes to match GL or Vulkan + expectations? */ + + /* Incomplete cubemaps are a 'feature' of KTX files. We just import them + as layers (which is how they're exposed to us). */ + if(header.faceCount != 6) { + const auto found = keyValueMap.find("KTXcubemapIncomplete"_s); + if(found != keyValueMap.end() && !found->second.empty()) { + const UnsignedByte mask = found->second.front(); + /* 0x3F = 00111111b = all cubemaps present, seems to be allowed */ + if((mask & 0x3F) != 0x3F) + Warning{} << "Trade::KtxImporter::openData(): image contains incomplete " + "cubemap faces, importing faces as array layers"; + } + } + { const auto found = keyValueMap.find("KTXswizzle"_s); if(found != keyValueMap.end()) { @@ -596,7 +673,6 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } } - /** @todo Read KTXorientation and flip image? What is the default? */ /** @todo Read KTXanimData and expose frame time between images through doImporterState (same as StbImageImporter). What if we need doImporterState for something else, like other API format @@ -614,6 +690,9 @@ Containers::Optional KtxImporter::doImage1D(UnsignedInt id, Unsigne /* Copy image data */ Containers::Array data{NoInit, levelData.data.size()}; + /** @todo Flip axes if necessary. This should be simple(?) with StridedArrayView + and negative stride. Note that we already disable flipping for + block-compressed data, so no need to check here. */ Utility::copy(levelData.data, data); /* Endian-swap if necessary */ @@ -644,6 +723,7 @@ Containers::Optional KtxImporter::doImage2D(UnsignedInt id, Unsigne /* Copy image data */ Containers::Array data{NoInit, levelData.data.size()}; + /** @todo Flip axes if necessary */ Utility::copy(levelData.data, data); /* Endian-swap if necessary */ @@ -674,6 +754,7 @@ Containers::Optional KtxImporter::doImage3D(UnsignedInt id, const U /* Copy image data */ Containers::Array data{NoInit, levelData.data.size()}; + /** @todo Flip axes if necessary */ Utility::copy(levelData.data, data); /* Endian-swap if necessary */ From d289b6ffabef2ad2626d714069678a1f8b45cb56 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Sat, 17 Jul 2021 18:51:30 +0200 Subject: [PATCH 12/95] KtxImporter: cleanup --- src/MagnumPlugins/KtxImporter/CMakeLists.txt | 4 - src/MagnumPlugins/KtxImporter/KtxHeader.h | 4 +- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 129 ++++++++---------- src/MagnumPlugins/KtxImporter/KtxImporter.h | 7 - 4 files changed, 63 insertions(+), 81 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/CMakeLists.txt b/src/MagnumPlugins/KtxImporter/CMakeLists.txt index 69c382e9d..b7de03e11 100644 --- a/src/MagnumPlugins/KtxImporter/CMakeLists.txt +++ b/src/MagnumPlugins/KtxImporter/CMakeLists.txt @@ -66,9 +66,5 @@ if(MAGNUM_KTXIMPORTER_BUILD_STATIC) target_sources(KtxImporter INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/importStaticPlugin.cpp) endif() -if(BUILD_TESTS) - add_subdirectory(Test) -endif() - # MagnumPlugins KtxImporter target alias for superprojects add_library(MagnumPlugins::KtxImporter ALIAS KtxImporter) diff --git a/src/MagnumPlugins/KtxImporter/KtxHeader.h b/src/MagnumPlugins/KtxImporter/KtxHeader.h index 1c3a7c15f..6c2f8a9ea 100644 --- a/src/MagnumPlugins/KtxImporter/KtxHeader.h +++ b/src/MagnumPlugins/KtxImporter/KtxHeader.h @@ -27,6 +27,7 @@ */ #include +#include /* Used by both KtxImporter and KtxImageConverter, which is why it isn't directly inside KtxImporter.cpp. OTOH it doesn't need to be exposed @@ -321,7 +322,8 @@ constexpr char KtxFileIdentifier[12]{ static_assert(sizeof(KtxFileIdentifier) == sizeof(KtxHeader::identifier), "Improper size of KtxFileIdentifier data"); constexpr std::size_t KtxFileVersionOffset = 5; -static_assert(KtxFileVersionOffset < sizeof(KtxFileIdentifier), "KtxFileVersionOffset out of bounds"); +constexpr std::size_t KtxFileVersionLength = 2; +static_assert(KtxFileVersionOffset + KtxFileVersionLength <= sizeof(KtxFileIdentifier), "KtxFileVersion(Offset|Length) out of bounds"); }}} diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 578b4c6d1..c64d0c565 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -133,16 +134,21 @@ void swizzlePixels(SwizzleType type, UnsignedInt typeSize, Containers::ArrayView } bool validateHeader(const Implementation::KtxHeader& header, std::size_t fileSize, const char* prefix) { - /* Check magic string to verify this is a KTX2 file */ - const auto identifier = Containers::StringView{header.identifier, sizeof(header.identifier)}; - if(identifier != Implementation::KtxFileIdentifier) { - /* Print a useful error if this is a KTX file with an unsupported - version. KTX1 uses the same identifier but different version string. */ - if(std::memcmp(identifier.data(), Implementation::KtxFileIdentifier, Implementation::KtxFileVersionOffset) == 0) - Error() << prefix << "unsupported KTX version, expected 20 but got" << - identifier.suffix(Implementation::KtxFileVersionOffset).prefix(2); - else - Error() << prefix << "wrong file signature"; + /* Check magic string */ + const Containers::StringView identifier{header.identifier, sizeof(header.identifier)}; + const Containers::StringView expected{Implementation::KtxFileIdentifier, sizeof(header.identifier)}; + if(identifier != expected) { + /* Print a useful error for a KTX file with an unsupported version. + KTX1 uses the same magic string but with a different version string. */ + if(identifier.hasPrefix(expected.prefix(Implementation::KtxFileVersionOffset))) { + const auto version = identifier.suffix(Implementation::KtxFileVersionOffset).prefix(Implementation::KtxFileVersionLength); + if(version != "20") { + Error() << prefix << "unsupported KTX version, expected 20 but got" << version; + return false; + } + } + + Error() << prefix << "wrong file signature"; return false; } @@ -212,9 +218,8 @@ bool validateHeader(const Implementation::KtxHeader& header, std::size_t fileSiz bool validateLevel(const Implementation::KtxHeader& header, std::size_t fileSize, const Implementation::KtxLevel& level, std::size_t imageLength, const char* prefix) { CORRADE_INTERNAL_ASSERT(imageLength > 0); - /* Both lengths should be equal without supercompression. Be lenient - here and only emit a warning in case some shitty exporter gets this - wrong. */ + /* Both lengths should be equal without supercompression. Be lenient here + and only emit a warning in case some shitty exporter gets this wrong. */ if(header.supercompressionScheme == 0 && level.byteLength != level.uncompressedByteLength) { Warning{} << prefix << "mismatching image data sizes, both compressed " "and uncompressed should be equal but got" << @@ -285,7 +290,7 @@ bool KtxImporter::File::Format::decode(Implementation::VkFormat vkFormat) { } /* PixelFormat doesn't contain any of the swizzled formats. Figure it out - from the Vulkan format and remember that we need to swizzle in doImage. */ + from the Vulkan format and remember that we need to swizzle in doImage(). */ if(format == PixelFormat{}) { switch(vkFormat) { case Implementation::VK_FORMAT_B8G8R8_UNORM: format = PixelFormat::RGB8Unorm; break; @@ -305,7 +310,7 @@ bool KtxImporter::File::Format::decode(Implementation::VkFormat vkFormat) { if(format != PixelFormat{}) { const UnsignedInt size = pixelSize(format); CORRADE_INTERNAL_ASSERT(size == 3 || size == 4); - swizzle = size == 3 ? SwizzleType::BGR : SwizzleType::BGRA; + swizzle = (size == 3) ? SwizzleType::BGR : SwizzleType::BGRA; } } @@ -330,7 +335,7 @@ bool KtxImporter::File::Format::decode(Implementation::VkFormat vkFormat) { return true; } - /* Find block-compressed pixel format. No swizzling necessary (or possible!). */ + /* Find block-compressed pixel format, no swizzling possible */ CompressedPixelFormat compressedFormat{}; switch(vkFormat) { #define _c(vulkan, magnum) case Implementation::VK_FORMAT_ ## vulkan: compressedFormat = CompressedPixelFormat:: ## magnum; break; @@ -373,7 +378,10 @@ void KtxImporter::doClose() { _f = nullptr; } void KtxImporter::doOpenData(const Containers::ArrayView data) { /* Check if the file is long enough for the header */ - if(data.size() < sizeof(Implementation::KtxHeader)) { + if(data.empty()) { + Error{} << "Trade::KtxImporter::openData(): the file is empty"; + return; + } else if(data.size() < sizeof(Implementation::KtxHeader)) { Error{} << "Trade::KtxImporter::openData(): file header too short, expected at least" << sizeof(Implementation::KtxHeader) << "bytes but got" << data.size(); return; } @@ -391,9 +399,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { header.kvdByteOffset, header.kvdByteLength, header.sgdByteOffset, header.sgdByteLength); - /* Perform some sanity checks on header data and check if - there is enough space for the level index, data format - descriptor and key/value data */ + /* Perform some sanity checks on header data, including byte ranges */ if(!validateHeader(header, data.size(), "Trade::KtxImporter::openData():")) return; @@ -436,8 +442,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { return; } - /* Make size in each dimension at least 1 so we don't choke on size - calculations using product(). */ + /* Make sure we don't choke on size calculations using product() */ const Vector3i size = Math::max(Vector3i{header.pixelSize}, Vector3i{1}); /* Number of array layers, imported as separate images */ @@ -447,9 +452,9 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { have to be array layers with extra metadata inside key/value data. */ const UnsignedInt numFaces = header.faceCount; - /* levelCount of 0 indicates to the user to generate mipmaps, not allowed - for block-compressed formats. We don't really care either way - and treat 0 and 1 the same. */ + /* levelCount == 0 indicates to the user/importer to generate mipmaps. We + don't really care either way since we don't generate mipmaps or pass + this on to the user. */ const UnsignedInt numMipmaps = Math::max(header.levelCount, 1u); /* KTX stores byte ranges for each mipmap in the level index, from largest @@ -479,15 +484,17 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { mipSize = Math::max(mipSize >> 1, Vector3i{1}); } + /* Read metadata */ + /** @todo Read data format descriptor */ if(header.dfdByteLength > 0) { - const auto DataFormatDescriptorData = f->in.suffix(header.dfdByteOffset).prefix(header.dfdByteLength); + Containers::ArrayView dataFormatDescriptorData = f->in.suffix(header.dfdByteOffset).prefix(header.dfdByteLength); } /* Read key/value data, optional */ std::map> keyValueMap; if(header.kvdByteLength > 0) { - auto keyValueData = f->in.suffix(header.kvdByteOffset).prefix(header.kvdByteLength); + Containers::ArrayView keyValueData{f->in.suffix(header.kvdByteOffset).prefix(header.kvdByteLength)}; /* Loop through entries, each one consisting of: UnsignedInt length @@ -505,22 +512,17 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { current += sizeof(length); if(current + length < keyValueData.size()) { - const auto entry = keyValueData.suffix(current).prefix(length); - - /* Split at zero separating the key string and value */ - const std::size_t keyLength = std::find(entry.begin(), entry.end(), '\0') - entry.begin(); - if(keyLength == 0 || keyLength >= entry.size()) - Warning{} << "Trade::KtxImporter::openData(): invalid key/value entry, skipping"; - else { - const Containers::StringView key{entry.data()}; - CORRADE_INTERNAL_ASSERT(key.size() == keyLength); - - if(keyValueMap.count(key) > 0) - keyValueMap[key] = entry.suffix(keyLength + 1); - else - Warning{} << "Trade::KtxImporter::openData(): key" << - key << "already set, skipping"; - } + const Containers::StringView entry{keyValueData.suffix(current).prefix(length)}; + const Containers::Array3 split = entry.partition('\0'); + const auto key = split[0]; + const auto value = split[2]; + + if(key.isEmpty() || value.isEmpty()) + Warning{} << "Trade::KtxImporter::openData(): invalid key/value entry, skipping"; + else if(keyValueMap.count(key) > 0) + Warning{} << "Trade::KtxImporter::openData(): key" << key << "already set, skipping"; + else + keyValueMap[key] = value; } /* Length value is dword-aligned, guaranteed for the first length by the file layout */ @@ -528,15 +530,6 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } } - /** @todo Remove, only for debugging */ - Debug{} << "Key/value data:"; - for(const auto& entry : keyValueMap) { - if(entry.first == "KTXorientation" || - entry.first == "KTXswizzle" || - entry.first == "KTXcubemapIncomplete") - Debug{} << entry.first << ":" << Containers::StringView{entry.second}; - } - using namespace Containers::Literals; /* Read image orientation so we can flip if needed. @@ -545,8 +538,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { u/d = up/down o/i = out of/into screen - Default is assumed to be rdi, Magnum expects ruo. */ - /** @todo Is it really ruo? I can't find any info on z texture origin in GL. */ + Default is assumed to be rdi, Magnum/GL expects ruo. */ { constexpr auto targetOrientation = "ruo"_s; constexpr auto defaultOrientation = "rdi"_s; @@ -585,6 +577,8 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { /** @todo KTX spec seems to really insist on rd for cubemaps but the wording is odd, I can't tell if they're saying it's mandatory or not: https://github.khronos.org/KTX-Specification/#cubemapOrientation + The toktx tool from Khronos Texture Tools also forces rd for + cubemaps, so we should probably do that too. Face orientation (+X, -X, etc.) is based on a left-handed y-up coordinate system, but neither GL nor Vulkan have that. The appendix implies that both need coordinate transformations. Do we @@ -593,15 +587,9 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { /* Incomplete cubemaps are a 'feature' of KTX files. We just import them as layers (which is how they're exposed to us). */ - if(header.faceCount != 6) { - const auto found = keyValueMap.find("KTXcubemapIncomplete"_s); - if(found != keyValueMap.end() && !found->second.empty()) { - const UnsignedByte mask = found->second.front(); - /* 0x3F = 00111111b = all cubemaps present, seems to be allowed */ - if((mask & 0x3F) != 0x3F) - Warning{} << "Trade::KtxImporter::openData(): image contains incomplete " - "cubemap faces, importing faces as array layers"; - } + if(header.faceCount != 6 && keyValueMap.count("KTXcubemapIncomplete"_s) > 0) { + Warning{} << "Trade::KtxImporter::openData(): image contains incomplete " + "cubemap faces, importing faces as array layers"; } { @@ -707,9 +695,10 @@ Containers::Optional KtxImporter::doImage1D(UnsignedInt id, Unsigne /* Swizzle BGR(A) if necessary */ swizzlePixels(_f->pixelFormat.swizzle, _f->pixelFormat.typeSize, data); - /* Rows are tightly packed */ + /* Adjust pixel storage if row size is not four byte aligned */ PixelStorage storage; - storage.setAlignment(1); + if((levelData.size.x()*pixelSize(_f->pixelFormat.uncompressed))%4 != 0) + storage.setAlignment(1); return ImageData1D{storage, _f->pixelFormat.uncompressed, levelData.size.x(), std::move(data)}; } @@ -738,9 +727,10 @@ Containers::Optional KtxImporter::doImage2D(UnsignedInt id, Unsigne /* Swizzle BGR(A) if necessary */ swizzlePixels(_f->pixelFormat.swizzle, _f->pixelFormat.typeSize, data); - /* Rows are tightly packed */ + /* Adjust pixel storage if row size is not four byte aligned */ PixelStorage storage; - storage.setAlignment(1); + if((levelData.size.x()*pixelSize(_f->pixelFormat.uncompressed))%4 != 0) + storage.setAlignment(1); return ImageData2D{storage, _f->pixelFormat.uncompressed, levelData.size.xy(), std::move(data)}; } @@ -769,9 +759,10 @@ Containers::Optional KtxImporter::doImage3D(UnsignedInt id, const U /* Swizzle BGR(A) if necessary */ swizzlePixels(_f->pixelFormat.swizzle, _f->pixelFormat.typeSize, data); - /* Rows are tightly packed */ + /* Adjust pixel storage if row size is not four byte aligned */ PixelStorage storage; - storage.setAlignment(1); + if((levelData.size.x()*pixelSize(_f->pixelFormat.uncompressed))%4 != 0) + storage.setAlignment(1); return ImageData3D{storage, _f->pixelFormat.uncompressed, levelData.size, std::move(data)}; } diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.h b/src/MagnumPlugins/KtxImporter/KtxImporter.h index 72a1fb186..e003c965e 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.h +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.h @@ -26,13 +26,6 @@ DEALINGS IN THE SOFTWARE. */ -/** @file - * @brief Class @ref Magnum::Trade::KtxImporter - */ - -#include -#include - #include #include "MagnumPlugins/KtxImporter/configure.h" From cc14641abdd9941153f6732eeeffb1b3127e8ed8 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Tue, 20 Jul 2021 01:06:10 +0200 Subject: [PATCH 13/95] KtxImporter: import array layers and cubemap faces as extra image dimensions This lets users quickly load entire arrays and (layered) cubemaps to the GPU. For 3D arrays, layers are exported as separate images. This is pending additions to TextureData::Type to expose array textures. --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 141 ++++++++++-------- src/MagnumPlugins/KtxImporter/KtxImporter.h | 3 + 2 files changed, 85 insertions(+), 59 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index c64d0c565..6ef7333ac 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include "MagnumPlugins/KtxImporter/KtxHeader.h" @@ -253,7 +254,12 @@ struct KtxImporter::File { Containers::Array in; - UnsignedByte dimensions; + /* Dimensions of the source image (1-3) */ + UnsignedByte numDimensions; + /* Dimensions of the imported image data, including extra dimensions for + array layers or cubemap faces */ + UnsignedByte numDataDimensions; + TextureData::Type type; BoolVector3 flip; struct Format { @@ -387,7 +393,6 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } Implementation::KtxHeader header = *reinterpret_cast(data.data()); - std::size_t offset = sizeof(Implementation::KtxHeader); /* KTX2 uses little-endian everywhere */ Utility::Endianness::littleEndianInPlace( @@ -434,10 +439,10 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { return; } - f->dimensions = Math::min(header.pixelSize, Vector3ui{1}).sum(); - CORRADE_INTERNAL_ASSERT(f->dimensions >= 1 && f->dimensions <= 3); + f->numDimensions = Math::min(header.pixelSize, Vector3ui{1}).sum(); + CORRADE_INTERNAL_ASSERT(f->numDimensions >= 1 && f->numDimensions <= 3); - if(f->dimensions == 3 && f->pixelFormat.isDepth) { + if(f->numDimensions == 3 && f->pixelFormat.isDepth) { Error{} << "Trade::KtxImporter::openData(): 3D images can't have depth/stencil format"; return; } @@ -445,11 +450,16 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { /* Make sure we don't choke on size calculations using product() */ const Vector3i size = Math::max(Vector3i{header.pixelSize}, Vector3i{1}); - /* Number of array layers, imported as separate images */ + /* Number of array layers, imported as extra image dimensions (except + for 3D images, there it's one Image3D per layer). + + layerCount == 1 is a 2D array image with one level, we export it as such + so that there are no surprises. This is equivalent to how we handle + depth == 1. */ + const bool isLayered = header.layerCount > 0; const UnsignedInt numLayers = Math::max(header.layerCount, 1u); - /* Cubemap faces, either 1 or 6. KTX allows incomplete cubemaps but those - have to be array layers with extra metadata inside key/value data. */ + const bool isCubemap = header.faceCount == 6; const UnsignedInt numFaces = header.faceCount; /* levelCount == 0 indicates to the user/importer to generate mipmaps. We @@ -457,33 +467,71 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { this on to the user. */ const UnsignedInt numMipmaps = Math::max(header.levelCount, 1u); - /* KTX stores byte ranges for each mipmap in the level index, from largest - to smallest. The actual pixel data is usually arranged in reverse order - for streaming but we only need the ranges. */ + /* The level index contains byte ranges for each mipmap, from largest to + smallest. Each mipmap contains tightly packed images ordered by + layers, faces, slices, rows, columns. */ const std::size_t levelIndexSize = numMipmaps*sizeof(Implementation::KtxLevel); const auto levelIndex = Containers::arrayCast( - f->in.suffix(offset).prefix(levelIndexSize)); + f->in.suffix(sizeof(Implementation::KtxHeader)).prefix(levelIndexSize)); + + /* Extract image data views */ + + const UnsignedInt numImages = (f->numDimensions == 3) ? numLayers : 1; + f->imageData = Containers::Array>{numImages}; + for(UnsignedInt image = 0; image != numImages; ++image) + f->imageData[image] = Containers::Array{numMipmaps}; - /* We later loop over all levels multiple times, so prepare and validate - a few things up-front */ - Containers::Array levelLengths{numMipmaps}; Vector3i mipSize{size}; - for(UnsignedInt i = 0; i != levelIndex.size(); ++i) { + for(UnsignedInt i = 0; i != numMipmaps; ++i) { auto& level = levelIndex[i]; Utility::Endianness::littleEndianInPlace(level.byteOffset, level.byteLength, level.uncompressedByteLength); - levelLengths[i] = f->pixelFormat.isCompressed + const std::size_t partLength = f->pixelFormat.isCompressed ? imageLength(mipSize, f->pixelFormat.compressed) : imageLength(mipSize, f->pixelFormat.uncompressed); - if(!validateLevel(header, data.size(), level, levelLengths[i], "Trade::KtxImporter::openData():")) + if(!validateLevel(header, data.size(), level, partLength, "Trade::KtxImporter::openData():")) return; + for(UnsignedInt image = 0; image != numImages; ++image) { + std::size_t length = partLength * numFaces; + std::size_t imageOffset = 0; + + if(numImages == numLayers) + imageOffset = image * length; + else + length *= numLayers; + + f->imageData[image][i] = {mipSize, f->in.suffix(level.byteOffset).suffix(imageOffset).prefix(length)}; + } + /* Shrink to next power of 2 */ mipSize = Math::max(mipSize >> 1, Vector3i{1}); } + /* Remember the image type for doImage() */ + switch(f->numDimensions) { + /** @todo array enums */ + case 1: + f->type = isLayered ? TextureData::Type::Texture1D/*Array*/ : TextureData::Type::Texture1D; + break; + case 2: + if(isCubemap) + f->type = isLayered ? TextureData::Type::Cube/*Array*/ : TextureData::Type::Cube; + else + f->type = isLayered ? TextureData::Type::Texture2D/*Array*/ : TextureData::Type::Texture2D; + break; + case 3: + f->type = TextureData::Type::Texture3D; + break; + default: CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + + f->numDataDimensions = f->numDimensions; + if(f->numDimensions != 3 && (isLayered || isCubemap)) + f->numDataDimensions++; + /* Read metadata */ /** @todo Read data format descriptor */ @@ -547,9 +595,9 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { const auto found = keyValueMap.find("KTXorientation"_s); if(found != keyValueMap.end()) { const Containers::StringView orientation{found->second}; - if(orientation.size() >= f->dimensions) { + if(orientation.size() >= f->numDimensions) { constexpr Containers::StringView validOrientations[3]{"rl"_s, "du"_s, "io"_s}; - for(UnsignedByte i = 0; i != f->dimensions; ++i) { + for(UnsignedByte i = 0; i != f->numDimensions; ++i) { useDefaultOrientation = !validOrientations[i].contains(orientation[i]); if(useDefaultOrientation) break; f->flip.set(i, orientation[i] != targetOrientation[i]); @@ -593,10 +641,11 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } { + /** @todo We need the channel count to correctly validate swizzle length. Read the DFD! */ const auto found = keyValueMap.find("KTXswizzle"_s); if(found != keyValueMap.end()) { - const Containers::StringView swizzle{found->second.prefix(Math::min(f->dimensions, found->second.size()))}; - if(swizzle != "rgba"_s.prefix(f->dimensions)) { + const Containers::StringView swizzle{found->second.prefix(Math::min(f->numDimensions, found->second.size()))}; + if(swizzle != "rgba"_s.prefix(f->numDimensions)) { bool handled = false; /* Special cases already supported for 8-bit Vulkan formats */ if(!f->pixelFormat.isCompressed) { @@ -631,45 +680,12 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } } - f->imageData = Containers::Array>{numLayers}; - - /** @todo Export cubemaps as 3D images instead of levels */ - - for(UnsignedInt layer = 0; layer != numLayers; ++layer) { - f->imageData[layer] = Containers::Array{numFaces*numMipmaps}; - /* This matches the level order of DdsImporter: faces, mipmaps */ - std::size_t currentLevel = 0; - /* +X, -X, +Y, -Y, +Z, -Z */ - for(UnsignedInt face = 0; face != numFaces; ++face) { - mipSize = size; - - /* Load all mipmaps for current face */ - for(UnsignedInt i = 0; i != levelIndex.size(); ++i) { - /* Each mipmap byte range contains tightly packed images, - ordered as follows: - layers, faces, (slices, rows, columns) */ - const std::size_t length = levelLengths[i]; - const std::size_t imageOffset = (layer*numFaces + face)*length; - - /* validateLevel already checked if the level data is large - enough for all images */ - f->imageData[layer][currentLevel++] = {mipSize, f->in.suffix(levelIndex[i].byteOffset).suffix(imageOffset).prefix(length)}; - - /* Shrink to next power of 2 */ - mipSize = Math::max(mipSize >> 1, Vector3i{1}); - } - } - } - - /** @todo Read KTXanimData and expose frame time between images through - doImporterState (same as StbImageImporter). What if we need - doImporterState for something else, like other API format - enum values (KTXdxgiFormat__ etc.)? */ + /** @todo Read KTXanimData and expose frame time between images */ _f = std::move(f); } -UnsignedInt KtxImporter::doImage1DCount() const { return (_f->dimensions == 1) ? _f->imageData.size() : 0; } +UnsignedInt KtxImporter::doImage1DCount() const { return (_f->numDataDimensions == 1) ? _f->imageData.size() : 0; } UnsignedInt KtxImporter::doImage1DLevelCount(UnsignedInt id) { return _f->imageData[id].size(); } @@ -703,7 +719,7 @@ Containers::Optional KtxImporter::doImage1D(UnsignedInt id, Unsigne return ImageData1D{storage, _f->pixelFormat.uncompressed, levelData.size.x(), std::move(data)}; } -UnsignedInt KtxImporter::doImage2DCount() const { return (_f->dimensions == 2) ? _f->imageData.size() : 0; } +UnsignedInt KtxImporter::doImage2DCount() const { return (_f->numDataDimensions == 2) ? _f->imageData.size() : 0; } UnsignedInt KtxImporter::doImage2DLevelCount(UnsignedInt id) { return _f->imageData[id].size(); } @@ -735,7 +751,7 @@ Containers::Optional KtxImporter::doImage2D(UnsignedInt id, Unsigne return ImageData2D{storage, _f->pixelFormat.uncompressed, levelData.size.xy(), std::move(data)}; } -UnsignedInt KtxImporter::doImage3DCount() const { return (_f->dimensions == 3) ? _f->imageData.size() : 0; } +UnsignedInt KtxImporter::doImage3DCount() const { return (_f->numDataDimensions == 3) ? _f->imageData.size() : 0; } UnsignedInt KtxImporter::doImage3DLevelCount(UnsignedInt id) { return _f->imageData[id].size(); } @@ -767,6 +783,13 @@ Containers::Optional KtxImporter::doImage3D(UnsignedInt id, const U return ImageData3D{storage, _f->pixelFormat.uncompressed, levelData.size, std::move(data)}; } +UnsignedInt KtxImporter::doTextureCount() const { return _f->imageData.size(); } + +Containers::Optional KtxImporter::doTexture(UnsignedInt id) { + return TextureData{_f->type, SamplerFilter::Nearest, SamplerFilter::Nearest, + SamplerMipmap::Base, SamplerWrapping::Repeat, id}; +} + }} CORRADE_PLUGIN_REGISTER(KtxImporter, Magnum::Trade::KtxImporter, diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.h b/src/MagnumPlugins/KtxImporter/KtxImporter.h index e003c965e..0775173c9 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.h +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.h @@ -76,6 +76,9 @@ class MAGNUM_KTXIMPORTER_EXPORT KtxImporter: public AbstractImporter { MAGNUM_KTXIMPORTER_LOCAL UnsignedInt doImage3DLevelCount(UnsignedInt id) override; MAGNUM_KTXIMPORTER_LOCAL Containers::Optional doImage3D(UnsignedInt id, UnsignedInt level) override; + MAGNUM_KTXIMPORTER_LOCAL UnsignedInt doTextureCount() const override; + MAGNUM_KTXIMPORTER_LOCAL Containers::Optional doTexture(UnsignedInt id) override; + private: struct File; Containers::Pointer _f; From 7021c96c060aa6bf665804d7b25412d612eb69cb Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Tue, 20 Jul 2021 21:34:04 +0200 Subject: [PATCH 14/95] KtxImporter: validate data format descriptor --- src/MagnumPlugins/KtxImporter/KtxHeader.h | 160 +++++++++-- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 148 ++++++---- .../KtxImporter/formatMapping.hpp | 262 +++++++++--------- .../KtxImporter/formatMapping.py | 14 +- 4 files changed, 379 insertions(+), 205 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxHeader.h b/src/MagnumPlugins/KtxImporter/KtxHeader.h index 6c2f8a9ea..1b981129a 100644 --- a/src/MagnumPlugins/KtxImporter/KtxHeader.h +++ b/src/MagnumPlugins/KtxImporter/KtxHeader.h @@ -35,9 +35,11 @@ namespace Magnum { namespace Trade { namespace Implementation { -/* Taken from flextVk.h, with KHR aliases removed. +/* Taken from magnum/src/MagnumExternal/Vulkan/flextVk.h + (commit 9d4a8b49943a084cff64550792bb2eba223e0e03) + Contains all formats from: - - 1.2 core + - 1.2 core (without KHR aliases) - EXT_texture_compression_astc_hdr - IMG_format_pvrtc */ enum VkFormat : UnsignedInt { @@ -284,23 +286,42 @@ enum VkFormat : UnsignedInt { VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007 }; +enum VkFormatSuffix : UnsignedByte { + UNORM = 1, + SNORM, + UINT, + SINT, + UFLOAT, + SFLOAT, + SRGB + /* SCALED formats are not allowed by KTX, and not exposed by Magnum, either. + They're usually used as vertex formats. */ +}; + +enum SuperCompressionScheme : UnsignedInt { + None = 0, + BasisLZ = 1, + Zstandard = 2, + ZLIB = 3 +}; + /* KTX2 file header */ struct KtxHeader { - char identifier[12]; /* File identifier */ - VkFormat vkFormat; /* Texel format, VK_FORMAT_UNDEFINED = custom */ - UnsignedInt typeSize; /* Size of channel data type, in bytes */ - Vector3ui pixelSize; /* Image level 0 size */ - UnsignedInt layerCount; /* Number of array elements */ - UnsignedInt faceCount; /* Number of cubemap faces */ - UnsignedInt levelCount; /* Number of mip levels */ - UnsignedInt supercompressionScheme; + char identifier[12]; /* File identifier */ + VkFormat vkFormat; /* Texel format, VK_FORMAT_UNDEFINED = custom */ + UnsignedInt typeSize; /* Size of channel data type, in bytes */ + Vector3ui imageSize; /* Image level 0 size */ + UnsignedInt layerCount; /* Number of array elements */ + UnsignedInt faceCount; /* Number of cubemap faces */ + UnsignedInt levelCount; /* Number of mip levels */ + SuperCompressionScheme supercompressionScheme; /* Index */ - UnsignedInt dfdByteOffset; /* Offset of Data Format Descriptor */ - UnsignedInt dfdByteLength; /* Length of Data Format Descriptor */ - UnsignedInt kvdByteOffset; /* Offset of Key/Value Data */ - UnsignedInt kvdByteLength; /* Length of Key/Value Data */ - UnsignedLong sgdByteOffset; /* Offset of Supercompression Global Data */ - UnsignedLong sgdByteLength; /* Length of Supercompression Global Data */ + UnsignedInt dfdByteOffset; /* Offset of Data Format Descriptor */ + UnsignedInt dfdByteLength; /* Length of Data Format Descriptor */ + UnsignedInt kvdByteOffset; /* Offset of Key/Value Data */ + UnsignedInt kvdByteLength; /* Length of Key/Value Data */ + UnsignedLong sgdByteOffset; /* Offset of Supercompression Global Data */ + UnsignedLong sgdByteLength; /* Length of Supercompression Global Data */ }; static_assert(sizeof(KtxHeader) == 80, "Improper size of KtxHeader struct"); @@ -325,6 +346,113 @@ constexpr std::size_t KtxFileVersionOffset = 5; constexpr std::size_t KtxFileVersionLength = 2; static_assert(KtxFileVersionOffset + KtxFileVersionLength <= sizeof(KtxFileIdentifier), "KtxFileVersion(Offset|Length) out of bounds"); +/* Khronos Data Format: basic block header */ +struct KdfBasicBlockHeader { + enum class VendorId : UnsignedShort { + Khronos = 0 + }; + + enum class DescriptorType : UnsignedShort { + Basic = 0 + }; + + enum class VersionNumber : UnsignedShort { + Kdf1_3 = 2 + }; + + enum class ColorModel : UnsignedByte { + /* Uncompressed formats. There are a lot more, but KTX doesn't allow + those. */ + Rgbsda = 1, /* Additive colors: red, green, blue, stencil, depth, alpha */ + + /* Compressed formats, each one has its own color model */ + Bc1 = 128, /* DXT1 */ + Bc2 = 129, /* DXT2/3 */ + Bc3 = 130, /* DXT4/5 */ + Bc4 = 131, + Bc5 = 132, + Bc6h = 133, + Bc7 = 134, + Etc1 = 160, + Etc2 = 161, + Astc = 162, + Etc1s = 163, + Pvrtc = 164, + Pvrtc2 = 165, + + /* Basis Universal */ + BasisUastc = 166, + BasisEtc1s = Etc1s + }; + + enum class ColorPrimaries : UnsignedByte { + /* We have no way to guess color space, this is the recommended default */ + Srgb = 1 /* BT.709 */ + }; + + enum class TransferFunction : UnsignedByte { + /* There are a lot more, but KTX doesn't allow those */ + Linear = 1, + Srgb = 2 + }; + + enum Flags : UnsignedByte { + AlphaPremultiplied = 1 + }; + + /* Technically, the first two members are 17 and 15 bits, but bit fields + aren't very portable. We only check for values 0/0 so this works for our + use case. */ + VendorId vendorId; + DescriptorType descriptorType; + VersionNumber versionNumber; + UnsignedShort descriptorBlockSize; + + ColorModel colorModel; + ColorPrimaries colorPrimaries; + TransferFunction transferFunction; + Flags flags; + UnsignedByte texelBlockDimension[4]; + UnsignedByte bytesPlane[8]; +}; + +static_assert(sizeof(KdfBasicBlockHeader) == 24, "Improper size of KdfBasicBlockHeader struct"); + +/* Khronos Data Format: Basic block sample element, one for each color channel */ +struct KdfBasicBlockSample { + /* Channel id encoded in lower half of channelType */ + enum ChannelId : UnsignedByte { + /* ColorModel::Rgbsda */ + Red = 0, + Green = 1, + Blue = 2, + Stencil = 13, + Depth = 14, + Alpha = 15, + /* Compressed color models. Some use Red/Green/Alpha from Rgbsda if + applicable. */ + Data = 0, + AlphaPresent = 0 /* DXT1 */ + }; + + /* Channel data type bit mask encoded in upper half of channelType */ + enum ChannelFormat : UnsignedByte { + Linear = 1 << (4 + 0), /* Ignore the transfer function */ + Exponent = 1 << (4 + 1), + Signed = 1 << (4 + 2), + Float = 1 << (4 + 3) + }; + + UnsignedShort bitOffset; + UnsignedByte bitLength; /* Length - 1 */ + UnsignedByte channelType; + UnsignedByte position[4]; + UnsignedInt lower; + UnsignedInt upper; +}; + +static_assert(sizeof(KdfBasicBlockSample) == 16, "Improper size of KdfBasicBlockSample struct"); + }}} #endif diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 6ef7333ac..6e6f43cd1 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -26,18 +26,19 @@ #include "KtxImporter.h" -#include #include #include #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -163,13 +164,13 @@ bool validateHeader(const Implementation::KtxHeader& header, std::size_t fileSiz return false; } - if(header.pixelSize.x() == 0) { + if(header.imageSize.x() == 0) { Error{} << prefix << "invalid image size, width is 0"; return false; } - if(header.pixelSize.y() == 0 && header.pixelSize.z() > 0) { - Error{} << prefix << "invalid image size, depth is" << header.pixelSize.z() << "but height is 0"; + if(header.imageSize.y() == 0 && header.imageSize.z() > 0) { + Error{} << prefix << "invalid image size, depth is" << header.imageSize.z() << "but height is 0"; return false; } @@ -179,13 +180,13 @@ bool validateHeader(const Implementation::KtxHeader& header, std::size_t fileSiz return false; } - if(header.pixelSize.z() > 0 || header.pixelSize.x() != header.pixelSize.y()) { - Error{} << prefix << "invalid cubemap dimensions, must be 2D and square, but got" << header.pixelSize; + if(header.imageSize.z() > 0 || header.imageSize.x() != header.imageSize.y()) { + Error{} << prefix << "invalid cubemap dimensions, must be 2D and square, but got" << header.imageSize; return false; } } - const UnsignedInt maxLevelCount = Math::log2(header.pixelSize.max()) + 1; + const UnsignedInt maxLevelCount = Math::log2(header.imageSize.max()) + 1; if(header.levelCount > maxLevelCount) { Error{} << prefix << "too many mipmap levels, expected at most" << maxLevelCount << "but got" << header.levelCount; @@ -194,7 +195,7 @@ bool validateHeader(const Implementation::KtxHeader& header, std::size_t fileSiz const std::size_t levelIndexEnd = sizeof(header) + Math::max(header.levelCount, 1u)*sizeof(Implementation::KtxLevel); if(fileSize < levelIndexEnd) { - Error{} << prefix << "level index too short, expected at least" << + Error{} << prefix << "level index out of bounds, expected at least" << levelIndexEnd << "bytes but got" << fileSize; return false; } @@ -206,6 +207,13 @@ bool validateHeader(const Implementation::KtxHeader& header, std::size_t fileSiz return false; } + const std::size_t dfdMinSize = sizeof(Implementation::KdfBasicBlockHeader) + sizeof(Implementation::KdfBasicBlockSample); + if(dfdMinSize > header.dfdByteLength) { + Error{} << prefix << "data format descriptor too short, expected at least" << + dfdMinSize << "bytes but got" << header.dfdByteLength; + return false; + } + const std::size_t kvdEnd = header.kvdByteOffset + header.kvdByteLength; if(fileSize < kvdEnd) { Error{} << prefix << "key/value data out of bounds, expected at least" << @@ -221,7 +229,9 @@ bool validateLevel(const Implementation::KtxHeader& header, std::size_t fileSize /* Both lengths should be equal without supercompression. Be lenient here and only emit a warning in case some shitty exporter gets this wrong. */ - if(header.supercompressionScheme == 0 && level.byteLength != level.uncompressedByteLength) { + if(header.supercompressionScheme == Implementation::SuperCompressionScheme::None && + level.byteLength != level.uncompressedByteLength) + { Warning{} << prefix << "mismatching image data sizes, both compressed " "and uncompressed should be equal but got" << level.byteLength << "and" << level.uncompressedByteLength; @@ -272,7 +282,12 @@ struct KtxImporter::File { bool isCompressed; bool isDepth; + + /* Size of entire pixel/block */ + UnsignedInt size; + /* Size of underlying data type, 1 for block-compressed formats */ UnsignedInt typeSize; + SwizzleType swizzle; } pixelFormat; @@ -281,14 +296,10 @@ struct KtxImporter::File { }; bool KtxImporter::File::Format::decode(Implementation::VkFormat vkFormat) { - isCompressed = false; - isDepth = false; - swizzle = SwizzleType::None; - /* Find uncompressed pixel format */ PixelFormat format{}; switch(vkFormat) { - #define _p(vulkan, magnum) case Implementation::VK_FORMAT_ ## vulkan: format = PixelFormat:: ## magnum; break; + #define _p(vulkan, magnum, _type) case Implementation::VK_FORMAT_ ## vulkan: format = PixelFormat::magnum; break; #include "MagnumPlugins/KtxImporter/formatMapping.hpp" #undef _p default: @@ -314,11 +325,12 @@ bool KtxImporter::File::Format::decode(Implementation::VkFormat vkFormat) { } if(format != PixelFormat{}) { - const UnsignedInt size = pixelSize(format); + size = pixelSize(format); CORRADE_INTERNAL_ASSERT(size == 3 || size == 4); swizzle = (size == 3) ? SwizzleType::BGR : SwizzleType::BGRA; } - } + } else + size = pixelSize(format); if(format != PixelFormat{}) { /* Depth formats are allowed by KTX. We only really use isDepth for @@ -344,7 +356,7 @@ bool KtxImporter::File::Format::decode(Implementation::VkFormat vkFormat) { /* Find block-compressed pixel format, no swizzling possible */ CompressedPixelFormat compressedFormat{}; switch(vkFormat) { - #define _c(vulkan, magnum) case Implementation::VK_FORMAT_ ## vulkan: compressedFormat = CompressedPixelFormat:: ## magnum; break; + #define _c(vulkan, magnum, _type) case Implementation::VK_FORMAT_ ## vulkan: compressedFormat = CompressedPixelFormat::magnum; break; #include "MagnumPlugins/KtxImporter/formatMapping.hpp" #undef _c default: @@ -352,6 +364,7 @@ bool KtxImporter::File::Format::decode(Implementation::VkFormat vkFormat) { } if(compressedFormat != CompressedPixelFormat{}) { + size = compressedBlockDataSize(compressedFormat); compressed = compressedFormat; isCompressed = true; return true; @@ -397,7 +410,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { /* KTX2 uses little-endian everywhere */ Utility::Endianness::littleEndianInPlace( header.vkFormat, header.typeSize, - header.pixelSize[0], header.pixelSize[1], header.pixelSize[2], + header.imageSize[0], header.imageSize[1], header.imageSize[2], header.layerCount, header.faceCount, header.levelCount, header.supercompressionScheme, header.dfdByteOffset, header.dfdByteLength, @@ -408,7 +421,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { if(!validateHeader(header, data.size(), "Trade::KtxImporter::openData():")) return; - Containers::Pointer f{new File}; + Containers::Pointer f{new File{}}; f->in = Containers::Array{NoInit, data.size()}; Utility::copy(data, f->in); @@ -434,12 +447,12 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { f->pixelFormat.typeSize = header.typeSize; /** @todo Support supercompression */ - if(header.supercompressionScheme != 0) { + if(header.supercompressionScheme != Implementation::SuperCompressionScheme::None) { Error{} << "Trade::KtxImporter::openData(): supercompression is currently not supported"; return; } - f->numDimensions = Math::min(header.pixelSize, Vector3ui{1}).sum(); + f->numDimensions = Math::min(header.imageSize, Vector3ui{1}).sum(); CORRADE_INTERNAL_ASSERT(f->numDimensions >= 1 && f->numDimensions <= 3); if(f->numDimensions == 3 && f->pixelFormat.isDepth) { @@ -448,7 +461,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } /* Make sure we don't choke on size calculations using product() */ - const Vector3i size = Math::max(Vector3i{header.pixelSize}, Vector3i{1}); + const Vector3i size = Math::max(Vector3i{header.imageSize}, Vector3i{1}); /* Number of array layers, imported as extra image dimensions (except for 3D images, there it's one Image3D per layer). @@ -512,7 +525,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { /* Remember the image type for doImage() */ switch(f->numDimensions) { - /** @todo array enums */ + /** @todo Use array enums once they're added to Magnum */ case 1: f->type = isLayered ? TextureData::Type::Texture1D/*Array*/ : TextureData::Type::Texture1D; break; @@ -528,15 +541,60 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { default: CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } - f->numDataDimensions = f->numDimensions; - if(f->numDimensions != 3 && (isLayered || isCubemap)) - f->numDataDimensions++; + f->numDataDimensions = Math::min(f->numDimensions + UnsignedByte(isLayered || isCubemap), 3); /* Read metadata */ - /** @todo Read data format descriptor */ - if(header.dfdByteLength > 0) { - Containers::ArrayView dataFormatDescriptorData = f->in.suffix(header.dfdByteOffset).prefix(header.dfdByteLength); + /* Read data format descriptor (DFD) */ + { + /* Only do some very basic sanity checks, the DFD is terribly + over-engineered and the data is redundant if we have a + (Compressed)PixelFormat. */ + bool valid = false; + const auto descriptorData = f->in.suffix(header.dfdByteOffset).prefix(header.dfdByteLength); + const UnsignedInt length = *reinterpret_cast(descriptorData.data()); + if(length == descriptorData.size()) { + const auto& block = *reinterpret_cast(descriptorData.suffix(sizeof(length)).data()); + Utility::Endianness::littleEndianInPlace(block.vendorId, + block.descriptorType, block.versionNumber, + block.descriptorBlockSize); + + /* Basic block must be the first block in the DFD */ + if(block.vendorId == Implementation::KdfBasicBlockHeader::VendorId::Khronos && + block.descriptorType == Implementation::KdfBasicBlockHeader::DescriptorType::Basic && + block.versionNumber == Implementation::KdfBasicBlockHeader::VersionNumber::Kdf1_3 && + block.descriptorBlockSize > sizeof(block) && + block.descriptorBlockSize + sizeof(length) <= length) + { + valid = true; + + /* Check if pixel/block size and channel count match the format */ + if(f->pixelFormat.isCompressed) { + /* Block size */ + const Vector4i expected = Vector4i::pad(compressedBlockSize(f->pixelFormat.compressed), 1); + const Vector4i actual{Vector4ub::from(block.texelBlockDimension)}; + valid = valid && actual == expected; + } else { + /* Pixel size. For supercompressed data, bytePlanes is all + zeros to indicate an unsized format. */ + /** @todo Does this work with depth-stencil formats? */ + if(header.supercompressionScheme == Implementation::SuperCompressionScheme::None) { + const UnsignedInt expected = f->pixelFormat.size; + const UnsignedInt actual = block.bytesPlane[0]; + valid = valid && actual == expected; + } + /* Channel count */ + const UnsignedInt expected = f->pixelFormat.size / f->pixelFormat.typeSize; + const UnsignedInt actual = (block.descriptorBlockSize - sizeof(block))/sizeof(Implementation::KdfBasicBlockSample); + valid = valid && actual == expected; + } + } + } + + if(!valid) { + Error{} << "Trade::KtxImporter::openData(): invalid data format descriptor"; + return; + } } /* Read key/value data, optional */ @@ -640,12 +698,13 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { "cubemap faces, importing faces as array layers"; } + /* Read swizzle information */ { - /** @todo We need the channel count to correctly validate swizzle length. Read the DFD! */ const auto found = keyValueMap.find("KTXswizzle"_s); if(found != keyValueMap.end()) { - const Containers::StringView swizzle{found->second.prefix(Math::min(f->numDimensions, found->second.size()))}; - if(swizzle != "rgba"_s.prefix(f->numDimensions)) { + const UnsignedInt numChannels = f->pixelFormat.size / f->pixelFormat.typeSize; + const Containers::StringView swizzle{found->second.prefix(Math::min(numChannels, found->second.size()))}; + if(swizzle != "rgba"_s.prefix(numChannels)) { bool handled = false; /* Special cases already supported for 8-bit Vulkan formats */ if(!f->pixelFormat.isCompressed) { @@ -666,7 +725,6 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } } - /** @todo More verbose output */ if(flags() & ImporterFlag::Verbose) { switch(f->pixelFormat.swizzle) { case SwizzleType::BGR: @@ -713,7 +771,7 @@ Containers::Optional KtxImporter::doImage1D(UnsignedInt id, Unsigne /* Adjust pixel storage if row size is not four byte aligned */ PixelStorage storage; - if((levelData.size.x()*pixelSize(_f->pixelFormat.uncompressed))%4 != 0) + if((levelData.size.x()*_f->pixelFormat.size)%4 != 0) storage.setAlignment(1); return ImageData1D{storage, _f->pixelFormat.uncompressed, levelData.size.x(), std::move(data)}; @@ -726,26 +784,17 @@ UnsignedInt KtxImporter::doImage2DLevelCount(UnsignedInt id) { return _f->imageD Containers::Optional KtxImporter::doImage2D(UnsignedInt id, UnsignedInt level) { const File::LevelData& levelData = _f->imageData[id][level]; - /* Copy image data */ Containers::Array data{NoInit, levelData.data.size()}; - /** @todo Flip axes if necessary */ Utility::copy(levelData.data, data); - - /* Endian-swap if necessary */ endianSwap(data, _f->pixelFormat.typeSize); - /* Compressed image */ if(_f->pixelFormat.isCompressed) return ImageData2D(_f->pixelFormat.compressed, levelData.size.xy(), std::move(data)); - /* Uncompressed */ - - /* Swizzle BGR(A) if necessary */ swizzlePixels(_f->pixelFormat.swizzle, _f->pixelFormat.typeSize, data); - /* Adjust pixel storage if row size is not four byte aligned */ PixelStorage storage; - if((levelData.size.x()*pixelSize(_f->pixelFormat.uncompressed))%4 != 0) + if((levelData.size.x()*_f->pixelFormat.size)%4 != 0) storage.setAlignment(1); return ImageData2D{storage, _f->pixelFormat.uncompressed, levelData.size.xy(), std::move(data)}; @@ -758,26 +807,17 @@ UnsignedInt KtxImporter::doImage3DLevelCount(UnsignedInt id) { return _f->imageD Containers::Optional KtxImporter::doImage3D(UnsignedInt id, const UnsignedInt level) { const File::LevelData& levelData = _f->imageData[id][level]; - /* Copy image data */ Containers::Array data{NoInit, levelData.data.size()}; - /** @todo Flip axes if necessary */ Utility::copy(levelData.data, data); - - /* Endian-swap if necessary */ endianSwap(data, _f->pixelFormat.typeSize); - /* Compressed image */ if(_f->pixelFormat.isCompressed) return ImageData3D(_f->pixelFormat.compressed, levelData.size, std::move(data)); - /* Uncompressed */ - - /* Swizzle BGR(A) if necessary */ swizzlePixels(_f->pixelFormat.swizzle, _f->pixelFormat.typeSize, data); - /* Adjust pixel storage if row size is not four byte aligned */ PixelStorage storage; - if((levelData.size.x()*pixelSize(_f->pixelFormat.uncompressed))%4 != 0) + if((levelData.size.x()*_f->pixelFormat.size)%4 != 0) storage.setAlignment(1); return ImageData3D{storage, _f->pixelFormat.uncompressed, levelData.size, std::move(data)}; diff --git a/src/MagnumPlugins/KtxImporter/formatMapping.hpp b/src/MagnumPlugins/KtxImporter/formatMapping.hpp index 94d4bb464..d341c7603 100644 --- a/src/MagnumPlugins/KtxImporter/formatMapping.hpp +++ b/src/MagnumPlugins/KtxImporter/formatMapping.hpp @@ -27,137 +27,137 @@ /* Autogenerated from formatMapping.py! Do not edit! */ #ifdef _p /* PixelFormat */ -_p(R8_UNORM, R8Unorm) -_p(R8G8_UNORM, RG8Unorm) -_p(R8G8B8_UNORM, RGB8Unorm) -_p(R8G8B8A8_UNORM, RGBA8Unorm) -_p(R8_SNORM, R8Snorm) -_p(R8G8_SNORM, RG8Snorm) -_p(R8G8B8_SNORM, RGB8Snorm) -_p(R8G8B8A8_SNORM, RGBA8Snorm) -_p(R8_SRGB, R8Srgb) -_p(R8G8_SRGB, RG8Srgb) -_p(R8G8B8_SRGB, RGB8Srgb) -_p(R8G8B8A8_SRGB, RGBA8Srgb) -_p(R8_UINT, R8UI) -_p(R8G8_UINT, RG8UI) -_p(R8G8B8_UINT, RGB8UI) -_p(R8G8B8A8_UINT, RGBA8UI) -_p(R8_SINT, R8I) -_p(R8G8_SINT, RG8I) -_p(R8G8B8_SINT, RGB8I) -_p(R8G8B8A8_SINT, RGBA8I) -_p(R16_UNORM, R16Unorm) -_p(R16G16_UNORM, RG16Unorm) -_p(R16G16B16_UNORM, RGB16Unorm) -_p(R16G16B16A16_UNORM, RGBA16Unorm) -_p(R16_SNORM, R16Snorm) -_p(R16G16_SNORM, RG16Snorm) -_p(R16G16B16_SNORM, RGB16Snorm) -_p(R16G16B16A16_SNORM, RGBA16Snorm) -_p(R16_UINT, R16UI) -_p(R16G16_UINT, RG16UI) -_p(R16G16B16_UINT, RGB16UI) -_p(R16G16B16A16_UINT, RGBA16UI) -_p(R16_SINT, R16I) -_p(R16G16_SINT, RG16I) -_p(R16G16B16_SINT, RGB16I) -_p(R16G16B16A16_SINT, RGBA16I) -_p(R32_UINT, R32UI) -_p(R32G32_UINT, RG32UI) -_p(R32G32B32_UINT, RGB32UI) -_p(R32G32B32A32_UINT, RGBA32UI) -_p(R32_SINT, R32I) -_p(R32G32_SINT, RG32I) -_p(R32G32B32_SINT, RGB32I) -_p(R32G32B32A32_SINT, RGBA32I) -_p(R16_SFLOAT, R16F) -_p(R16G16_SFLOAT, RG16F) -_p(R16G16B16_SFLOAT, RGB16F) -_p(R16G16B16A16_SFLOAT, RGBA16F) -_p(R32_SFLOAT, R32F) -_p(R32G32_SFLOAT, RG32F) -_p(R32G32B32_SFLOAT, RGB32F) -_p(R32G32B32A32_SFLOAT, RGBA32F) -_p(D16_UNORM, Depth16Unorm) -_p(X8_D24_UNORM_PACK32, Depth24Unorm) -_p(D32_SFLOAT, Depth32F) -_p(S8_UINT, Stencil8UI) -_p(D16_UNORM_S8_UINT, Depth16UnormStencil8UI) -_p(D24_UNORM_S8_UINT, Depth24UnormStencil8UI) -_p(D32_SFLOAT_S8_UINT, Depth32FStencil8UI) +_p(R8_UNORM, R8Unorm, UNORM) +_p(R8G8_UNORM, RG8Unorm, UNORM) +_p(R8G8B8_UNORM, RGB8Unorm, UNORM) +_p(R8G8B8A8_UNORM, RGBA8Unorm, UNORM) +_p(R8_SNORM, R8Snorm, SNORM) +_p(R8G8_SNORM, RG8Snorm, SNORM) +_p(R8G8B8_SNORM, RGB8Snorm, SNORM) +_p(R8G8B8A8_SNORM, RGBA8Snorm, SNORM) +_p(R8_SRGB, R8Srgb, SRGB) +_p(R8G8_SRGB, RG8Srgb, SRGB) +_p(R8G8B8_SRGB, RGB8Srgb, SRGB) +_p(R8G8B8A8_SRGB, RGBA8Srgb, SRGB) +_p(R8_UINT, R8UI, UINT) +_p(R8G8_UINT, RG8UI, UINT) +_p(R8G8B8_UINT, RGB8UI, UINT) +_p(R8G8B8A8_UINT, RGBA8UI, UINT) +_p(R8_SINT, R8I, SINT) +_p(R8G8_SINT, RG8I, SINT) +_p(R8G8B8_SINT, RGB8I, SINT) +_p(R8G8B8A8_SINT, RGBA8I, SINT) +_p(R16_UNORM, R16Unorm, UNORM) +_p(R16G16_UNORM, RG16Unorm, UNORM) +_p(R16G16B16_UNORM, RGB16Unorm, UNORM) +_p(R16G16B16A16_UNORM, RGBA16Unorm, UNORM) +_p(R16_SNORM, R16Snorm, SNORM) +_p(R16G16_SNORM, RG16Snorm, SNORM) +_p(R16G16B16_SNORM, RGB16Snorm, SNORM) +_p(R16G16B16A16_SNORM, RGBA16Snorm, SNORM) +_p(R16_UINT, R16UI, UINT) +_p(R16G16_UINT, RG16UI, UINT) +_p(R16G16B16_UINT, RGB16UI, UINT) +_p(R16G16B16A16_UINT, RGBA16UI, UINT) +_p(R16_SINT, R16I, SINT) +_p(R16G16_SINT, RG16I, SINT) +_p(R16G16B16_SINT, RGB16I, SINT) +_p(R16G16B16A16_SINT, RGBA16I, SINT) +_p(R32_UINT, R32UI, UINT) +_p(R32G32_UINT, RG32UI, UINT) +_p(R32G32B32_UINT, RGB32UI, UINT) +_p(R32G32B32A32_UINT, RGBA32UI, UINT) +_p(R32_SINT, R32I, SINT) +_p(R32G32_SINT, RG32I, SINT) +_p(R32G32B32_SINT, RGB32I, SINT) +_p(R32G32B32A32_SINT, RGBA32I, SINT) +_p(R16_SFLOAT, R16F, SFLOAT) +_p(R16G16_SFLOAT, RG16F, SFLOAT) +_p(R16G16B16_SFLOAT, RGB16F, SFLOAT) +_p(R16G16B16A16_SFLOAT, RGBA16F, SFLOAT) +_p(R32_SFLOAT, R32F, SFLOAT) +_p(R32G32_SFLOAT, RG32F, SFLOAT) +_p(R32G32B32_SFLOAT, RGB32F, SFLOAT) +_p(R32G32B32A32_SFLOAT, RGBA32F, SFLOAT) +_p(D16_UNORM, Depth16Unorm, UNORM) +_p(X8_D24_UNORM_PACK32, Depth24Unorm, UNORM) +_p(D32_SFLOAT, Depth32F, SFLOAT) +_p(S8_UINT, Stencil8UI, UINT) +_p(D16_UNORM_S8_UINT, Depth16UnormStencil8UI, UINT) +_p(D24_UNORM_S8_UINT, Depth24UnormStencil8UI, UINT) +_p(D32_SFLOAT_S8_UINT, Depth32FStencil8UI, UINT) #endif #ifdef _c /* CompressedPixelFormat */ -_c(BC1_RGB_UNORM_BLOCK, Bc1RGBUnorm) -_c(BC1_RGB_SRGB_BLOCK, Bc1RGBSrgb) -_c(BC1_RGBA_UNORM_BLOCK, Bc1RGBAUnorm) -_c(BC1_RGBA_SRGB_BLOCK, Bc1RGBASrgb) -_c(BC2_UNORM_BLOCK, Bc2RGBAUnorm) -_c(BC2_SRGB_BLOCK, Bc2RGBASrgb) -_c(BC3_UNORM_BLOCK, Bc3RGBAUnorm) -_c(BC3_SRGB_BLOCK, Bc3RGBASrgb) -_c(BC4_UNORM_BLOCK, Bc4RUnorm) -_c(BC4_SNORM_BLOCK, Bc4RSnorm) -_c(BC5_UNORM_BLOCK, Bc5RGUnorm) -_c(BC5_SNORM_BLOCK, Bc5RGSnorm) -_c(BC6H_UFLOAT_BLOCK, Bc6hRGBUfloat) -_c(BC6H_SFLOAT_BLOCK, Bc6hRGBSfloat) -_c(BC7_UNORM_BLOCK, Bc7RGBAUnorm) -_c(BC7_SRGB_BLOCK, Bc7RGBASrgb) -_c(EAC_R11_UNORM_BLOCK, EacR11Unorm) -_c(EAC_R11_SNORM_BLOCK, EacR11Snorm) -_c(EAC_R11G11_UNORM_BLOCK, EacRG11Unorm) -_c(EAC_R11G11_SNORM_BLOCK, EacRG11Snorm) -_c(ETC2_R8G8B8_UNORM_BLOCK, Etc2RGB8Unorm) -_c(ETC2_R8G8B8_SRGB_BLOCK, Etc2RGB8Srgb) -_c(ETC2_R8G8B8A1_UNORM_BLOCK, Etc2RGB8A1Unorm) -_c(ETC2_R8G8B8A1_SRGB_BLOCK, Etc2RGB8A1Srgb) -_c(ETC2_R8G8B8A8_UNORM_BLOCK, Etc2RGBA8Unorm) -_c(ETC2_R8G8B8A8_SRGB_BLOCK, Etc2RGBA8Srgb) -_c(ASTC_4x4_UNORM_BLOCK, Astc4x4RGBAUnorm) -_c(ASTC_4x4_SRGB_BLOCK, Astc4x4RGBASrgb) -_c(ASTC_4x4_SFLOAT_BLOCK_EXT, Astc4x4RGBAF) -_c(ASTC_5x4_UNORM_BLOCK, Astc5x4RGBAUnorm) -_c(ASTC_5x4_SRGB_BLOCK, Astc5x4RGBASrgb) -_c(ASTC_5x4_SFLOAT_BLOCK_EXT, Astc5x4RGBAF) -_c(ASTC_5x5_UNORM_BLOCK, Astc5x5RGBAUnorm) -_c(ASTC_5x5_SRGB_BLOCK, Astc5x5RGBASrgb) -_c(ASTC_5x5_SFLOAT_BLOCK_EXT, Astc5x5RGBAF) -_c(ASTC_6x5_UNORM_BLOCK, Astc6x5RGBAUnorm) -_c(ASTC_6x5_SRGB_BLOCK, Astc6x5RGBASrgb) -_c(ASTC_6x5_SFLOAT_BLOCK_EXT, Astc6x5RGBAF) -_c(ASTC_6x6_UNORM_BLOCK, Astc6x6RGBAUnorm) -_c(ASTC_6x6_SRGB_BLOCK, Astc6x6RGBASrgb) -_c(ASTC_6x6_SFLOAT_BLOCK_EXT, Astc6x6RGBAF) -_c(ASTC_8x5_UNORM_BLOCK, Astc8x5RGBAUnorm) -_c(ASTC_8x5_SRGB_BLOCK, Astc8x5RGBASrgb) -_c(ASTC_8x5_SFLOAT_BLOCK_EXT, Astc8x5RGBAF) -_c(ASTC_8x6_UNORM_BLOCK, Astc8x6RGBAUnorm) -_c(ASTC_8x6_SRGB_BLOCK, Astc8x6RGBASrgb) -_c(ASTC_8x6_SFLOAT_BLOCK_EXT, Astc8x6RGBAF) -_c(ASTC_8x8_UNORM_BLOCK, Astc8x8RGBAUnorm) -_c(ASTC_8x8_SRGB_BLOCK, Astc8x8RGBASrgb) -_c(ASTC_8x8_SFLOAT_BLOCK_EXT, Astc8x8RGBAF) -_c(ASTC_10x5_UNORM_BLOCK, Astc10x5RGBAUnorm) -_c(ASTC_10x5_SRGB_BLOCK, Astc10x5RGBASrgb) -_c(ASTC_10x5_SFLOAT_BLOCK_EXT, Astc10x5RGBAF) -_c(ASTC_10x6_UNORM_BLOCK, Astc10x6RGBAUnorm) -_c(ASTC_10x6_SRGB_BLOCK, Astc10x6RGBASrgb) -_c(ASTC_10x6_SFLOAT_BLOCK_EXT, Astc10x6RGBAF) -_c(ASTC_10x8_UNORM_BLOCK, Astc10x8RGBAUnorm) -_c(ASTC_10x8_SRGB_BLOCK, Astc10x8RGBASrgb) -_c(ASTC_10x8_SFLOAT_BLOCK_EXT, Astc10x8RGBAF) -_c(ASTC_10x10_UNORM_BLOCK, Astc10x10RGBAUnorm) -_c(ASTC_10x10_SRGB_BLOCK, Astc10x10RGBASrgb) -_c(ASTC_10x10_SFLOAT_BLOCK_EXT, Astc10x10RGBAF) -_c(ASTC_12x10_UNORM_BLOCK, Astc12x10RGBAUnorm) -_c(ASTC_12x10_SRGB_BLOCK, Astc12x10RGBASrgb) -_c(ASTC_12x10_SFLOAT_BLOCK_EXT, Astc12x10RGBAF) -_c(ASTC_12x12_UNORM_BLOCK, Astc12x12RGBAUnorm) -_c(ASTC_12x12_SRGB_BLOCK, Astc12x12RGBASrgb) -_c(ASTC_12x12_SFLOAT_BLOCK_EXT, Astc12x12RGBAF) -_c(PVRTC1_2BPP_UNORM_BLOCK_IMG, PvrtcRGBA2bppUnorm) -_c(PVRTC1_2BPP_SRGB_BLOCK_IMG, PvrtcRGBA2bppSrgb) -_c(PVRTC1_4BPP_UNORM_BLOCK_IMG, PvrtcRGBA4bppUnorm) -_c(PVRTC1_4BPP_SRGB_BLOCK_IMG, PvrtcRGBA4bppSrgb) +_c(BC1_RGB_UNORM_BLOCK, Bc1RGBUnorm, UNORM) +_c(BC1_RGB_SRGB_BLOCK, Bc1RGBSrgb, SRGB) +_c(BC1_RGBA_UNORM_BLOCK, Bc1RGBAUnorm, UNORM) +_c(BC1_RGBA_SRGB_BLOCK, Bc1RGBASrgb, SRGB) +_c(BC2_UNORM_BLOCK, Bc2RGBAUnorm, UNORM) +_c(BC2_SRGB_BLOCK, Bc2RGBASrgb, SRGB) +_c(BC3_UNORM_BLOCK, Bc3RGBAUnorm, UNORM) +_c(BC3_SRGB_BLOCK, Bc3RGBASrgb, SRGB) +_c(BC4_UNORM_BLOCK, Bc4RUnorm, UNORM) +_c(BC4_SNORM_BLOCK, Bc4RSnorm, SNORM) +_c(BC5_UNORM_BLOCK, Bc5RGUnorm, UNORM) +_c(BC5_SNORM_BLOCK, Bc5RGSnorm, SNORM) +_c(BC6H_UFLOAT_BLOCK, Bc6hRGBUfloat, UFLOAT) +_c(BC6H_SFLOAT_BLOCK, Bc6hRGBSfloat, SFLOAT) +_c(BC7_UNORM_BLOCK, Bc7RGBAUnorm, UNORM) +_c(BC7_SRGB_BLOCK, Bc7RGBASrgb, SRGB) +_c(EAC_R11_UNORM_BLOCK, EacR11Unorm, UNORM) +_c(EAC_R11_SNORM_BLOCK, EacR11Snorm, SNORM) +_c(EAC_R11G11_UNORM_BLOCK, EacRG11Unorm, UNORM) +_c(EAC_R11G11_SNORM_BLOCK, EacRG11Snorm, SNORM) +_c(ETC2_R8G8B8_UNORM_BLOCK, Etc2RGB8Unorm, UNORM) +_c(ETC2_R8G8B8_SRGB_BLOCK, Etc2RGB8Srgb, SRGB) +_c(ETC2_R8G8B8A1_UNORM_BLOCK, Etc2RGB8A1Unorm, UNORM) +_c(ETC2_R8G8B8A1_SRGB_BLOCK, Etc2RGB8A1Srgb, SRGB) +_c(ETC2_R8G8B8A8_UNORM_BLOCK, Etc2RGBA8Unorm, UNORM) +_c(ETC2_R8G8B8A8_SRGB_BLOCK, Etc2RGBA8Srgb, SRGB) +_c(ASTC_4x4_UNORM_BLOCK, Astc4x4RGBAUnorm, UNORM) +_c(ASTC_4x4_SRGB_BLOCK, Astc4x4RGBASrgb, SRGB) +_c(ASTC_4x4_SFLOAT_BLOCK_EXT, Astc4x4RGBAF, SFLOAT) +_c(ASTC_5x4_UNORM_BLOCK, Astc5x4RGBAUnorm, UNORM) +_c(ASTC_5x4_SRGB_BLOCK, Astc5x4RGBASrgb, SRGB) +_c(ASTC_5x4_SFLOAT_BLOCK_EXT, Astc5x4RGBAF, SFLOAT) +_c(ASTC_5x5_UNORM_BLOCK, Astc5x5RGBAUnorm, UNORM) +_c(ASTC_5x5_SRGB_BLOCK, Astc5x5RGBASrgb, SRGB) +_c(ASTC_5x5_SFLOAT_BLOCK_EXT, Astc5x5RGBAF, SFLOAT) +_c(ASTC_6x5_UNORM_BLOCK, Astc6x5RGBAUnorm, UNORM) +_c(ASTC_6x5_SRGB_BLOCK, Astc6x5RGBASrgb, SRGB) +_c(ASTC_6x5_SFLOAT_BLOCK_EXT, Astc6x5RGBAF, SFLOAT) +_c(ASTC_6x6_UNORM_BLOCK, Astc6x6RGBAUnorm, UNORM) +_c(ASTC_6x6_SRGB_BLOCK, Astc6x6RGBASrgb, SRGB) +_c(ASTC_6x6_SFLOAT_BLOCK_EXT, Astc6x6RGBAF, SFLOAT) +_c(ASTC_8x5_UNORM_BLOCK, Astc8x5RGBAUnorm, UNORM) +_c(ASTC_8x5_SRGB_BLOCK, Astc8x5RGBASrgb, SRGB) +_c(ASTC_8x5_SFLOAT_BLOCK_EXT, Astc8x5RGBAF, SFLOAT) +_c(ASTC_8x6_UNORM_BLOCK, Astc8x6RGBAUnorm, UNORM) +_c(ASTC_8x6_SRGB_BLOCK, Astc8x6RGBASrgb, SRGB) +_c(ASTC_8x6_SFLOAT_BLOCK_EXT, Astc8x6RGBAF, SFLOAT) +_c(ASTC_8x8_UNORM_BLOCK, Astc8x8RGBAUnorm, UNORM) +_c(ASTC_8x8_SRGB_BLOCK, Astc8x8RGBASrgb, SRGB) +_c(ASTC_8x8_SFLOAT_BLOCK_EXT, Astc8x8RGBAF, SFLOAT) +_c(ASTC_10x5_UNORM_BLOCK, Astc10x5RGBAUnorm, UNORM) +_c(ASTC_10x5_SRGB_BLOCK, Astc10x5RGBASrgb, SRGB) +_c(ASTC_10x5_SFLOAT_BLOCK_EXT, Astc10x5RGBAF, SFLOAT) +_c(ASTC_10x6_UNORM_BLOCK, Astc10x6RGBAUnorm, UNORM) +_c(ASTC_10x6_SRGB_BLOCK, Astc10x6RGBASrgb, SRGB) +_c(ASTC_10x6_SFLOAT_BLOCK_EXT, Astc10x6RGBAF, SFLOAT) +_c(ASTC_10x8_UNORM_BLOCK, Astc10x8RGBAUnorm, UNORM) +_c(ASTC_10x8_SRGB_BLOCK, Astc10x8RGBASrgb, SRGB) +_c(ASTC_10x8_SFLOAT_BLOCK_EXT, Astc10x8RGBAF, SFLOAT) +_c(ASTC_10x10_UNORM_BLOCK, Astc10x10RGBAUnorm, UNORM) +_c(ASTC_10x10_SRGB_BLOCK, Astc10x10RGBASrgb, SRGB) +_c(ASTC_10x10_SFLOAT_BLOCK_EXT, Astc10x10RGBAF, SFLOAT) +_c(ASTC_12x10_UNORM_BLOCK, Astc12x10RGBAUnorm, UNORM) +_c(ASTC_12x10_SRGB_BLOCK, Astc12x10RGBASrgb, SRGB) +_c(ASTC_12x10_SFLOAT_BLOCK_EXT, Astc12x10RGBAF, SFLOAT) +_c(ASTC_12x12_UNORM_BLOCK, Astc12x12RGBAUnorm, UNORM) +_c(ASTC_12x12_SRGB_BLOCK, Astc12x12RGBASrgb, SRGB) +_c(ASTC_12x12_SFLOAT_BLOCK_EXT, Astc12x12RGBAF, SFLOAT) +_c(PVRTC1_2BPP_UNORM_BLOCK_IMG, PvrtcRGBA2bppUnorm, UNORM) +_c(PVRTC1_2BPP_SRGB_BLOCK_IMG, PvrtcRGBA2bppSrgb, SRGB) +_c(PVRTC1_4BPP_UNORM_BLOCK_IMG, PvrtcRGBA4bppUnorm, UNORM) +_c(PVRTC1_4BPP_SRGB_BLOCK_IMG, PvrtcRGBA4bppSrgb, SRGB) #endif diff --git a/src/MagnumPlugins/KtxImporter/formatMapping.py b/src/MagnumPlugins/KtxImporter/formatMapping.py index 204c69398..80c884434 100644 --- a/src/MagnumPlugins/KtxImporter/formatMapping.py +++ b/src/MagnumPlugins/KtxImporter/formatMapping.py @@ -42,7 +42,7 @@ print('Writing to', file_out) -Format = namedtuple('Format', 'compressed magnum vulkan') +Format = namedtuple('Format', 'compressed magnum vulkan type') formats = [] file_in = os.path.join(magnum_dir, 'src/Magnum/Vk/PixelFormat.h') @@ -54,7 +54,13 @@ # PixelFormat and Vk::PixelFormat names are identical match = re.search('^\s+(Compressed)?(\w+) = VK_FORMAT_(\w+),?$', line) if match: - formats.append(Format(match.group(1) != None, match.group(2), match.group(3))) + compressed = match.group(1) != None + magnum = match.group(2) + vulkan = match.group(3) + type = re.search('\w+_([U|S](NORM|INT|FLOAT|RGB))\w*', vulkan) + assert type != None + assert type.group(1) != 'URGB' + formats.append(Format(compressed, magnum, vulkan, type.group(1))) if len(formats) != 135: print('Unexpected number of formats') @@ -105,10 +111,10 @@ def partition(pred, iterable): outfile.write('#ifdef _p /* PixelFormat */\n') for format in formats: - outfile.write('_p(' + format.vulkan + ', ' + format.magnum + ')\n') + outfile.write('_p(' + format.vulkan + ', ' + format.magnum + ', ' + format.type + ')\n') outfile.write('#endif\n') outfile.write('#ifdef _c /* CompressedPixelFormat */\n') for format in compressed_formats: - outfile.write('_c(' + format.vulkan + ', ' + format.magnum + ')\n') + outfile.write('_c(' + format.vulkan + ', ' + format.magnum + ', ' + format.type + ')\n') outfile.write('#endif\n') From b3f906a23073ef8e77e1775091040476a6d68c19 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 23 Jul 2021 00:49:16 +0200 Subject: [PATCH 15/95] KtxImporter: flip axes --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 92 +++++++++++++++---- 1 file changed, 73 insertions(+), 19 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 6e6f43cd1..6e3400869 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -133,6 +133,46 @@ void swizzlePixels(SwizzleType type, UnsignedInt typeSize, Containers::ArrayView CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ break; } + +template void copyPixels(Math::Vector size, BoolVector3 flip, UnsignedInt texelSize, Containers::ArrayView src, Containers::ArrayView dst) { + static_assert(dimensions >= 1 && dimensions <= 3, ""); + + /* Nothing to flip, just memcpy */ + if(flip.none()) { + Utility::copy(src, dst); + return; + } + + /* Flip selected axes by using StridedArrayView with negative stride. + Ideally we'd just call flipped() on the view but we can't conditionally + call it with a dimension larger than the actual view dimensions. We'd + need a template specialization (more code) or constexpr if (requires + C++ 17), so we manually calculate negative strides and adjust the data + pointer. */ + std::size_t sizes[dimensions + 1]; + std::ptrdiff_t strides[dimensions + 1]; + + sizes[dimensions] = texelSize; + strides[dimensions] = 1; + for(UnsignedInt i = 0; i != dimensions; ++i) { + sizes[dimensions - 1 - i] = size[i]; + strides[dimensions - 1 - i] = strides[dimensions - i]*sizes[dimensions - i]; + } + + const Containers::StridedArrayView dstView{dst, sizes, strides}; + + const char* srcData = src.data(); + for(UnsignedInt i = 0; i != dimensions; ++i) { + if(flip[i]) { + /* Point to the last item of the dimension */ + srcData += strides[dimensions - 1 - i]*(sizes[dimensions - 1 - i] - 1); + strides[dimensions - 1 - i] *= -1; + } + } + + Containers::StridedArrayView srcView{{srcData, src.size()}, sizes, strides}; + + Utility::copy(srcView, dstView); } bool validateHeader(const Implementation::KtxHeader& header, std::size_t fileSize, const char* prefix) { @@ -644,10 +684,9 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { u/d = up/down o/i = out of/into screen - Default is assumed to be rdi, Magnum/GL expects ruo. */ + The spec strongly recommends defaulting to rdi, Magnum/GL expects ruo. */ { constexpr auto targetOrientation = "ruo"_s; - constexpr auto defaultOrientation = "rdi"_s; bool useDefaultOrientation = true; const auto found = keyValueMap.find("KTXorientation"_s); @@ -664,10 +703,17 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } if(useDefaultOrientation) { + constexpr Containers::StringView defaultDirections[3]{ + "right"_s, "down"_s, "forward"_s + }; Warning{} << "Trade::KtxImporter::openData(): missing or invalid " - "orientation, falling back to" << defaultOrientation; - f->flip = Math::Vector3::from(defaultOrientation.data()) != - Math::Vector3::from(targetOrientation.data()); + "orientation, assuming" << ", "_s.join(Containers::arrayView(defaultDirections).prefix(f->numDimensions)); + + constexpr auto defaultOrientation = "rdi"_s; + const BoolVector3 flip = Math::notEqual(Math::Vector3::from(defaultOrientation.data()), + Math::Vector3::from(targetOrientation.data())); + for(UnsignedByte i = 0; i != f->numDimensions; ++i) + f->flip.set(i, flip[i]); } } @@ -699,11 +745,12 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } /* Read swizzle information */ - { + if(!f->pixelFormat.isDepth) { const auto found = keyValueMap.find("KTXswizzle"_s); if(found != keyValueMap.end()) { - const UnsignedInt numChannels = f->pixelFormat.size / f->pixelFormat.typeSize; - const Containers::StringView swizzle{found->second.prefix(Math::min(numChannels, found->second.size()))}; + /** @todo This is broken for block-compressed formats. Get numChannels from DFD */ + const std::size_t numChannels = f->pixelFormat.size / f->pixelFormat.typeSize; + const Containers::StringView swizzle{found->second.prefix(Math::min(numChannels, found->second.size()))}; if(swizzle != "rgba"_s.prefix(numChannels)) { bool handled = false; /* Special cases already supported for 8-bit Vulkan formats */ @@ -726,6 +773,16 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } if(flags() & ImporterFlag::Verbose) { + if(f->flip.any()) { + const Containers::StringView axes[3]{ + f->flip[0] ? "x"_s : ""_s, + f->flip[1] ? "y"_s : ""_s, + f->flip[2] ? "z"_s : ""_s, + }; + Debug{} << "Trade::KtxImporter::openData(): image will be flipped along" << + " and "_s.joinWithoutEmptyParts(axes); + } + switch(f->pixelFormat.swizzle) { case SwizzleType::BGR: Debug{} << "Trade::KtxImporter::openData(): format requires conversion from BGR to RGB"; @@ -750,22 +807,17 @@ UnsignedInt KtxImporter::doImage1DLevelCount(UnsignedInt id) { return _f->imageD Containers::Optional KtxImporter::doImage1D(UnsignedInt id, UnsignedInt level) { const File::LevelData& levelData = _f->imageData[id][level]; - /* Copy image data */ + /* Copy image data. If we don't have to flip any axes, this is just a memcpy. + We already cleared flip for block-compressed data because we can't + reliably flip blocks, so there we always memcpy. */ Containers::Array data{NoInit, levelData.data.size()}; - /** @todo Flip axes if necessary. This should be simple(?) with StridedArrayView - and negative stride. Note that we already disable flipping for - block-compressed data, so no need to check here. */ - Utility::copy(levelData.data, data); + copyPixels<1>(levelData.size.x(), _f->flip, _f->pixelFormat.size, levelData.data, data); - /* Endian-swap if necessary */ endianSwap(data, _f->pixelFormat.typeSize); - /* Compressed image */ if(_f->pixelFormat.isCompressed) return ImageData1D(_f->pixelFormat.compressed, levelData.size.x(), std::move(data)); - /* Uncompressed */ - /* Swizzle BGR(A) if necessary */ swizzlePixels(_f->pixelFormat.swizzle, _f->pixelFormat.typeSize, data); @@ -785,7 +837,8 @@ Containers::Optional KtxImporter::doImage2D(UnsignedInt id, Unsigne const File::LevelData& levelData = _f->imageData[id][level]; Containers::Array data{NoInit, levelData.data.size()}; - Utility::copy(levelData.data, data); + copyPixels<2>(levelData.size.xy(), _f->flip, _f->pixelFormat.size, levelData.data, data); + endianSwap(data, _f->pixelFormat.typeSize); if(_f->pixelFormat.isCompressed) @@ -808,7 +861,8 @@ Containers::Optional KtxImporter::doImage3D(UnsignedInt id, const U const File::LevelData& levelData = _f->imageData[id][level]; Containers::Array data{NoInit, levelData.data.size()}; - Utility::copy(levelData.data, data); + copyPixels<3>(levelData.size, _f->flip, _f->pixelFormat.size, levelData.data, data); + endianSwap(data, _f->pixelFormat.typeSize); if(_f->pixelFormat.isCompressed) From ef11d332a7435c27c8af0b1b48008e53199078be Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 23 Jul 2021 00:50:22 +0200 Subject: [PATCH 16/95] KtxImporter: cleanup --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 6e3400869..4808947e9 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -75,20 +75,19 @@ void endianSwap(Containers::ArrayView data, UnsignedInt typeSize) { switch(typeSize) { case 1: /* Single-byte or block-compressed format, nothing to do */ - break; + return; case 2: Utility::Endianness::littleEndianInPlace(Containers::arrayCast::Type>(data)); - break; + return; case 4: Utility::Endianness::littleEndianInPlace(Containers::arrayCast::Type>(data)); - break; + return; case 8: Utility::Endianness::littleEndianInPlace(Containers::arrayCast::Type>(data)); - break; - default: - CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ - break; + return; } + + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } enum SwizzleType : UnsignedByte { @@ -119,21 +118,21 @@ void swizzlePixels(SwizzleType type, UnsignedInt typeSize, Containers::ArrayView switch(typeSize) { case 1: swizzlePixels::Type>(type, data); - break; + return; case 2: swizzlePixels::Type>(type, data); - break; + return; case 4: swizzlePixels::Type>(type, data); - break; + return; case 8: swizzlePixels::Type>(type, data); - break; - default: - CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ - break; + return; } + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ +} + template void copyPixels(Math::Vector size, BoolVector3 flip, UnsignedInt texelSize, Containers::ArrayView src, Containers::ArrayView dst) { static_assert(dimensions >= 1 && dimensions <= 3, ""); @@ -247,7 +246,7 @@ bool validateHeader(const Implementation::KtxHeader& header, std::size_t fileSiz return false; } - const std::size_t dfdMinSize = sizeof(Implementation::KdfBasicBlockHeader) + sizeof(Implementation::KdfBasicBlockSample); + const std::size_t dfdMinSize = sizeof(UnsignedInt) + sizeof(Implementation::KdfBasicBlockHeader) + sizeof(Implementation::KdfBasicBlockSample); if(dfdMinSize > header.dfdByteLength) { Error{} << prefix << "data format descriptor too short, expected at least" << dfdMinSize << "bytes but got" << header.dfdByteLength; @@ -331,12 +330,16 @@ struct KtxImporter::File { SwizzleType swizzle; } pixelFormat; - /* Each array layer is an image with faces and mipmaps as levels */ + /* Usually only one image with n or n+1 dimensions, multiple images for + 3D array layers */ Containers::Array> imageData; }; bool KtxImporter::File::Format::decode(Implementation::VkFormat vkFormat) { - /* Find uncompressed pixel format */ + /* Find uncompressed pixel format. Note that none of the formats forbidden + by KTX are supported by Magnum, so we filter those at the same time. + This might change in the future, but we'll be fine as long as + formatMapping.hpp isn't updated without adding an extra check. */ PixelFormat format{}; switch(vkFormat) { #define _p(vulkan, magnum, _type) case Implementation::VK_FORMAT_ ## vulkan: format = PixelFormat::magnum; break; @@ -501,7 +504,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } /* Make sure we don't choke on size calculations using product() */ - const Vector3i size = Math::max(Vector3i{header.imageSize}, Vector3i{1}); + const Vector3i size = Math::max(Vector3i{header.imageSize}, 1); /* Number of array layers, imported as extra image dimensions (except for 3D images, there it's one Image3D per layer). @@ -522,13 +525,12 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { /* The level index contains byte ranges for each mipmap, from largest to smallest. Each mipmap contains tightly packed images ordered by - layers, faces, slices, rows, columns. */ + layers, faces/slices, rows, columns. */ const std::size_t levelIndexSize = numMipmaps*sizeof(Implementation::KtxLevel); const auto levelIndex = Containers::arrayCast( f->in.suffix(sizeof(Implementation::KtxHeader)).prefix(levelIndexSize)); /* Extract image data views */ - const UnsignedInt numImages = (f->numDimensions == 3) ? numLayers : 1; f->imageData = Containers::Array>{numImages}; for(UnsignedInt image = 0; image != numImages; ++image) @@ -560,12 +562,12 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } /* Shrink to next power of 2 */ - mipSize = Math::max(mipSize >> 1, Vector3i{1}); + mipSize = Math::max(mipSize >> 1, 1); } - /* Remember the image type for doImage() */ + /* Remember the type for doTexture() */ switch(f->numDimensions) { - /** @todo Use array enums once they're added to Magnum */ + /** @todo Use *Array enums once they're added to Magnum */ case 1: f->type = isLayered ? TextureData::Type::Texture1D/*Array*/ : TextureData::Type::Texture1D; break; @@ -583,18 +585,17 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { f->numDataDimensions = Math::min(f->numDimensions + UnsignedByte(isLayered || isCubemap), 3); - /* Read metadata */ - /* Read data format descriptor (DFD) */ { /* Only do some very basic sanity checks, the DFD is terribly over-engineered and the data is redundant if we have a - (Compressed)PixelFormat. */ + (Compressed)PixelFormat */ bool valid = false; const auto descriptorData = f->in.suffix(header.dfdByteOffset).prefix(header.dfdByteLength); const UnsignedInt length = *reinterpret_cast(descriptorData.data()); + Utility::Endianness::littleEndianInPlace(length); if(length == descriptorData.size()) { - const auto& block = *reinterpret_cast(descriptorData.suffix(sizeof(length)).data()); + auto& block = *reinterpret_cast(descriptorData.suffix(sizeof(length)).data()); Utility::Endianness::littleEndianInPlace(block.vendorId, block.descriptorType, block.versionNumber, block.descriptorBlockSize); @@ -612,7 +613,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { if(f->pixelFormat.isCompressed) { /* Block size */ const Vector4i expected = Vector4i::pad(compressedBlockSize(f->pixelFormat.compressed), 1); - const Vector4i actual{Vector4ub::from(block.texelBlockDimension)}; + const Vector4i actual{Math::max(Vector4ub::from(block.texelBlockDimension), {1})}; valid = valid && actual == expected; } else { /* Pixel size. For supercompressed data, bytePlanes is all @@ -647,7 +648,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { Byte data[length] Byte padding[...] - data begins with a zero-terminated key, the rest of the bytes is the + data[] begins with a zero-terminated key, the rest of the bytes is the value content. Value alignment must be implicitly done through key length, hence the funny KTX keys with multiple underscores. Any multi-byte numbers in values must be endian-swapped later. */ @@ -655,6 +656,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { while(current + sizeof(UnsignedInt) < keyValueData.size()) { /* Length without padding */ const UnsignedInt length = *reinterpret_cast(keyValueData.suffix(current).data()); + Utility::Endianness::littleEndianInPlace(length); current += sizeof(length); if(current + length < keyValueData.size()) { @@ -765,7 +767,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } if(!handled) { Error{} << "Trade::KtxImporter::openData(): unsupported channel " - "mapping:" << swizzle; + "mapping" << swizzle; return; } } From fa7946768cc34c828c32506ffb92c43ad04d35e9 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 23 Jul 2021 00:51:05 +0200 Subject: [PATCH 17/95] KtxImporter: return texture with linear sampler filters --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 4808947e9..4460cd4ab 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -882,8 +882,8 @@ Containers::Optional KtxImporter::doImage3D(UnsignedInt id, const U UnsignedInt KtxImporter::doTextureCount() const { return _f->imageData.size(); } Containers::Optional KtxImporter::doTexture(UnsignedInt id) { - return TextureData{_f->type, SamplerFilter::Nearest, SamplerFilter::Nearest, - SamplerMipmap::Base, SamplerWrapping::Repeat, id}; + return TextureData{_f->type, SamplerFilter::Linear, SamplerFilter::Linear, + SamplerMipmap::Linear, SamplerWrapping::Repeat, id}; } }} From b8a8c221436203b4ed084ed19020fe8bac5a306e Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 23 Jul 2021 01:01:16 +0200 Subject: [PATCH 18/95] KtxImporter: first round of tests --- src/MagnumPlugins/KtxImporter/CMakeLists.txt | 4 + .../KtxImporter/Test/1d-mipmaps.ktx2 | Bin 0 -> 273 bytes src/MagnumPlugins/KtxImporter/Test/1d.ktx2 | Bin 0 -> 237 bytes .../KtxImporter/Test/3d-files.txt | 3 + .../KtxImporter/Test/CMakeLists.txt | 65 ++ .../KtxImporter/Test/KtxImporterTest.cpp | 772 ++++++++++++++++++ src/MagnumPlugins/KtxImporter/Test/README.md | 7 + .../KtxImporter/Test/bgr-swizzle-bgr.ktx2 | Bin 0 -> 312 bytes src/MagnumPlugins/KtxImporter/Test/bgr.ktx2 | Bin 0 -> 288 bytes .../KtxImporter/Test/bgra-swizzle-bgra.ktx2 | Bin 0 -> 336 bytes src/MagnumPlugins/KtxImporter/Test/bgra.ktx2 | Bin 0 -> 316 bytes src/MagnumPlugins/KtxImporter/Test/black.png | Bin 0 -> 120 bytes .../KtxImporter/Test/configure.h.cmake | 28 + .../KtxImporter/Test/generate.sh | 50 ++ .../KtxImporter/Test/orientation-empty.ktx2 | Bin 0 -> 264 bytes .../KtxImporter/Test/pattern-mip1.png | Bin 0 -> 122 bytes .../KtxImporter/Test/pattern-mip2.png | Bin 0 -> 119 bytes .../KtxImporter/Test/pattern-mips.txt | 3 + .../KtxImporter/Test/pattern.png | Bin 0 -> 140 bytes .../KtxImporter/Test/pattern1d-mip1.png | Bin 0 -> 119 bytes .../KtxImporter/Test/pattern1d-mips.txt | 2 + .../KtxImporter/Test/pattern1d.png | Bin 0 -> 125 bytes .../KtxImporter/Test/rgb-mipmaps.ktx2 | Bin 0 -> 360 bytes src/MagnumPlugins/KtxImporter/Test/rgb.ktx2 | Bin 0 -> 288 bytes src/MagnumPlugins/KtxImporter/Test/rgba.ktx2 | Bin 0 -> 316 bytes .../KtxImporter/Test/swizzle-bgr.ktx2 | Bin 0 -> 312 bytes .../KtxImporter/Test/swizzle-bgra.ktx2 | Bin 0 -> 336 bytes .../KtxImporter/Test/swizzle-identity.ktx2 | Bin 0 -> 312 bytes .../KtxImporter/Test/swizzle-unsupported.ktx2 | Bin 0 -> 336 bytes .../KtxImporter/Test/version1.ktx | Bin 0 -> 132 bytes src/MagnumPlugins/KtxImporter/Test/white.png | Bin 0 -> 130 bytes 31 files changed, 934 insertions(+) create mode 100644 src/MagnumPlugins/KtxImporter/Test/1d-mipmaps.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/1d.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/3d-files.txt create mode 100644 src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt create mode 100644 src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp create mode 100644 src/MagnumPlugins/KtxImporter/Test/README.md create mode 100644 src/MagnumPlugins/KtxImporter/Test/bgr-swizzle-bgr.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/bgr.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/bgra-swizzle-bgra.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/bgra.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/black.png create mode 100644 src/MagnumPlugins/KtxImporter/Test/configure.h.cmake create mode 100644 src/MagnumPlugins/KtxImporter/Test/generate.sh create mode 100644 src/MagnumPlugins/KtxImporter/Test/orientation-empty.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/pattern-mip1.png create mode 100644 src/MagnumPlugins/KtxImporter/Test/pattern-mip2.png create mode 100644 src/MagnumPlugins/KtxImporter/Test/pattern-mips.txt create mode 100644 src/MagnumPlugins/KtxImporter/Test/pattern.png create mode 100644 src/MagnumPlugins/KtxImporter/Test/pattern1d-mip1.png create mode 100644 src/MagnumPlugins/KtxImporter/Test/pattern1d-mips.txt create mode 100644 src/MagnumPlugins/KtxImporter/Test/pattern1d.png create mode 100644 src/MagnumPlugins/KtxImporter/Test/rgb-mipmaps.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/rgb.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/rgba.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/swizzle-bgr.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/swizzle-bgra.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/swizzle-identity.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/swizzle-unsupported.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/version1.ktx create mode 100644 src/MagnumPlugins/KtxImporter/Test/white.png diff --git a/src/MagnumPlugins/KtxImporter/CMakeLists.txt b/src/MagnumPlugins/KtxImporter/CMakeLists.txt index b7de03e11..69c382e9d 100644 --- a/src/MagnumPlugins/KtxImporter/CMakeLists.txt +++ b/src/MagnumPlugins/KtxImporter/CMakeLists.txt @@ -66,5 +66,9 @@ if(MAGNUM_KTXIMPORTER_BUILD_STATIC) target_sources(KtxImporter INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/importStaticPlugin.cpp) endif() +if(BUILD_TESTS) + add_subdirectory(Test) +endif() + # MagnumPlugins KtxImporter target alias for superprojects add_library(MagnumPlugins::KtxImporter ALIAS KtxImporter) diff --git a/src/MagnumPlugins/KtxImporter/Test/1d-mipmaps.ktx2 b/src/MagnumPlugins/KtxImporter/Test/1d-mipmaps.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..e975ba6657d6e99c9c00dc26cfa9773ce1f72974 GIT binary patch literal 273 zcmZ4O9TK5nWU!l;ONvXDfq{V$h?#*H8Gyu?paKm*+6Rcw0I>lOqXQ0*Vj$pzP;mMW zln*l-MuW@(0VW0y21Z7REigF-26l)fko*tC91QGG(;<8T26mXU!1CHaH9+r_7iE^D o7BQ6MXO~ncl$q!m=o!?RDd;QYWF{erm;%lC4>A=3fGh|X0Nk4%Pyhe` literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/1d.ktx2 b/src/MagnumPlugins/KtxImporter/Test/1d.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..4282e228158a8628e0eea187fd0ed89c134a956e GIT binary patch literal 237 zcmZ4O9TK5nWU!l;ONvXDfq{V$h?#*H8Gyt<7z8qa*awKW0I>lOqk|_<0Zud;qz43; z7(5sl8JWOrggxwFK8W}a#2gIlFzdj40S0!62B^F?kOn%tyeP9IwTPi4Kf9ztq0B_j YK+mAgOhI2ECo>62#1w4i|NkI^0n%O?`v3p{ literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/3d-files.txt b/src/MagnumPlugins/KtxImporter/Test/3d-files.txt new file mode 100644 index 000000000..e8b1dcd33 --- /dev/null +++ b/src/MagnumPlugins/KtxImporter/Test/3d-files.txt @@ -0,0 +1,3 @@ +pattern.png +black.png +white.png diff --git a/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt b/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt new file mode 100644 index 000000000..be0f649bf --- /dev/null +++ b/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt @@ -0,0 +1,65 @@ +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, +# 2020, 2021 Vladimír Vondruš +# Copyright © 2021 Pablo Escobar +# 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. +# + +if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) + set(KTXIMPORTER_TEST_DIR ".") +else() + set(KTXIMPORTER_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +endif() + +# CMake before 3.8 has broken $ expressions for iOS (see +# https://gitlab.kitware.com/cmake/cmake/merge_requests/404) and since Corrade +# doesn't support dynamic plugins on iOS, this sorta works around that. Should +# be revisited when updating Travis to newer Xcode (xcode7.3 has CMake 3.6). +if(NOT MAGNUM_KTXIMPORTER_BUILD_STATIC) + set(KTXIMPORTER_PLUGIN_FILENAME $) +endif() + +# First replace ${} variables, then $<> generator expressions +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) +file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h + INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) + +corrade_add_test(KtxImporterTest KtxImporterTest.cpp + LIBRARIES MagnumTrade + FILES file.tga) +target_include_directories(KtxImporterTest PRIVATE + ${CMAKE_CURRENT_BINARY_DIR}/$ + ${PROJECT_SOURCE_DIR}/src) +if(MAGNUM_KTXIMPORTER_BUILD_STATIC) + target_link_libraries(KtxImporterTest PRIVATE KtxImporter) +else() + # So the plugins get properly built when building the test + add_dependencies(KtxImporterTest KtxImporter) +endif() +set_target_properties(KtxImporterTest PROPERTIES FOLDER "MagnumPlugins/KtxImporter/Test") +if(CORRADE_BUILD_STATIC AND NOT MAGNUM_KTXIMPORTER_BUILD_STATIC) + # CMake < 3.4 does this implicitly, but 3.4+ not anymore (see CMP0065). + # That's generally okay, *except if* the build is static, the executable + # uses a plugin manager and needs to share globals with the plugins (such + # as output redirection and so on). + set_target_properties(KtxImporterTest PROPERTIES ENABLE_EXPORTS ON) +endif() diff --git a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp new file mode 100644 index 000000000..efef7d55e --- /dev/null +++ b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp @@ -0,0 +1,772 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + Copyright © 2021 Pablo Escobar + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MagnumPlugins/KtxImporter/KtxHeader.h" + +#include "configure.h" + +namespace Magnum { namespace Trade { namespace Test { namespace { + +struct KtxImporterTest: TestSuite::Tester { + explicit KtxImporterTest(); + + /** @todo - array textures + - 3D textures + - cubemaps + - depth/stencil formats + - orientation flips + - invalid KVD + - larger formats + - block-compressed formats + */ + + void openShort(); + + void invalid(); + void invalidDataFormatDescriptor(); + void invalidVersion(); + void invalidFormat(); + + void texture(); + + void rgb8(); + void rgba8(); + + void image1d(); + void image1dMipmaps(); + + void image2dMipmaps(); + + //void image3d(); + //void image3dMipmaps(); + + void keyValueDataEmpty(); + void keyValueDataInvalid(); + + void orientationEmpty(); + void orientationInvalid(); + void orientationYDown(); + void orientationYUp(); + void orientationFlipCompressed(); + + void cube(); + void cubeIncomplete(); + + void swizzle(); + void swizzleIdentity(); + void swizzleUnsupported(); + + void openTwice(); + void importTwice(); + + /* Explicitly forbid system-wide plugin dependencies */ + PluginManager::Manager _manager{"nonexistent"}; +}; + +constexpr Color3ub Black{0}; +constexpr Color3ub White{0xff}; +constexpr Color3ub Purple{0x7f, 0, 0x7f}; + +constexpr Color3ub PatternRgb2DData[3][4]{ + /* Origin bottom-left */ + {Color3ub::red(), White, Black, Color3ub::green()}, + {White, Color3ub::red(), Black, Color3ub::green()}, + {Color3ub::blue(), Color3ub::green(), Purple, Purple} +}; + +constexpr Color4ub PatternRgba2DData[3][4]{ + {PatternRgb2DData[0][0], PatternRgb2DData[0][1], PatternRgb2DData[0][2], PatternRgb2DData[0][3]}, + {PatternRgb2DData[1][0], PatternRgb2DData[1][1], PatternRgb2DData[1][2], PatternRgb2DData[1][3]}, + {PatternRgb2DData[2][0], PatternRgb2DData[2][1], PatternRgb2DData[2][2], PatternRgb2DData[2][3]} +}; + +const struct { + const char* name; + const std::size_t length; + const char* message; +} ShortData[] { + {"empty", 0, + "the file is empty"}, + {"identifier", sizeof(Implementation::KtxHeader::identifier) - 1, + "file header too short, expected at least 80 bytes but got 11"}, + {"header", sizeof(Implementation::KtxHeader) - 1, + "file header too short, expected at least 80 bytes but got 79"}, + {"level index", sizeof(Implementation::KtxHeader) + sizeof(Implementation::KtxLevel) - 1, + "level index out of bounds, expected at least 104 bytes but got 103"}, + {"data format descriptor", sizeof(Implementation::KtxHeader) + sizeof(Implementation::KtxLevel) + sizeof(UnsignedInt) + sizeof(Implementation::KdfBasicBlockHeader), + "data format descriptor out of bounds, expected at least 180 bytes but got 132"}, + {"key/value data", sizeof(Implementation::KtxHeader) + sizeof(Implementation::KtxLevel) + sizeof(UnsignedInt) + sizeof(Implementation::KdfBasicBlockHeader) + 3*sizeof(Implementation::KdfBasicBlockSample), + "key/value data out of bounds, expected at least 252 bytes but got 180"}, + {"level data", 287, + "level data out of bounds, expected at least 288 bytes but got 287"} +}; + +const struct { + const char* name; + const char* file; + const std::size_t offset; + const char value; + const char* message; +} InvalidData[] { + {"signature", "rgb.ktx2", + offsetof(Implementation::KtxHeader, identifier) + sizeof(Implementation::KtxHeader::identifier) - 1, 0, + "wrong file signature"}, + {"type size", "rgb.ktx2", + offsetof(Implementation::KtxHeader, typeSize), 7, + "unsupported type size 7"}, + {"image size x", "rgb.ktx2", + offsetof(Implementation::KtxHeader, imageSize), 0, + "invalid image size, width is 0"}, + /** @todo */ + //{"image size y", "rgb.ktx2", + // offsetof(Implementation::KtxHeader, imageSize), 0, + // "invalid image size, depth is 5 but height is 0"}, + {"face count", "rgb.ktx2", + offsetof(Implementation::KtxHeader, faceCount), 3, + "invalid cubemap face count, expected 1 or 6 but got 3"}, + {"cube not square", "rgb.ktx2", + offsetof(Implementation::KtxHeader, faceCount), 6, + "invalid cubemap dimensions, must be 2D and square, but got Vector(4, 3, 0)"}, + /** @todo */ + //{"cube 3d", "rgb.ktx2", + // offsetof(Implementation::KtxHeader, faceCount), 6, + // "invalid cubemap dimensions, must be 2D and square, but got Vector(4, 3, 0)"}, + {"level count", "rgb.ktx2", + offsetof(Implementation::KtxHeader, levelCount), 7, + "too many mipmap levels, expected at most 3 but got 7"}, + {"custom format", "rgb.ktx2", + offsetof(Implementation::KtxHeader, vkFormat), 0, + "custom formats are not supported"}, + /** @todo get compressed file */ + //{"compressed type size", "rgb.ktx2", + // offsetof(Implementation::KtxHeader, typeSize), 4, + // "invalid type size for compressed format, expected 1 but got 4"}, + {"supercompression", "rgb.ktx2", + offsetof(Implementation::KtxHeader, supercompressionScheme), 1, + "supercompression is currently not supported"}, + /** @todo get depth format and/or 3D format */ + //{"3d depth", "rgb.ktx2", + // 0, 0, + // "3D images can't have depth/stencil format"}, + {"DFD header too short", "rgb.ktx2", + offsetof(Implementation::KtxHeader, dfdByteLength), 1, + "data format descriptor too short, expected at least 44 bytes but got 1"}, + {"DFD no space for block", "rgb.ktx2", + offsetof(Implementation::KtxHeader, dfdByteLength), sizeof(UnsignedInt) + sizeof(Implementation::KdfBasicBlockHeader) + sizeof(Implementation::KdfBasicBlockSample), + "invalid data format descriptor"}, + {"level data too short", "rgb.ktx2", + sizeof(Implementation::KtxHeader) + offsetof(Implementation::KtxLevel, byteLength), 1, + "level data too short, expected at least 36 bytes but got 1"} +}; + +const struct { + const char* name; + const char* file; + const std::size_t offset; + const char value; +} InvalidDataFormatDescriptorData[] { + {"vendor", "rgb.ktx2", offsetof(Implementation::KdfBasicBlockHeader, vendorId), 1}, + {"type", "rgb.ktx2", offsetof(Implementation::KdfBasicBlockHeader, descriptorType), 1}, + {"version", "rgb.ktx2", offsetof(Implementation::KdfBasicBlockHeader, versionNumber), 0}, + {"block too short", "rgb.ktx2", offsetof(Implementation::KdfBasicBlockHeader, descriptorBlockSize), 1}, + {"block too long", "rgb.ktx2", offsetof(Implementation::KdfBasicBlockHeader, descriptorBlockSize), sizeof(Implementation::KdfBasicBlockHeader) + 4*sizeof(Implementation::KdfBasicBlockSample)}, + {"texel size", "rgb.ktx2", offsetof(Implementation::KdfBasicBlockHeader, bytesPlane), 2}, + {"channel count", "rgb.ktx2", offsetof(Implementation::KdfBasicBlockHeader, descriptorBlockSize), sizeof(Implementation::KdfBasicBlockHeader) + 1*sizeof(Implementation::KdfBasicBlockSample)}, + /** @todo */ + //{"texel block dimensions", "rgb.ktx2", offsetof(Implementation::KdfBasicBlockHeader, texelBlockDimension), 24}, +}; + +const struct { + const char* name; + const char* file; + const Trade::TextureData::Type type; +} TextureData[] { + /** @todo Use *Array enums once they're added to Magnum */ + {"1D", "1d.ktx2", Trade::TextureData::Type::Texture1D}, + {"2D", "rgb.ktx2", Trade::TextureData::Type::Texture2D} + /** @todo one of each texture type */ +}; + +const struct { + const char* name; + const char* file; + const PixelFormat format; + const Implementation::VkFormat vkFormat; + const char* message; + const Containers::ArrayView data; +} SwizzleData[] { + {"BGR8 header", "bgr-swizzle-bgr.ktx2", + PixelFormat::RGB8Srgb, Implementation::VK_FORMAT_R8G8B8_SRGB, /* Unchanged */ + "format requires conversion from BGR to RGB", Containers::arrayCast(PatternRgb2DData)}, + {"BGRA8 header", "bgra-swizzle-bgra.ktx2", + PixelFormat::RGBA8Srgb, Implementation::VK_FORMAT_R8G8B8A8_SRGB, /* Unchanged */ + "format requires conversion from BGRA to RGBA", Containers::arrayCast(PatternRgba2DData)}, + {"BGR8 format", "bgr.ktx2", + PixelFormat::RGB8Srgb, Implementation::VK_FORMAT_B8G8R8_SRGB, + "format requires conversion from BGR to RGB", Containers::arrayCast(PatternRgb2DData)}, + {"BGRA8 format", "bgra.ktx2", + PixelFormat::RGBA8Srgb, Implementation::VK_FORMAT_B8G8R8A8_SRGB, + "format requires conversion from BGRA to RGBA", Containers::arrayCast(PatternRgba2DData)}, + {"BGRA8 format+header cancel", "swizzle-bgra.ktx2", + PixelFormat::RGBA8Srgb, Implementation::VK_FORMAT_B8G8R8A8_SRGB, + nullptr, Containers::arrayCast(PatternRgba2DData)}, + {"BGR8 format+header cancel", "swizzle-bgr.ktx2", + PixelFormat::RGB8Srgb, Implementation::VK_FORMAT_B8G8R8_SRGB, + nullptr, Containers::arrayCast(PatternRgb2DData)} + /** @todo Check swizzle of larger formats */ +}; + +KtxImporterTest::KtxImporterTest() { + addInstancedTests({&KtxImporterTest::openShort}, + Containers::arraySize(ShortData)); + + addInstancedTests({&KtxImporterTest::invalid}, + Containers::arraySize(InvalidData)); + + addInstancedTests({&KtxImporterTest::invalidDataFormatDescriptor}, + Containers::arraySize(InvalidDataFormatDescriptorData)); + + addTests({&KtxImporterTest::invalidVersion, + &KtxImporterTest::invalidFormat}); + + addInstancedTests({&KtxImporterTest::texture}, + Containers::arraySize(TextureData)); + + addTests({&KtxImporterTest::rgb8, + &KtxImporterTest::rgba8, + + & KtxImporterTest::image1d, + & KtxImporterTest::image1dMipmaps, + & KtxImporterTest::image2dMipmaps, + + &KtxImporterTest::keyValueDataEmpty, + &KtxImporterTest::keyValueDataInvalid, + + &KtxImporterTest::orientationEmpty, + &KtxImporterTest::orientationInvalid, + &KtxImporterTest::orientationYDown, + &KtxImporterTest::orientationYUp, + &KtxImporterTest::orientationFlipCompressed, + + &KtxImporterTest::cube, + &KtxImporterTest::cubeIncomplete}); + + addInstancedTests({&KtxImporterTest::swizzle}, + Containers::arraySize(SwizzleData)); + + addTests({&KtxImporterTest::swizzleIdentity, + &KtxImporterTest::swizzleUnsupported, + + &KtxImporterTest::openTwice, + &KtxImporterTest::importTwice}); + + /* Load the plugin directly from the build tree. Otherwise it's static and + already loaded. */ + #ifdef KTXIMPORTER_PLUGIN_FILENAME + CORRADE_INTERNAL_ASSERT_OUTPUT(_manager.load(KTXIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif +} + +void KtxImporterTest::openShort() { + auto&& data = ShortData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + + const auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2")); + CORRADE_COMPARE(fileData.size(), 288u); + + std::ostringstream out; + Error redirectError{&out}; + + CORRADE_VERIFY(!importer->openData(fileData.prefix(data.length))); + CORRADE_COMPARE(out.str(), Utility::formatString("Trade::KtxImporter::openData(): {}\n", data.message)); +} + +void KtxImporterTest::invalid() { + auto&& data = InvalidData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, data.file)); + CORRADE_VERIFY(!fileData.empty()); + + fileData[data.offset] = data.value; + + std::ostringstream out; + Error redirectError{&out}; + + CORRADE_VERIFY(!importer->openData(fileData)); + CORRADE_COMPARE(out.str(), Utility::formatString("Trade::KtxImporter::openData(): {}\n", data.message)); +} + +void KtxImporterTest::invalidDataFormatDescriptor() { + auto&& data = InvalidDataFormatDescriptorData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, data.file)); + CORRADE_VERIFY(!fileData.empty()); + + const Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); + const std::size_t dfdBlock = header.dfdByteOffset + sizeof(UnsignedInt); + fileData[dfdBlock + data.offset] = data.value; + + std::ostringstream out; + Error redirectError{&out}; + + CORRADE_VERIFY(!importer->openData(fileData)); + CORRADE_COMPARE(out.str(), "Trade::KtxImporter::openData(): invalid data format descriptor\n"); +} + +void KtxImporterTest::invalidVersion() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + + std::ostringstream out; + Error redirectError{&out}; + + CORRADE_VERIFY(!importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "version1.ktx"))); + CORRADE_COMPARE(out.str(), "Trade::KtxImporter::openData(): unsupported KTX version, expected 20 but got 11\n"); +} + +void KtxImporterTest::invalidFormat() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + + auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2")); + CORRADE_VERIFY(fileData.size() >= sizeof(Implementation::KtxHeader)); + + Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); + + constexpr Implementation::VkFormat formats[]{ + /* Not allowed by KTX. All of the unsupported formats happen to not be + supported by Magnum, either. */ + Implementation::VK_FORMAT_R4G4_UNORM_PACK8, + Implementation::VK_FORMAT_A1R5G5B5_UNORM_PACK16, + Implementation::VK_FORMAT_R8_USCALED, + Implementation::VK_FORMAT_R16_SSCALED, + Implementation::VK_FORMAT_G8B8G8R8_422_UNORM, + Implementation::VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, + Implementation::VK_FORMAT_R10X6G10X6_UNORM_2PACK16, + Implementation::VK_FORMAT_G16B16G16R16_422_UNORM, + /* Not supported by Magnum */ + Implementation::VK_FORMAT_R64G64B64A64_SFLOAT, + Implementation::VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG + }; + + std::ostringstream out; + Error redirectError{&out}; + + for(UnsignedInt i = 0; i != Containers::arraySize(formats); ++i) { + CORRADE_ITERATION(i); + out.str({}); /* Reset stream content */ + header.vkFormat = Utility::Endianness::littleEndian(formats[i]); + CORRADE_VERIFY(!importer->openData(fileData)); + CORRADE_COMPARE(out.str(), Utility::formatString("Trade::KtxImporter::openData(): unsupported format {}\n", UnsignedInt(formats[i]))); + } +} + +void KtxImporterTest::texture() { + auto&& data = TextureData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, data.file))); + + const Vector3ui counts{ + importer->image1DCount(), + importer->image2DCount(), + importer->image3DCount() + }; + const UnsignedInt total = counts.sum(); + + CORRADE_VERIFY(total > 0); + CORRADE_COMPARE(counts.max(), total); + CORRADE_COMPARE(importer->textureCount(), total); + + for(UnsignedInt i = 0; i != total; ++i) { + CORRADE_ITERATION(i); + const auto texture = importer->texture(i); + CORRADE_VERIFY(texture); + CORRADE_COMPARE(texture->minificationFilter(), SamplerFilter::Linear); + CORRADE_COMPARE(texture->magnificationFilter(), SamplerFilter::Linear); + CORRADE_COMPARE(texture->mipmapFilter(), SamplerMipmap::Linear); + CORRADE_COMPARE(texture->wrapping(), Math::Vector3{SamplerWrapping::Repeat}); + CORRADE_COMPARE(texture->image(), i); + CORRADE_COMPARE(texture->importerState(), nullptr); + CORRADE_COMPARE(texture->type(), data.type); + } + + /** @todo Use *Array enums once they're added to Magnum */ + UnsignedInt dimensions; + switch(data.type) { + case TextureData::Type::Texture1D: + dimensions = 1; + break; + //case TextureData::Type::Texture1DArray: + case TextureData::Type::Texture2D: + dimensions = 2; + break; + //case TextureData::Type::Texture2DArray: + //case TextureData::Type::Texture3DArray: + case TextureData::Type::Texture3D: + case TextureData::Type::Cube: + dimensions = 3; + break; + default: CORRADE_INTERNAL_ASSERT_UNREACHABLE(); + } + CORRADE_COMPARE(counts[dimensions - 1], total); +} + +void KtxImporterTest::rgb8() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + + CORRADE_COMPARE(importer->image2DCount(), 1); + CORRADE_COMPARE(importer->image2DLevelCount(0), 1); + + auto image = importer->image2D(0); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); + CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); + + const PixelStorage storage = image->storage(); + CORRADE_COMPARE(storage.alignment(), 4); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(PatternRgb2DData), TestSuite::Compare::Container); +} + +void KtxImporterTest::rgba8() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgba.ktx2"))); + + CORRADE_COMPARE(importer->image2DCount(), 1); + CORRADE_COMPARE(importer->image2DLevelCount(0), 1); + + auto image = importer->image2D(0); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGBA8Srgb); + CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); + + const PixelStorage storage = image->storage(); + CORRADE_COMPARE(storage.alignment(), 4); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(PatternRgba2DData), TestSuite::Compare::Container); +} + +void KtxImporterTest::image1d() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d.ktx2"))); + + CORRADE_COMPARE(importer->image1DCount(), 1); + CORRADE_COMPARE(importer->image1DLevelCount(0), 1); + + auto image = importer->image1D(0); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); + CORRADE_COMPARE(image->size(), (Math::Vector<1, Int>{3})); + + const PixelStorage storage = image->storage(); + CORRADE_COMPARE(storage.alignment(), 1); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + constexpr Color3ub data[3]{ + Color3ub::red(), White, Black + }; + + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(data), TestSuite::Compare::Container); +} + +void KtxImporterTest::image1dMipmaps() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d-mipmaps.ktx2"))); + + const Containers::Array mipData[2]{ + {InPlaceInit, {Color3ub::red(), White, Black}}, + {InPlaceInit, {White}} + }; + + CORRADE_COMPARE(importer->image1DCount(), 1); + CORRADE_COMPARE(importer->image1DLevelCount(0), Containers::arraySize(mipData)); + + Math::Vector<1, Int> mipSize{3}; + for(UnsignedInt i = 0; i != importer->image1DLevelCount(0); ++i) { + auto image = importer->image1D(0, i); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); + CORRADE_COMPARE(image->size(), mipSize); + + const PixelStorage storage = image->storage(); + CORRADE_COMPARE(storage.alignment(), 1); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(mipData[i]), TestSuite::Compare::Container); + + mipSize = Math::max(mipSize >> 1, 1); + } +} + +void KtxImporterTest::image2dMipmaps() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb-mipmaps.ktx2"))); + + /* Is there a nicer way to get a flat view for a nested array? */ + const auto mipData0 = Containers::arrayCast(Containers::arrayView(PatternRgb2DData)); + Containers::Array mipData[3]{ + Containers::Array{mipData0.size()}, + {InPlaceInit, {Color3ub::red(), Black}}, + {InPlaceInit, {Black}} + }; + Utility::copy(mipData0, mipData[0]); + + CORRADE_COMPARE(importer->image2DCount(), 1); + CORRADE_COMPARE(importer->image2DLevelCount(0), Containers::arraySize(mipData)); + + Vector2i mipSize{4, 3}; + for(UnsignedInt i = 0; i != importer->image2DLevelCount(0); ++i) { + auto image = importer->image2D(0, i); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); + CORRADE_COMPARE(image->size(), mipSize); + + const PixelStorage storage = image->storage(); + /* Alignment is 4 when row length is a multiple of 4 */ + const Int alignment = ((mipSize.x()*image->pixelSize())%4 == 0) ? 4 : 1; + CORRADE_COMPARE(storage.alignment(), alignment); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(mipData[i]), TestSuite::Compare::Container); + + mipSize = Math::max(mipSize >> 1, 1); + } +} + +void KtxImporterTest::keyValueDataEmpty() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + + /** @todo */ +} + +void KtxImporterTest::keyValueDataInvalid() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + + /** @todo */ +} + +void KtxImporterTest::orientationEmpty() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + importer->addFlags(ImporterFlag::Verbose); + + std::ostringstream outDebug; + Debug redirectDebug{&outDebug}; + + std::ostringstream outWarning; + Warning redirectWarning{&outWarning}; + + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "orientation-empty.ktx2"))); + CORRADE_COMPARE(outWarning.str(), "Trade::KtxImporter::openData(): missing or invalid orientation, assuming right, down\n"); + CORRADE_COMPARE(outDebug.str(), "Trade::KtxImporter::openData(): image will be flipped along y\n"); +} + +void KtxImporterTest::orientationInvalid() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + + /** @todo */ +} + +void KtxImporterTest::orientationYDown() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + + /** @todo */ +} + +void KtxImporterTest::orientationYUp() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + + /** @todo */ +} + +void KtxImporterTest::orientationFlipCompressed() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + + /** @todo */ +} + +void KtxImporterTest::cube() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + + /** @todo */ +} + +void KtxImporterTest::cubeIncomplete() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + + /** @todo */ +} + +void KtxImporterTest::swizzle() { + auto&& data = SwizzleData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + importer->addFlags(ImporterFlag::Verbose); + + auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, data.file)); + CORRADE_VERIFY(fileData.size() > sizeof(Implementation::KtxHeader)); + + /* toktx lets us swizzle the input data, but doesn't turn the format into + a swizzled one. Patch the header manually. */ + auto& header = *reinterpret_cast(fileData.data()); + header.vkFormat = Utility::Endianness::littleEndian(data.vkFormat); + + std::ostringstream outDebug; + Debug redirectDebug{&outDebug}; + + CORRADE_VERIFY(importer->openData(fileData)); + + if(data.message) + CORRADE_VERIFY(Containers::StringView{outDebug.str()}.contains(Utility::formatString("Trade::KtxImporter::openData(): {}\n", data.message))); + + CORRADE_COMPARE(importer->image2DCount(), 1); + auto image = importer->image2D(0); + CORRADE_VERIFY(image); + CORRADE_COMPARE(image->format(), data.format); + CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); + CORRADE_COMPARE_AS(image->data(), data.data, TestSuite::Compare::Container); +} + +void KtxImporterTest::swizzleIdentity() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + importer->addFlags(ImporterFlag::Verbose); + + std::ostringstream out; + Debug redirectError{&out}; + + /* RGB1 swizzle. This also checks that the correct prefix based on channel + count is used, since swizzle is always a constant length 4 in the + key/value data. */ + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "swizzle-identity.ktx2"))); + CORRADE_VERIFY(!Containers::StringView{out.str()}.contains("unsupported channel mapping")); + CORRADE_VERIFY(!Containers::StringView{out.str()}.contains("format requires conversion from")); +} + +void KtxImporterTest::swizzleUnsupported() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + + std::ostringstream out; + Error redirectError{&out}; + + /* Only identity (RG?B?A?), BGR and BGRA swizzle supported. This is the same + swizzle string as in swizzle-identity.ktx2, but this file is RGBA instead + of RGB, so the 1 shouldn't be ignored. */ + CORRADE_VERIFY(!importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "swizzle-unsupported.ktx2"))); + CORRADE_COMPARE(out.str(), "Trade::KtxImporter::openData(): unsupported channel mapping rgb1\n"); +} + +void KtxImporterTest::openTwice() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + CORRADE_COMPARE(importer->image2DCount(), 1); + CORRADE_COMPARE(importer->textureCount(), 1); + + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + CORRADE_COMPARE(importer->image2DCount(), 1); + CORRADE_COMPARE(importer->textureCount(), 1); + + /* Shouldn't crash, leak or anything */ +} + +void KtxImporterTest::importTwice() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + + /* Verify that everything is working the same way on second use */ + { + Containers::Optional image = importer->image2D(0); + CORRADE_VERIFY(image); + CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); + } { + Containers::Optional image = importer->image2D(0); + CORRADE_VERIFY(image); + CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); + } +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Trade::Test::KtxImporterTest) diff --git a/src/MagnumPlugins/KtxImporter/Test/README.md b/src/MagnumPlugins/KtxImporter/Test/README.md new file mode 100644 index 000000000..13cbb5335 --- /dev/null +++ b/src/MagnumPlugins/KtxImporter/Test/README.md @@ -0,0 +1,7 @@ +Updating test files +=================== + +The `*.ktx2?` files are created using the toktx tool from +[Khronos Texture Tools](https://github.com/KhronosGroup/KTX-Software). + +Simply execute the `generate.sh` script to create them. diff --git a/src/MagnumPlugins/KtxImporter/Test/bgr-swizzle-bgr.ktx2 b/src/MagnumPlugins/KtxImporter/Test/bgr-swizzle-bgr.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..4fe75fce623b6e843abbaca181ac887c66d55fca GIT binary patch literal 312 zcmZ4O9TK5nWU!l;ONvXDfq{V$h*^M`8Hiy3BnHADkO9O#K)eNrV}KYPh(HypKqX)_ zNDl}wF?cXAGBSbL2s_xpd=T*;h&dS8Vb+290u1aB4N!R@APsbOeoUP)p}W_}(+ zQ3_B@03u#oo>^6ulgf~kUS!Ci4G}9Z$}CAOVkpVaE~!u`Gto29GpI9D&{xRGOhOVd c1u6ytkQ@Fp)HBor(f|KI9!LNp3g&{?0RCezoB#j- literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/bgr.ktx2 b/src/MagnumPlugins/KtxImporter/Test/bgr.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..a6d482e4b2c2f0100012105948f9037c432c86cd GIT binary patch literal 288 zcmZ4O9TK5nWU!l;ONvXDfq{V$h*^M`8Hiy3BnHADkO9O#K)eNrJ%AV;{DBInpwS>b zAixCF#mLA6W+Uui2lGM1e<0>yV24=;<_j>eLo`6;g@82B+4)78sd*)dC7Jno3`HqG zF>Q!=c~NFbY7s+8es)QPLYax4fu2E~nS#DTPG%C4h$+Ob{|xmE^+5FhKhS27-|)casKmVA$E& zU50%fjw#=W_cU5WD*yq)+g;e*M9*~ls<3( literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/bgra.ktx2 b/src/MagnumPlugins/KtxImporter/Test/bgra.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..8bcb81107dfabc03df34b9130cbc927c8dadbce7 GIT binary patch literal 316 zcmZ4O9TK5nWU!l;ONvXIfq{V$h*^M`8Hiy3BnHADkO9OoKzsykAbAM}c6pcv5MKz0f!@e3 z%1q5GNi50C&toV`0g8eA0~9YW$}CAOVkpVaE~!u`Gto29GpI9D&{xRGOhOVd1)KdJ aXu>n^7y|$R literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/configure.h.cmake b/src/MagnumPlugins/KtxImporter/Test/configure.h.cmake new file mode 100644 index 000000000..b10586f4e --- /dev/null +++ b/src/MagnumPlugins/KtxImporter/Test/configure.h.cmake @@ -0,0 +1,28 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + Copyright © 2021 Pablo Escobar + + 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. +*/ + +#cmakedefine KTXIMPORTER_PLUGIN_FILENAME "${KTXIMPORTER_PLUGIN_FILENAME}" +#define KTXIMPORTER_TEST_DIR "${KTXIMPORTER_TEST_DIR}" diff --git a/src/MagnumPlugins/KtxImporter/Test/generate.sh b/src/MagnumPlugins/KtxImporter/Test/generate.sh new file mode 100644 index 000000000..6e8e562ac --- /dev/null +++ b/src/MagnumPlugins/KtxImporter/Test/generate.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +set -e + +# https://github.com/KhronosGroup/KTX-Software v4.0.0 + +toktx version1.ktx white.png + +toktx --t2 --nometadata orientation-empty.ktx2 pattern.png +#toktx --t2 orientation-rd.ktx2 pattern.png +#toktx --t2 --lower_left_maps_to_s0t0 orientation-ru.ktx2 pattern.png + +toktx --t2 --swizzle rgb1 swizzle-identity.ktx2 pattern.png +toktx --t2 --target_type RGBA --swizzle rgb1 swizzle-unsupported.ktx2 pattern.png + +# swizzled data, same swizzle in header +# data should come out normally after the importer swizzles it +toktx --t2 --input_swizzle bgra --swizzle bgr1 bgr-swizzle-bgr.ktx2 pattern.png +toktx --t2 --target_type RGBA --input_swizzle bgra --swizzle bgra bgra-swizzle-bgra.ktx2 pattern.png + +# swizzled header +# with patched swizzled vkFormat data should come out normally because both cancel each other out +toktx --t2 --swizzle bgr1 swizzle-bgr.ktx2 pattern.png +toktx --t2 --target_type RGBA --swizzle bgra swizzle-bgra.ktx2 pattern.png + +# swizzled data +# with patched swizzled vkFormat data should come out normally +toktx --t2 --input_swizzle bgra bgr.ktx2 pattern.png +toktx --t2 --target_type RGBA --input_swizzle bgra bgra.ktx2 pattern.png + +# 2D +toktx --t2 rgb.ktx2 pattern.png +toktx --t2 --target_type RGBA rgba.ktx2 pattern.png + +# manual mipmaps +toktx --t2 --mipmap rgb-mipmaps.ktx2 @@pattern-mips.txt + +#1D +toktx --t2 1d.ktx2 pattern1d.png +toktx --t2 --mipmap 1d-mipmaps.ktx2 @@pattern1d-mips.txt + +# 3D +# toktx failed to create ktxTexture; KTX error: Operation not allowed in the current state. +#toktx --t2 --depth 3 3d-rgb.ktx2 @@3d-files.txt +#toktx --t2 --depth 1 3d-one-slice.ktx2 pattern.png + +# TODO: +# 3D/cube +# arrays of all dimensions +# orientation: 1D l, 2D l, cubemap always rd, 3D diff --git a/src/MagnumPlugins/KtxImporter/Test/orientation-empty.ktx2 b/src/MagnumPlugins/KtxImporter/Test/orientation-empty.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..979bda14d9dda33fefe8bcd30fe2bb9204cc566e GIT binary patch literal 264 zcmZ4O9TK5nWU!l;ONvXDfq{V$h*^M`8Hiy3BnHADkO9O#K)eNr4S*OOJb?b zAi%`n!NADK1ZE@bUU|@$?2j&YfutPLJ<+Xt{(AnihnI)-33?=#5B^3%~ qCVB>X26bi%`U*LjNk}55K*c~){xj4w)C19fAOML##2BEw{|o?CWhE&9 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/pattern-mip1.png b/src/MagnumPlugins/KtxImporter/Test/pattern-mip1.png new file mode 100644 index 0000000000000000000000000000000000000000..d57b14659f85743b34e4f99d1c99d2b738a8049f GIT binary patch literal 122 zcmeAS@N?(olHy`uVBq!ia0vp^OhC-Y0V1m%Ufcz7ii6yp7}lMWc?smOq&xaLGB9lH z=l+w(3gmMZctjR6F!1dMVMYtqU=^Sszo(01h(vhukMjqBfPsOHk&&T3T7eHJ#o+1c K=d#Wzp$P!|avRD3 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/pattern-mip2.png b/src/MagnumPlugins/KtxImporter/Test/pattern-mip2.png new file mode 100644 index 0000000000000000000000000000000000000000..706ddf82a7a38f1df5e823beadb7777193f8633d GIT binary patch literal 119 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1SBVv2j2s6ii6yp7}lMWc?smOq&xaLGB9lH z=l+w(3gmMZctjR6F!1dMVMYtqU=^SskEe@ch(vgDLP7#a3j<@!+5ee97K5j&pUXO@ GgeCxA;u&24 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/pattern-mips.txt b/src/MagnumPlugins/KtxImporter/Test/pattern-mips.txt new file mode 100644 index 000000000..10ee359d2 --- /dev/null +++ b/src/MagnumPlugins/KtxImporter/Test/pattern-mips.txt @@ -0,0 +1,3 @@ +pattern.png +pattern-mip1.png +pattern-mip2.png diff --git a/src/MagnumPlugins/KtxImporter/Test/pattern.png b/src/MagnumPlugins/KtxImporter/Test/pattern.png new file mode 100644 index 0000000000000000000000000000000000000000..65beef67012aace785249fa4669c20dd659db572 GIT binary patch literal 140 zcmeAS@N?(olHy`uVBq!ia0vp^EI`c6!2~3&r&&$}QjEnx?oJHr&dIz4$)-E{Ix;Y9 z?C1WI$O`0h7I;J!GcfS&2Vq7F)?gK&prWUXV~9k!XvaZ81_Pd^1>NZ-t7|L-oNW3h f8Rgq;NoJIPu$S|%$ql0%pkfA3S3j3^P6eTAINBqR}2 fpkgEdvKImV1Km;2P!B}^fdC>75@Ue!{xbjoD6lOC literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/rgb.ktx2 b/src/MagnumPlugins/KtxImporter/Test/rgb.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..417fd5e1c980deaedd70c3b2da3e148a76f8a668 GIT binary patch literal 288 zcmZ4O9TK5nWU!l;ONvXDfq{V$h*^M`8Hiy3BnHADkO9O#K)eNrJ%AV;{DBInpwS>b zAixCF#mLA6W+Uui2lGM1e<0>yV24=;<_j>eLo`6;g@82B+4)78sd*)dC7Jno3`HqG zF>Q!=c~NFbY7s+8es)QPLYax4fu2E~nS#DTPG%C4h$&Dp(DeTd^$hhu^dAU7A`meK IDDOW50N-FMOIIN*^G!1&;=b3#TmmogFX0w4ft2TZB%U`zC6Q9sh}iDz2`kaA{ugV zJrZ%9{|`xo0o1rYCNYfz{2u|FQ5#)W_F#2gkvSpbdywCawxuCkU+hEPmbuE+b1moc bs*gX(3eDsk7Py8Qk7I?EK4%{y?jrXEc0D+a literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/swizzle-bgr.ktx2 b/src/MagnumPlugins/KtxImporter/Test/swizzle-bgr.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..bd1dbd874c794c069cac69e860e16f103f93f39f GIT binary patch literal 312 zcmZ4O9TK5nWU!l;ONvXDfq{V$h*^M`8Hiy3BnHADkO9O#K)eNrV}KYPh(HypKqX)_ zNDl}wF?cXAGBSbL2s_xpd=T*;h&dS8Vb+290u1aB4N!R@APsbOeoUP)p}W_}(+ zQ3_B@03u#oo>^6ulgf~kUS!Ci4G}9Z$}CAOVkpVaE~!u`Gto29GpI9D&{xRGOhOVd Z1u6!E|3G`|8R~)PKM)|q7@)lW3;+OdFq{AY literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/swizzle-bgra.ktx2 b/src/MagnumPlugins/KtxImporter/Test/swizzle-bgra.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..722cc1ccd175cc56553f483f19e37a8e4185f8d9 GIT binary patch literal 336 zcmY*UK?=e!5S*$|DG0@rcrkaO7EwRp)k6;~&A`XcT>@gV^;*t32E0sTpJ{b3|t31OPAc5x%I?a15d?#Q? zjp4g>T_3`?1N6r4Gx!TP!2Og+XH-h&rB1c1N~jZgq4jUIY?>lRc2+6)M#|Po>Rdrr nSN#z8O%zA*b0=1!klBy3r7(=4rKUyO(J<^cI-j+*I0f?sQ<6S# literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/swizzle-identity.ktx2 b/src/MagnumPlugins/KtxImporter/Test/swizzle-identity.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..108847430ce0cdb32a681e23d4fd4e6a8728e69b GIT binary patch literal 312 zcmZ4O9TK5nWU!l;ONvXDfq{V$h*^M`8Hiy3BnHADkO9O#K)eNrV}KYPh(HypKqX)_ zNDl}wF?cXAGBSbL2s_xpd=T*;h&dS8Vb+290u1aB4N!R@APsbOeoUP)p}W_}(+ zQ3_B@03u#oo>^6ulgdz(o@B_N4G}9Z$}CAOVkpVaE~!u`Gto29GpI9D&{xRGOhOVd Z1u6!E|3G`|8R~)PKM)|q7@)lW3;+O-Fq{AY literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/swizzle-unsupported.ktx2 b/src/MagnumPlugins/KtxImporter/Test/swizzle-unsupported.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..a2040dda253d8fba71ee5e7b08449fe1b778d808 GIT binary patch literal 336 zcmY*UK?=e!5S&(_QV@zK@nY^mt)hOytA`%EC>9Nt7Gki{gME-s@an(fZqtAc3_ClS zWwWpCE)~npIV%&c_A#M#@%7>P$hG nSN#z8Rh-1hb0g*=m&YGxO<@>AOHG5OrD51_v_5NVaSG-OK~g?I literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/version1.ktx b/src/MagnumPlugins/KtxImporter/Test/version1.ktx new file mode 100644 index 0000000000000000000000000000000000000000..9735cf5db7c9a326fc81730e7fd63097554b9135 GIT binary patch literal 132 zcmZ4O9TK5nXtqAZc)I$ztaD0e0swsA91Q>f literal 0 HcmV?d00001 From 06858dd6b975d5e585d70a7e02147c1a2863f293 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 23 Jul 2021 01:04:46 +0200 Subject: [PATCH 19/95] Trade: KtxImageConverter Currently missing support for block-compressed formats --- CMakeLists.txt | 1 + src/MagnumPlugins/CMakeLists.txt | 4 + .../KtxImageConverter/CMakeLists.txt | 72 +++ .../KtxImageConverter/KtxImageConverter.conf | 0 .../KtxImageConverter/KtxImageConverter.cpp | 608 ++++++++++++++++++ .../KtxImageConverter/KtxImageConverter.h | 73 +++ .../KtxImageConverter/Test/CMakeLists.txt | 66 ++ .../Test/KtxImageConverterTest.cpp | 66 ++ .../KtxImageConverter/Test/configure.h.cmake | 28 + .../KtxImageConverter/configure.h.cmake | 27 + .../KtxImageConverter/importStaticPlugin.cpp | 36 ++ 11 files changed, 981 insertions(+) create mode 100644 src/MagnumPlugins/KtxImageConverter/CMakeLists.txt create mode 100644 src/MagnumPlugins/KtxImageConverter/KtxImageConverter.conf create mode 100644 src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp create mode 100644 src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/configure.h.cmake create mode 100644 src/MagnumPlugins/KtxImageConverter/configure.h.cmake create mode 100644 src/MagnumPlugins/KtxImageConverter/importStaticPlugin.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 238dd234b..2c509db62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,7 @@ option(WITH_HARFBUZZFONT "Build HarfBuzzFont plugin" OFF) option(WITH_ICOIMPORTER "Build IcoImporter plugin" OFF) option(WITH_JPEGIMAGECONVERTER "Build JpegImageConverter plugin" OFF) option(WITH_JPEGIMPORTER "Build JpegImporter plugin" OFF) +option(WITH_KTXIMAGECONVERTER "Build KtxImageConverter plugin" OFF) option(WITH_KTXIMPORTER "Build KtxImporter plugin" OFF) option(WITH_MESHOPTIMIZERSCENECONVERTER "Build MeshOptimizerSceneConverter plugin" OFF) option(WITH_MINIEXRIMAGECONVERTER "Build MiniExrImageConverter plugin" OFF) diff --git a/src/MagnumPlugins/CMakeLists.txt b/src/MagnumPlugins/CMakeLists.txt index 6b50e6b9b..dcf8ba1c0 100644 --- a/src/MagnumPlugins/CMakeLists.txt +++ b/src/MagnumPlugins/CMakeLists.txt @@ -95,6 +95,10 @@ if(WITH_JPEGIMPORTER) add_subdirectory(JpegImporter) endif() +if(WITH_KTXIMAGECONVERTER) + add_subdirectory(KtxImageConverter) +endif() + if(WITH_KTXIMPORTER) add_subdirectory(KtxImporter) endif() diff --git a/src/MagnumPlugins/KtxImageConverter/CMakeLists.txt b/src/MagnumPlugins/KtxImageConverter/CMakeLists.txt new file mode 100644 index 000000000..3b2b35281 --- /dev/null +++ b/src/MagnumPlugins/KtxImageConverter/CMakeLists.txt @@ -0,0 +1,72 @@ +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, +# 2020, 2021 Vladimír Vondruš +# Copyright © 2021 Pablo Escobar +# +# 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. +# + +find_package(Magnum REQUIRED Trade) + +if(BUILD_PLUGINS_STATIC AND NOT DEFINED MAGNUM_KTXIMAGECONVERTER_BUILD_STATIC) + set(MAGNUM_KTXIMAGECONVERTER_BUILD_STATIC 1) +endif() + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/configure.h) + +# KtxImageConverter plugin +add_plugin(KtxImageConverter + "${MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_BINARY_INSTALL_DIR};${MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_LIBRARY_INSTALL_DIR}" + "${MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_BINARY_INSTALL_DIR};${MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_LIBRARY_INSTALL_DIR}" + KtxImageConverter.conf + KtxImageConverter.cpp + KtxImageConverter.h) +if(MAGNUM_KTXIMAGECONVERTER_BUILD_STATIC AND BUILD_STATIC_PIC) + set_target_properties(KtxImageConverter PROPERTIES POSITION_INDEPENDENT_CODE ON) +endif() +target_include_directories(KtxImageConverter PUBLIC + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_BINARY_DIR}/src) +target_link_libraries(KtxImageConverter PUBLIC Magnum::Trade) +# Modify output location only if all are set, otherwise it makes no sense +if(CMAKE_RUNTIME_OUTPUT_DIRECTORY AND CMAKE_LIBRARY_OUTPUT_DIRECTORY AND CMAKE_ARCHIVE_OUTPUT_DIRECTORY) + set_target_properties(KtxImageConverter PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/magnum$<$:-d>/imageconverters + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/magnum$<$:-d>/imageconverters + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/magnum$<$:-d>/imageconverters) +endif() + +install(FILES KtxImageConverter.h ${CMAKE_CURRENT_BINARY_DIR}/configure.h + DESTINATION ${MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR}/KtxImageConverter) + +# Automatic static plugin import +if(MAGNUM_KTXIMAGECONVERTER_BUILD_STATIC) + install(FILES importStaticPlugin.cpp DESTINATION ${MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR}/KtxImageConverter) + target_sources(KtxImageConverter INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/importStaticPlugin.cpp) +endif() + +if(BUILD_TESTS) + add_subdirectory(Test) +endif() + +# MagnumPlugins KtxImageConverter target alias for superprojects +add_library(MagnumPlugins::KtxImageConverter ALIAS KtxImageConverter) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.conf b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.conf new file mode 100644 index 000000000..e69de29bb diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp new file mode 100644 index 000000000..023a96b71 --- /dev/null +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -0,0 +1,608 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + Copyright © 2021 Pablo Escobar + + 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 "KtxImageConverter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "MagnumPlugins/KtxImporter/KtxHeader.h" + +namespace Magnum { namespace Trade { + +namespace { + +typedef Containers::Pair FormatPair; + +FormatPair vulkanFormat(PixelFormat format) { + switch(format) { + #define _p(vulkan, magnum, type) case PixelFormat::magnum: \ + return {Implementation::VK_FORMAT_ ## vulkan, Implementation::VkFormatSuffix::type}; + #include "MagnumPlugins/KtxImporter/formatMapping.hpp" + #undef _p + default: + return {{}, {}}; + } +} + +FormatPair vulkanFormat(CompressedPixelFormat format) { + switch(format) { + #define _c(vulkan, magnum, type) case CompressedPixelFormat::magnum: \ + return {Implementation::VK_FORMAT_ ## vulkan, Implementation::VkFormatSuffix::type}; + #include "MagnumPlugins/KtxImporter/formatMapping.hpp" + #undef _c + default: + return {{}, {}}; + } +} + +UnsignedByte componentSize(PixelFormat format) { + CORRADE_INTERNAL_ASSERT(!isPixelFormatImplementationSpecific(format)); + + #ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic error "-Wswitch" + #endif + switch(format) { + case PixelFormat::R8Unorm: + case PixelFormat::RG8Unorm: + case PixelFormat::RGB8Unorm: + case PixelFormat::RGBA8Unorm: + case PixelFormat::R8Snorm: + case PixelFormat::RG8Snorm: + case PixelFormat::RGB8Snorm: + case PixelFormat::RGBA8Snorm: + case PixelFormat::R8Srgb: + case PixelFormat::RG8Srgb: + case PixelFormat::RGB8Srgb: + case PixelFormat::RGBA8Srgb: + case PixelFormat::R8UI: + case PixelFormat::RG8UI: + case PixelFormat::RGB8UI: + case PixelFormat::RGBA8UI: + case PixelFormat::R8I: + case PixelFormat::RG8I: + case PixelFormat::RGB8I: + case PixelFormat::RGBA8I: + case PixelFormat::Stencil8UI: + return 1; + case PixelFormat::R16Unorm: + case PixelFormat::RG16Unorm: + case PixelFormat::RGB16Unorm: + case PixelFormat::RGBA16Unorm: + case PixelFormat::R16Snorm: + case PixelFormat::RG16Snorm: + case PixelFormat::RGB16Snorm: + case PixelFormat::RGBA16Snorm: + case PixelFormat::R16UI: + case PixelFormat::RG16UI: + case PixelFormat::RGB16UI: + case PixelFormat::RGBA16UI: + case PixelFormat::R16I: + case PixelFormat::RG16I: + case PixelFormat::RGB16I: + case PixelFormat::RGBA16I: + case PixelFormat::R16F: + case PixelFormat::RG16F: + case PixelFormat::RGB16F: + case PixelFormat::RGBA16F: + case PixelFormat::Depth16Unorm: + case PixelFormat::Depth16UnormStencil8UI: + return 2; + case PixelFormat::R32UI: + case PixelFormat::RG32UI: + case PixelFormat::RGB32UI: + case PixelFormat::RGBA32UI: + case PixelFormat::R32I: + case PixelFormat::RG32I: + case PixelFormat::RGB32I: + case PixelFormat::RGBA32I: + case PixelFormat::R32F: + case PixelFormat::RG32F: + case PixelFormat::RGB32F: + case PixelFormat::RGBA32F: + case PixelFormat::Depth24Unorm: + case PixelFormat::Depth32F: + case PixelFormat::Depth24UnormStencil8UI: + case PixelFormat::Depth32FStencil8UI: + return 4; + } + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif + + CORRADE_ASSERT_UNREACHABLE("componentSize(): invalid format" << format, {}); +} + +UnsignedByte componentSize(CompressedPixelFormat) { + return 1; +} + +UnsignedByte channelFormat(Implementation::VkFormatSuffix suffix, Implementation::KdfBasicBlockSample::ChannelId id) { + switch(suffix) { + case Implementation::VkFormatSuffix::UNORM: + return {}; + case Implementation::VkFormatSuffix::SNORM: + return Implementation::KdfBasicBlockSample::ChannelFormat::Signed; + case Implementation::VkFormatSuffix::UINT: + return {}; + case Implementation::VkFormatSuffix::SINT: + return Implementation::KdfBasicBlockSample::ChannelFormat::Signed; + case Implementation::VkFormatSuffix::UFLOAT: + return Implementation::KdfBasicBlockSample::ChannelFormat::Float; + case Implementation::VkFormatSuffix::SFLOAT: + return Implementation::KdfBasicBlockSample::ChannelFormat::Float | + Implementation::KdfBasicBlockSample::ChannelFormat::Signed; + case Implementation::VkFormatSuffix::SRGB: + return (id == Implementation::KdfBasicBlockSample::ChannelId::Alpha) + ? Implementation::KdfBasicBlockSample::ChannelFormat::Linear + : Implementation::KdfBasicBlockSample::ChannelFormat{}; + } + + CORRADE_ASSERT_UNREACHABLE("channelFormat(): invalid format suffix" << suffix, {}); +} + +Containers::Array2 channelMapping(Implementation::VkFormatSuffix suffix, UnsignedByte typeSize) { + /* sampleLower and sampleUpper define how to interpret the range of values + found in a channel. + samplerLower = black value or -1 for signed values + samplerUpper = white value or 1 for signed values + + There are a lot more weird subtleties for other color modes but this + simple version is enough for our needs. + + Signed integer values are sign-extended. Floats need to be bitcast. */ + + /** @todo If we support custom formats, this might require changes for + typeSize == 8 because these values are only 32-bit. Currently, + Magnum doesn't expose 64-bit formats. */ + CORRADE_INTERNAL_ASSERT(typeSize <= 4); + + const UnsignedInt typeMask = ~0u >> ((4 - typeSize) * 8); + + switch(suffix) { + case Implementation::VkFormatSuffix::UNORM: + case Implementation::VkFormatSuffix::SRGB: + return {0u, typeMask}; + case Implementation::VkFormatSuffix::SNORM: + { + /* Remove sign bit to get largest positive value. If we flip the + bits of that, we get the sign-extended lowest negative value. */ + const UnsignedInt positiveTypeMask = typeMask >> 1; + return {~positiveTypeMask, positiveTypeMask}; + } + case Implementation::VkFormatSuffix::UINT: + return {0u, 1u}; + case Implementation::VkFormatSuffix::SINT: + return {~0u, 1u}; + case Implementation::VkFormatSuffix::UFLOAT: + return {Corrade::Utility::bitCast(0.0f), Corrade::Utility::bitCast(1.0f)}; + case Implementation::VkFormatSuffix::SFLOAT: + return {Corrade::Utility::bitCast(-1.0f), Corrade::Utility::bitCast(1.0f)}; + } + + CORRADE_ASSERT_UNREACHABLE("channelLimits(): invalid format suffix" << suffix, {}); +} + +bool isSrgb(PixelFormat format) { + CORRADE_INTERNAL_ASSERT(!isPixelFormatImplementationSpecific(format)); + + switch(format) { + case PixelFormat::R8Srgb: + case PixelFormat::RG8Srgb: + case PixelFormat::RGB8Srgb: + case PixelFormat::RGBA8Srgb: + return true; + default: + return false; + } +} + +bool isSrgb(CompressedPixelFormat format) { + CORRADE_INTERNAL_ASSERT(!isCompressedPixelFormatImplementationSpecific(format)); + + switch(format) { + case CompressedPixelFormat::Bc1RGBSrgb: + case CompressedPixelFormat::Bc1RGBASrgb: + case CompressedPixelFormat::Bc2RGBASrgb: + case CompressedPixelFormat::Bc3RGBASrgb: + case CompressedPixelFormat::Bc7RGBASrgb: + case CompressedPixelFormat::Etc2RGB8Srgb: + case CompressedPixelFormat::Etc2RGB8A1Srgb: + case CompressedPixelFormat::Etc2RGBA8Srgb: + case CompressedPixelFormat::Astc4x4RGBASrgb: + case CompressedPixelFormat::Astc5x4RGBASrgb: + case CompressedPixelFormat::Astc5x5RGBASrgb: + case CompressedPixelFormat::Astc6x5RGBASrgb: + case CompressedPixelFormat::Astc6x6RGBASrgb: + case CompressedPixelFormat::Astc8x5RGBASrgb: + case CompressedPixelFormat::Astc8x6RGBASrgb: + case CompressedPixelFormat::Astc8x8RGBASrgb: + case CompressedPixelFormat::Astc10x5RGBASrgb: + case CompressedPixelFormat::Astc10x6RGBASrgb: + case CompressedPixelFormat::Astc10x8RGBASrgb: + case CompressedPixelFormat::Astc10x10RGBASrgb: + case CompressedPixelFormat::Astc12x10RGBASrgb: + case CompressedPixelFormat::Astc12x12RGBASrgb: + case CompressedPixelFormat::Astc3x3x3RGBASrgb: + case CompressedPixelFormat::Astc4x3x3RGBASrgb: + case CompressedPixelFormat::Astc4x4x3RGBASrgb: + case CompressedPixelFormat::Astc4x4x4RGBASrgb: + case CompressedPixelFormat::Astc5x4x4RGBASrgb: + case CompressedPixelFormat::Astc5x5x4RGBASrgb: + case CompressedPixelFormat::Astc5x5x5RGBASrgb: + case CompressedPixelFormat::Astc6x5x5RGBASrgb: + case CompressedPixelFormat::Astc6x6x5RGBASrgb: + case CompressedPixelFormat::Astc6x6x6RGBASrgb: + case CompressedPixelFormat::PvrtcRGB2bppSrgb: + case CompressedPixelFormat::PvrtcRGBA2bppSrgb: + case CompressedPixelFormat::PvrtcRGB4bppSrgb: + case CompressedPixelFormat::PvrtcRGBA4bppSrgb: + return true; + default: + return false; + } +} + +Containers::Array fillDataFormatDescriptor(PixelFormat format, Implementation::VkFormatSuffix suffix) { + const UnsignedInt texelSize = pixelSize(format); + const UnsignedInt typeSize = componentSize(format); + /** @todo numChannels will be wrong for block-compressed formats */ + const UnsignedInt numChannels = texelSize / typeSize; + + /* Calculate size */ + const std::size_t dfdSamplesSize = numChannels * sizeof(Implementation::KdfBasicBlockSample); + const std::size_t dfdBlockSize = sizeof(Implementation::KdfBasicBlockHeader) + dfdSamplesSize; + const std::size_t dfdSize = sizeof(UnsignedInt) + dfdBlockSize; + CORRADE_INTERNAL_ASSERT(dfdSize % 4 == 0); + + Containers::Array data{ValueInit, dfdSize}; + + std::size_t offset = 0; + + /* Length */ + UnsignedInt& length = *reinterpret_cast(data.suffix(offset).data()); + offset += sizeof(length); + + length = dfdSize; + + /* Block header */ + Implementation::KdfBasicBlockHeader& header = *reinterpret_cast(data.suffix(offset).data()); + offset += sizeof(header); + + header.vendorId = Implementation::KdfBasicBlockHeader::VendorId::Khronos; + header.descriptorType = Implementation::KdfBasicBlockHeader::DescriptorType::Basic; + header.versionNumber = Implementation::KdfBasicBlockHeader::VersionNumber::Kdf1_3; + header.descriptorBlockSize = dfdBlockSize; + + header.colorModel = Implementation::KdfBasicBlockHeader::ColorModel::Rgbsda; + header.colorPrimaries = Implementation::KdfBasicBlockHeader::ColorPrimaries::Srgb; + header.transferFunction = isSrgb(format) + ? Implementation::KdfBasicBlockHeader::TransferFunction::Srgb + : Implementation::KdfBasicBlockHeader::TransferFunction::Linear; + /** @todo Do we ever have premultiplied alpha? */ + header.bytesPlane[0] = texelSize; + + /* Color channels */ + const auto samples = Containers::arrayCast(data.suffix(offset)); + offset += dfdSamplesSize; + + const UnsignedByte bitLength = typeSize*8; + + static const Implementation::KdfBasicBlockSample::ChannelId channelIdsRgba[4]{ + Implementation::KdfBasicBlockSample::ChannelId::Red, + Implementation::KdfBasicBlockSample::ChannelId::Green, + Implementation::KdfBasicBlockSample::ChannelId::Blue, + Implementation::KdfBasicBlockSample::ChannelId::Alpha + }; + /* + static const Implementation::KdfBasicBlockSample::ChannelId channelIdsDepthStencil[2]{ + Implementation::KdfBasicBlockSample::ChannelId::Depth, + Implementation::KdfBasicBlockSample::ChannelId::Stencil + }; + static const Implementation::KdfBasicBlockSample::ChannelId channelIdsStencil[1]{ + Implementation::KdfBasicBlockSample::ChannelId::Stencil + }; + */ + + /** @todo detect depth/stencil */ + Containers::ArrayView channelIds = + Containers::arrayView(channelIdsRgba); + + UnsignedShort bitOffset = 0; + for(UnsignedInt i = 0; i != numChannels; ++i) { + auto& sample = samples[i]; + sample.bitOffset = bitOffset; + sample.bitLength = bitLength - 1; + const auto channelId = channelIds[i]; + sample.channelType = channelId | channelFormat(suffix, channelId); + const auto mapping = channelMapping(suffix, typeSize); + sample.lower = mapping[0]; + sample.upper = mapping[1]; + + bitOffset += bitLength; + + Utility::Endianness::littleEndianInPlace(sample.bitOffset, + sample.lower, sample.upper); + } + + Utility::Endianness::littleEndianInPlace(length); + Utility::Endianness::littleEndianInPlace(header.vendorId, header.descriptorType, + header.versionNumber, header.descriptorBlockSize); + + CORRADE_INTERNAL_ASSERT(offset == dfdSize); + + return data; +} + +typedef Containers::Pair KeyValuePair; + +Containers::Array packKeyValueData(Containers::ArrayView pairs) { + /* Calculate size */ + std::size_t kvdSize = 0; + for(const KeyValuePair& entry: pairs) { + const UnsignedInt length = entry.first().size() + 1 + entry.second().size() + 1; + kvdSize += sizeof(length) + (length + 3)/4*4; + } + CORRADE_INTERNAL_ASSERT(kvdSize % 4 == 0); + + /* Pack. We assume that values are actual text strings and don't need any + endian-swapping. */ + std::size_t offset = 0; + Containers::Array data{ValueInit, kvdSize}; + for(const KeyValuePair& entry: pairs) { + const auto key = entry.first(); + const auto value = entry.second(); + const UnsignedInt length = key.size() + 1 + value.size() + 1; + *reinterpret_cast(data.suffix(offset).data()) = length; + Utility::Endianness::littleEndianInPlace(length); + offset += sizeof(length); + Utility::copy(key, data.suffix(offset).prefix(key.size())); + offset += entry.first().size() + 1; + Utility::copy(value, data.suffix(offset).prefix(value.size())); + offset += entry.second().size() + 1; + offset = (offset + 3)/4*4; + } + CORRADE_INTERNAL_ASSERT(offset == kvdSize); + + return data; +} + +template struct TypeForSize {}; +template<> struct TypeForSize<1> { typedef UnsignedByte Type; }; +template<> struct TypeForSize<2> { typedef UnsignedShort Type; }; +template<> struct TypeForSize<4> { typedef UnsignedInt Type; }; +template<> struct TypeForSize<8> { typedef UnsignedLong Type; }; + +void endianSwap(Containers::ArrayView data, UnsignedInt typeSize) { + switch(typeSize) { + case 1: + /* Single-byte or block-compressed format, nothing to do */ + return; + case 2: + Utility::Endianness::littleEndianInPlace(Containers::arrayCast::Type>(data)); + return; + case 4: + Utility::Endianness::littleEndianInPlace(Containers::arrayCast::Type>(data)); + return; + case 8: + Utility::Endianness::littleEndianInPlace(Containers::arrayCast::Type>(data)); + return; + } + + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ +} + +UnsignedInt leastCommonMultiple(UnsignedInt a, UnsignedInt b) { + const UnsignedInt product = a*b; + + /* Greatest common divisor */ + while(b != 0) { + const UnsignedInt t = a % b; + a = b; + b = t; + } + const UnsignedInt gcd = a; + + return product/gcd; +} + +template +Containers::Array convertImage(const BasicImageView& image) { + if(isPixelFormatImplementationSpecific(image.format())) { + Error{} << "Trade::KtxImageConverter::convertToData(): implementation-specific formats are not supported"; + return {}; + } + + const auto vkFormat = vulkanFormat(image.format()); + if(vkFormat.first() == Implementation::VK_FORMAT_UNDEFINED) { + Error{} << "Trade::KtxImageConverter::convertToData(): unsupported format" << image.format(); + return {}; + } + + const Containers::Array dataFormatDescriptor = fillDataFormatDescriptor(image.format(), vkFormat.second()); + + /* Fill key/value data. Values can be any byte-string but the values we + write are all constant text strings. Keys must be sorted alphabetically.*/ + using namespace Containers::Literals; + + const Containers::StaticArray<2, const KeyValuePair> keyValueMap{ + /* Origin left, bottom, back (increasing right, up, out) */ + /** @todo KTX spec says "most other APIs and the majority of texture compression tools use [r, rd, rdi]". + Should we just always flip image data? Maybe make this configurable. */ + Containers::pair("KTXorientation"_s, "ruo"_s.prefix(dimensions)), + Containers::pair("KTXwriter"_s, "Magnum::KtxImageConverter 1.0"_s) + }; + + const Containers::Array keyValueData = packKeyValueData(keyValueMap); + + /* Calculate level offsets. Needs to be aligned to the least common + multiple of the texel/block size and 4. */ + constexpr UnsignedInt numMipmaps = 1; + Containers::Array levelIndex{numMipmaps}; + const std::size_t levelIndexSize = levelIndex.size()*sizeof(Implementation::KtxLevel); + + const UnsignedInt pixelSize = UnsignedByte(image.pixelSize()); + std::size_t levelOffset = sizeof(Implementation::KtxHeader) + levelIndexSize + + dataFormatDescriptor.size() + keyValueData.size(); + + /* @todo Store mip levels from smallest to lowest for efficient streaming */ + for(UnsignedInt i = 0; i != levelIndex.size(); ++i) { + const std::size_t alignment = leastCommonMultiple(pixelSize, 4); + levelOffset = (levelOffset + (alignment - 1)) / alignment * alignment; + const std::size_t levelSize = pixelSize*image.size().product(); + + levelIndex[i].byteOffset = levelOffset; + levelIndex[i].byteLength = levelSize; + levelIndex[i].uncompressedByteLength = levelSize; + + levelOffset += levelSize; + } + + /* Initialize data buffer */ + const std::size_t dataSize = levelOffset; + Containers::Array data{ValueInit, dataSize}; + + std::size_t offset = 0; + + /* Fill header */ + auto& header = *reinterpret_cast(data.data()); + offset += sizeof(header); + Utility::copy(Containers::arrayView(Implementation::KtxFileIdentifier), Containers::arrayView(header.identifier)); + + header.vkFormat = vkFormat.first(); + header.typeSize = componentSize(image.format()); + header.imageSize = Vector3ui{Vector3i::pad(image.size(), 0u)}; + header.layerCount = 0; + header.faceCount = 1; + header.levelCount = levelIndex.size(); + header.supercompressionScheme = Implementation::SuperCompressionScheme::None; + + for(auto& level: levelIndex) { + /* Copy the pixels into output, dropping padding (if any) */ + auto pixels = data.suffix(level.byteOffset).prefix(level.byteLength); + + std::size_t sizes[dimensions + 1]; + sizes[dimensions] = pixelSize; + for(UnsignedInt i = 0; i != dimensions; ++i) { + sizes[dimensions - 1 - i] = image.size()[i]; + } + + Utility::copy(image.pixels(), Containers::StridedArrayView{pixels, sizes}); + + endianSwap(pixels, header.typeSize); + + Utility::Endianness::littleEndianInPlace( + level.byteOffset, level.byteLength, + level.uncompressedByteLength); + } + + Utility::copy(Containers::arrayCast(levelIndex), data.suffix(offset).prefix(levelIndexSize)); + offset += levelIndexSize; + + header.dfdByteOffset = offset; + header.dfdByteLength = dataFormatDescriptor.size(); + offset += header.dfdByteLength; + + Utility::copy(dataFormatDescriptor, data.suffix(header.dfdByteOffset).prefix(header.dfdByteLength)); + + header.kvdByteOffset = offset; + header.kvdByteLength = keyValueData.size(); + offset += header.kvdByteLength; + + Utility::copy(keyValueData, data.suffix(header.kvdByteOffset).prefix(header.kvdByteLength)); + + /* Endian-swap once we're done using the header data */ + Utility::Endianness::littleEndianInPlace( + header.vkFormat, header.typeSize, + header.imageSize[0], header.imageSize[1], header.imageSize[2], + header.layerCount, header.faceCount, header.levelCount, + header.supercompressionScheme, + header.dfdByteOffset, header.dfdByteLength, + header.kvdByteOffset, header.kvdByteLength); + + return data; +} + +/** @todo */ +template +Containers::Array convertImage(const BasicCompressedImageView&) { + return {}; +} + +} + +KtxImageConverter::KtxImageConverter() = default; + +KtxImageConverter::KtxImageConverter(PluginManager::AbstractManager& manager, const std::string& plugin): AbstractImageConverter{manager, plugin} {} + +ImageConverterFeatures KtxImageConverter::doFeatures() const { + return ImageConverterFeature::Convert1DToData | + ImageConverterFeature::Convert2DToData | + ImageConverterFeature::Convert3DToData | + ImageConverterFeature::ConvertCompressed1DToData | + ImageConverterFeature::ConvertCompressed2DToData | + ImageConverterFeature::ConvertCompressed3DToData; +} + +Containers::Array KtxImageConverter::doConvertToData(const ImageView1D& image) { + return convertImage(image); +} + +Containers::Array KtxImageConverter::doConvertToData(const ImageView2D& image) { + return convertImage(image); +} + +Containers::Array KtxImageConverter::doConvertToData(const ImageView3D& image) { + return convertImage(image); +} + +Containers::Array KtxImageConverter::doConvertToData(const CompressedImageView1D& image) { + return convertImage(image); +} + +Containers::Array KtxImageConverter::doConvertToData(const CompressedImageView2D& image) { + return convertImage(image); +} + +Containers::Array KtxImageConverter::doConvertToData(const CompressedImageView3D& image) { + return convertImage(image); +} + +}} + +CORRADE_PLUGIN_REGISTER(KtxImageConverter, Magnum::Trade::KtxImageConverter, + "cz.mosra.magnum.Trade.AbstractImageConverter/0.3") diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h new file mode 100644 index 000000000..4c7c53883 --- /dev/null +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h @@ -0,0 +1,73 @@ +#ifndef Magnum_Trade_KtxImageConverter_h +#define Magnum_Trade_KtxImageConverter_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + Copyright © 2021 Pablo Escobar + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include "MagnumPlugins/KtxImageConverter/configure.h" + +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_KTXIMAGECONVERTER_BUILD_STATIC + #ifdef KtxImageConverter_EXPORTS + #define MAGNUM_KTXIMAGECONVERTER_EXPORT CORRADE_VISIBILITY_EXPORT + #else + #define MAGNUM_KTXIMAGECONVERTER_EXPORT CORRADE_VISIBILITY_IMPORT + #endif +#else + #define MAGNUM_KTXIMAGECONVERTER_EXPORT CORRADE_VISIBILITY_STATIC +#endif +#define MAGNUM_KTXIMAGECONVERTER_LOCAL CORRADE_VISIBILITY_LOCAL +#else +#define MAGNUM_KTXIMAGECONVERTER_EXPORT +#define MAGNUM_KTXIMAGECONVERTER_LOCAL +#endif + +namespace Magnum { namespace Trade { + +class MAGNUM_KTXIMAGECONVERTER_EXPORT KtxImageConverter: public AbstractImageConverter { + public: + /** @brief Default constructor */ + explicit KtxImageConverter(); + + /** @brief Plugin manager constructor */ + explicit KtxImageConverter(PluginManager::AbstractManager& manager, const std::string& plugin); + + private: + ImageConverterFeatures MAGNUM_KTXIMAGECONVERTER_LOCAL doFeatures() const override; + + Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(const ImageView1D& image) override; + Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(const ImageView2D& image) override; + Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(const ImageView3D& image) override; + + Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(const CompressedImageView1D& image) override; + Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(const CompressedImageView2D& image) override; + Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(const CompressedImageView3D& image) override; +}; + +}} + +#endif diff --git a/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt b/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt new file mode 100644 index 000000000..7dd83d282 --- /dev/null +++ b/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt @@ -0,0 +1,66 @@ +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, +# 2020, 2021 Vladimír Vondruš +# Copyright © 2021 Pablo Escobar +# +# 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. +# + +# CMake before 3.8 has broken $ expressions for iOS (see +# https://gitlab.kitware.com/cmake/cmake/merge_requests/404) and since Corrade +# doesn't support dynamic plugins on iOS, this sorta works around that. Should +# be revisited when updating Travis to newer Xcode (xcode7.3 has CMake 3.6). +if(NOT MAGNUM_KTXIMAGECONVERTER_BUILD_STATIC) + set(KTXIMAGECONVERTER_PLUGIN_FILENAME $) + if(WITH_KTXIMPORTER) + set(KTXIMPORTER_PLUGIN_FILENAME $) + endif() +endif() + +# First replace ${} variables, then $<> generator expressions +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) +file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h + INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) + +corrade_add_test(KtxImageConverterTest KtxImageConverterTest.cpp + LIBRARIES MagnumTrade) +target_include_directories(KtxImageConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) +if(MAGNUM_KTXIMAGECONVERTER_BUILD_STATIC) + target_link_libraries(KtxImageConverterTest PRIVATE KtxImageConverter) + if(WITH_KTXIMPORTER) + target_link_libraries(KtxImageConverterTest PRIVATE KtxImporter) + endif() +else() + # So the plugins get properly built when building the test + add_dependencies(KtxImageConverterTest KtxImageConverter) + if(WITH_KTXIMPORTER) + add_dependencies(KtxImageConverterTest KtxImporter) + endif() +endif() +set_target_properties(KtxImageConverterTest PROPERTIES FOLDER "MagnumPlugins/KtxImageConverter/Test") +if(CORRADE_BUILD_STATIC AND NOT MAGNUM_KTXIMAGECONVERTER_BUILD_STATIC) + # CMake < 3.4 does this implicitly, but 3.4+ not anymore (see CMP0065). + # That's generally okay, *except if* the build is static, the executable + # uses a plugin manager and needs to share globals with the plugins (such + # as output redirection and so on). + set_target_properties(KtxImageConverterTest PROPERTIES ENABLE_EXPORTS ON) +endif() diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp new file mode 100644 index 000000000..0669ee3c0 --- /dev/null +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -0,0 +1,66 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + Copyright © 2021 Pablo Escobar + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "configure.h" + +namespace Magnum { namespace Trade { namespace Test { namespace { + +struct KtxImageConverterTest: TestSuite::Tester { + explicit KtxImageConverterTest(); + + /* Explicitly forbid system-wide plugin dependencies */ + PluginManager::Manager _converterManager{"nonexistent"}; + PluginManager::Manager _importerManager{"nonexistent"}; +}; + +KtxImageConverterTest::KtxImageConverterTest() { + /* Load the plugin directly from the build tree. Otherwise it's static and + already loaded. */ + #ifdef KTXIMAGECONVERTER_PLUGIN_FILENAME + CORRADE_INTERNAL_ASSERT_OUTPUT(_converterManager.load(KTXIMAGECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + /* Optional plugins that don't have to be here */ + #ifdef KTXIMPORTER_PLUGIN_FILENAME + CORRADE_INTERNAL_ASSERT_OUTPUT(_importerManager.load(KTXIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Trade::Test::KtxImageConverterTest) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/configure.h.cmake b/src/MagnumPlugins/KtxImageConverter/Test/configure.h.cmake new file mode 100644 index 000000000..857e9bd47 --- /dev/null +++ b/src/MagnumPlugins/KtxImageConverter/Test/configure.h.cmake @@ -0,0 +1,28 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + Copyright © 2021 Pablo Escobar + + 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. +*/ + +#cmakedefine KTXIMAGECONVERTER_PLUGIN_FILENAME "${KTXIMAGECONVERTER_PLUGIN_FILENAME}" +#cmakedefine KTXIMPORTER_PLUGIN_FILENAME "${KTXIMPORTER_PLUGIN_FILENAME}" diff --git a/src/MagnumPlugins/KtxImageConverter/configure.h.cmake b/src/MagnumPlugins/KtxImageConverter/configure.h.cmake new file mode 100644 index 000000000..d07728d96 --- /dev/null +++ b/src/MagnumPlugins/KtxImageConverter/configure.h.cmake @@ -0,0 +1,27 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + Copyright © 2021 Pablo Escobar + + 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. +*/ + +#cmakedefine MAGNUM_KTXIMAGECONVERTER_BUILD_STATIC diff --git a/src/MagnumPlugins/KtxImageConverter/importStaticPlugin.cpp b/src/MagnumPlugins/KtxImageConverter/importStaticPlugin.cpp new file mode 100644 index 000000000..d07298524 --- /dev/null +++ b/src/MagnumPlugins/KtxImageConverter/importStaticPlugin.cpp @@ -0,0 +1,36 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + Copyright © 2021 Pablo Escobar + + 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 "MagnumPlugins/KtxImageConverter/configure.h" + +#ifdef MAGNUM_KTXIMAGECONVERTER_BUILD_STATIC +#include + +static int magnumKtxImageConverterStaticImporter() { + CORRADE_PLUGIN_IMPORT(KtxImageConverter) + return 1; +} CORRADE_AUTOMATIC_INITIALIZER(magnumKtxImageConverterStaticImporter) +#endif From fc2ba000d8956dfaeac1f3710e2e60f74e77f62b Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 23 Jul 2021 01:09:43 +0200 Subject: [PATCH 20/95] Ktx{Importer,ImageConverter}: register plugins --- doc/building-plugins.dox | 4 ++++ doc/cmake-plugins.dox | 3 +++ doc/namespaces.dox | 6 ++++++ modules/FindMagnumPlugins.cmake | 16 +++++++++++----- package/archlinux/PKGBUILD | 2 ++ package/archlinux/PKGBUILD-android-arm64 | 2 ++ package/archlinux/PKGBUILD-clang | 2 ++ .../archlinux/PKGBUILD-clang-addresssanitizer | 2 ++ package/archlinux/PKGBUILD-clang-threadsanitizer | 2 ++ package/archlinux/PKGBUILD-coverage | 2 ++ package/archlinux/PKGBUILD-emscripten | 2 ++ package/archlinux/PKGBUILD-emscripten-wasm | 2 ++ .../archlinux/PKGBUILD-emscripten-wasm-webgl2 | 2 ++ package/archlinux/PKGBUILD-gcc48 | 2 ++ package/archlinux/PKGBUILD-mingw-w64 | 4 ++++ package/archlinux/PKGBUILD-release | 4 ++++ package/archlinux/magnum-plugins-git/PKGBUILD | 2 ++ package/archlinux/magnum-plugins/PKGBUILD | 2 ++ package/ci/appveyor-desktop-mingw.bat | 2 ++ package/ci/appveyor-desktop.bat | 2 ++ package/ci/appveyor-rt.bat | 2 ++ package/ci/emscripten.sh | 2 ++ package/ci/travis-android-arm.sh | 2 ++ package/ci/travis-ios-simulator.sh | 2 ++ package/ci/unix-desktop.sh | 2 ++ package/debian/rules | 2 ++ .../magnum-plugins/magnum-plugins-9999.ebuild | 2 ++ package/homebrew/magnum-plugins.rb | 2 ++ package/msys/PKGBUILD | 2 ++ package/msys/magnum-plugins/PKGBUILD | 2 ++ 30 files changed, 80 insertions(+), 5 deletions(-) diff --git a/doc/building-plugins.dox b/doc/building-plugins.dox index b58d68bc4..fcf5c0a7e 100644 --- a/doc/building-plugins.dox +++ b/doc/building-plugins.dox @@ -369,6 +369,10 @@ By default no plugins are built and you need to select them manually: @ref Trade::JpegImageConverter "JpegImageConverter" plugin. - `WITH_JPEGIMPORTER` --- Build the @ref Trade::JpegImporter "JpegImporter" plugin. Depends on [libJPEG](http://libjpeg.sourceforge.net/). +- `WITH_KTXIMAGECONVERTER` --- Build the + @relativeref{Trade,KtxImageConverter} plugin. +- `WITH_KTXIMPORTER` --- Build the + @relativeref{Trade,KtxImporter} plugin. - `WITH_MESHOPTIMIZERSCENECONVERTER` --- Build the @ref Trade::MeshOptimizerSceneConverter "MeshOptimizerSceneConverter" plugin. diff --git a/doc/cmake-plugins.dox b/doc/cmake-plugins.dox index b364d8d30..b2c50b1da 100644 --- a/doc/cmake-plugins.dox +++ b/doc/cmake-plugins.dox @@ -140,6 +140,9 @@ This command will not try to find any actual plugin. The plugins are: - `JpegImageConverter` --- @ref Trade::JpegImageConverter "JpegImageConverter" plugin - `JpegImporter` --- @ref Trade::JpegImporter "JpegImporter" plugin +- `KtxImageConverter` --- @ref Trade::KtxImageConverter "KtxImageConverter" + plugin +- `KtxImporter` --- @ref Trade::KtxImporter "KtxImporter" plugin - `MeshOptimizerSceneConverter` --- @ref Trade::MeshOptimizerSceneConverter "MeshOptimizerSceneConverter" plugin diff --git a/doc/namespaces.dox b/doc/namespaces.dox index dd1c8be65..9f4c4cf99 100644 --- a/doc/namespaces.dox +++ b/doc/namespaces.dox @@ -76,6 +76,12 @@ /** @dir MagnumPlugins/JpegImporter * @brief Plugin @ref Magnum::Trade::JpegImporter */ +/** @dir MagnumPlugins/KtxImageConverter + * @brief Plugin @ref Magnum::Trade::KtxImageConverter + */ +/** @dir MagnumPlugins/KtxImporter + * @brief Plugin @ref Magnum::Trade::KtxImporter + */ /** @dir MagnumPlugins/MeshOptimizerSceneConverter * @brief Plugin @ref Magnum::Trade::MeshOptimizerSceneConverter * @m_since_{plugins,2020,06} diff --git a/modules/FindMagnumPlugins.cmake b/modules/FindMagnumPlugins.cmake index 99a265c9e..a5ed7ab9c 100644 --- a/modules/FindMagnumPlugins.cmake +++ b/modules/FindMagnumPlugins.cmake @@ -27,6 +27,8 @@ # IcoImporter - ICO importer # JpegImageConverter - JPEG image converter # JpegImporter - JPEG importer +# KtxImageConverter - KTX image converter +# KtxImporter - KTX importer # MeshOptimizerSceneConverter - MeshOptimizer scene converter # MiniExrImageConverter - OpenEXR image converter using miniexr # OpenGexImporter - OpenGEX importer @@ -144,11 +146,12 @@ set(_MAGNUMPLUGINS_PLUGIN_COMPONENTS DevIlImageImporter DrFlacAudioImporter DrMp3AudioImporter DrWavAudioImporter Faad2AudioImporter FreeTypeFont GlslangShaderConverter HarfBuzzFont IcoImporter JpegImageConverter JpegImporter - MeshOptimizerSceneConverter MiniExrImageConverter OpenExrImageConverter - OpenExrImporter OpenGexImporter PngImageConverter PngImporter - PrimitiveImporter SpirvToolsShaderConverter StanfordImporter - StanfordSceneConverter StbDxtImageConverter StbImageConverter - StbImageImporter StbTrueTypeFont StbVorbisAudioImporter StlImporter + KtxImageConverter KtxImporter MeshOptimizerSceneConverter + MiniExrImageConverter OpenExrImageConverter OpenExrImporter + OpenGexImporter PngImageConverter PngImporter PrimitiveImporter + SpirvToolsShaderConverter StanfordImporter StanfordSceneConverter + StbDxtImageConverter StbImageConverter StbImageImporter + StbTrueTypeFont StbVorbisAudioImporter StlImporter TinyGltfImporter) # Nothing is enabled by default right now set(_MAGNUMPLUGINS_IMPLICITLY_ENABLED_COMPONENTS ) @@ -384,6 +387,9 @@ foreach(_component ${MagnumPlugins_FIND_COMPONENTS}) INTERFACE_LINK_LIBRARIES ${JPEG_LIBRARIES}) endif() + # KtxImageConverter has no dependencies + # KtxImporter has no dependencies + # MeshOptimizerSceneConverter plugin dependencies elseif(_component STREQUAL MeshOptimizerSceneConverter) if(NOT TARGET meshoptimizer) diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD index 11193fcd8..18e031526 100644 --- a/package/archlinux/PKGBUILD +++ b/package/archlinux/PKGBUILD @@ -38,6 +38,8 @@ build() { -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=ON \ diff --git a/package/archlinux/PKGBUILD-android-arm64 b/package/archlinux/PKGBUILD-android-arm64 index c81226a84..7216d13a1 100644 --- a/package/archlinux/PKGBUILD-android-arm64 +++ b/package/archlinux/PKGBUILD-android-arm64 @@ -46,6 +46,8 @@ build() { -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=OFF \ -DWITH_JPEGIMPORTER=OFF \ + -DWITH_KTXIMAGECONVERTER=OFF \ + -DWITH_KTXIMPORTER=OFF \ -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=OFF \ diff --git a/package/archlinux/PKGBUILD-clang b/package/archlinux/PKGBUILD-clang index 5e2fcaacc..582c4c217 100644 --- a/package/archlinux/PKGBUILD-clang +++ b/package/archlinux/PKGBUILD-clang @@ -50,6 +50,8 @@ build() { -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=ON \ diff --git a/package/archlinux/PKGBUILD-clang-addresssanitizer b/package/archlinux/PKGBUILD-clang-addresssanitizer index 38d42bb6d..57d290074 100644 --- a/package/archlinux/PKGBUILD-clang-addresssanitizer +++ b/package/archlinux/PKGBUILD-clang-addresssanitizer @@ -40,6 +40,8 @@ build() { -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=ON \ diff --git a/package/archlinux/PKGBUILD-clang-threadsanitizer b/package/archlinux/PKGBUILD-clang-threadsanitizer index 9a8ea8a4e..fa8ce7631 100644 --- a/package/archlinux/PKGBUILD-clang-threadsanitizer +++ b/package/archlinux/PKGBUILD-clang-threadsanitizer @@ -40,6 +40,8 @@ build() { -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=ON \ diff --git a/package/archlinux/PKGBUILD-coverage b/package/archlinux/PKGBUILD-coverage index c4a358e8a..dcc3bd857 100644 --- a/package/archlinux/PKGBUILD-coverage +++ b/package/archlinux/PKGBUILD-coverage @@ -43,6 +43,8 @@ build() { -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=ON \ diff --git a/package/archlinux/PKGBUILD-emscripten b/package/archlinux/PKGBUILD-emscripten index 2603eb2a0..f8751afc3 100644 --- a/package/archlinux/PKGBUILD-emscripten +++ b/package/archlinux/PKGBUILD-emscripten @@ -42,6 +42,8 @@ build() { -DWITH_DRWAVAUDIOIMPORTER=ON \ -DWITH_FAAD2AUDIOIMPORTER=ON \ -DWITH_ICOIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=OFF \ diff --git a/package/archlinux/PKGBUILD-emscripten-wasm b/package/archlinux/PKGBUILD-emscripten-wasm index 619aeb00a..3743bfb3f 100644 --- a/package/archlinux/PKGBUILD-emscripten-wasm +++ b/package/archlinux/PKGBUILD-emscripten-wasm @@ -41,6 +41,8 @@ build() { -DWITH_DRWAVAUDIOIMPORTER=ON \ -DWITH_FAAD2AUDIOIMPORTER=ON \ -DWITH_ICOIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=OFF \ diff --git a/package/archlinux/PKGBUILD-emscripten-wasm-webgl2 b/package/archlinux/PKGBUILD-emscripten-wasm-webgl2 index c7b91f8cb..cd224c540 100644 --- a/package/archlinux/PKGBUILD-emscripten-wasm-webgl2 +++ b/package/archlinux/PKGBUILD-emscripten-wasm-webgl2 @@ -41,6 +41,8 @@ build() { -DWITH_DRWAVAUDIOIMPORTER=ON \ -DWITH_FAAD2AUDIOIMPORTER=ON \ -DWITH_ICOIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=OFF \ diff --git a/package/archlinux/PKGBUILD-gcc48 b/package/archlinux/PKGBUILD-gcc48 index e041e3075..8484eea6b 100644 --- a/package/archlinux/PKGBUILD-gcc48 +++ b/package/archlinux/PKGBUILD-gcc48 @@ -50,6 +50,8 @@ build() { -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=ON \ diff --git a/package/archlinux/PKGBUILD-mingw-w64 b/package/archlinux/PKGBUILD-mingw-w64 index ef6a8bc2f..73f4926b2 100644 --- a/package/archlinux/PKGBUILD-mingw-w64 +++ b/package/archlinux/PKGBUILD-mingw-w64 @@ -35,6 +35,8 @@ build() { -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=OFF \ @@ -82,6 +84,8 @@ build() { -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=OFF \ diff --git a/package/archlinux/PKGBUILD-release b/package/archlinux/PKGBUILD-release index 48f802d11..7b1e4e5cf 100644 --- a/package/archlinux/PKGBUILD-release +++ b/package/archlinux/PKGBUILD-release @@ -38,6 +38,8 @@ build() { -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=ON \ @@ -82,6 +84,8 @@ build() { -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=ON \ diff --git a/package/archlinux/magnum-plugins-git/PKGBUILD b/package/archlinux/magnum-plugins-git/PKGBUILD index 68fff3f80..7876abe25 100644 --- a/package/archlinux/magnum-plugins-git/PKGBUILD +++ b/package/archlinux/magnum-plugins-git/PKGBUILD @@ -47,6 +47,8 @@ build() { -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=ON \ diff --git a/package/archlinux/magnum-plugins/PKGBUILD b/package/archlinux/magnum-plugins/PKGBUILD index 07d8cf95f..ade70ec46 100644 --- a/package/archlinux/magnum-plugins/PKGBUILD +++ b/package/archlinux/magnum-plugins/PKGBUILD @@ -50,6 +50,8 @@ build() { -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=ON \ diff --git a/package/ci/appveyor-desktop-mingw.bat b/package/ci/appveyor-desktop-mingw.bat index c4b596eb6..37bd98f48 100644 --- a/package/ci/appveyor-desktop-mingw.bat +++ b/package/ci/appveyor-desktop-mingw.bat @@ -121,6 +121,8 @@ cmake .. ^ -DWITH_ICOIMPORTER=ON ^ -DWITH_JPEGIMAGECONVERTER=ON ^ -DWITH_JPEGIMPORTER=ON ^ + -DWITH_KTXIMAGECONVERTER=ON ^ + -DWITH_KTXIMPORTER=ON ^ -DWITH_MESHOPTIMIZERSCENECONVERTER=ON ^ -DWITH_MINIEXRIMAGECONVERTER=ON ^ -DWITH_OPENEXRIMAGECONVERTER=ON ^ diff --git a/package/ci/appveyor-desktop.bat b/package/ci/appveyor-desktop.bat index a7b4d572d..ff0fd4573 100644 --- a/package/ci/appveyor-desktop.bat +++ b/package/ci/appveyor-desktop.bat @@ -99,6 +99,8 @@ cmake .. ^ -DWITH_ICOIMPORTER=ON ^ -DWITH_JPEGIMAGECONVERTER=%EXCEPT_MSVC2015% ^ -DWITH_JPEGIMPORTER=%EXCEPT_MSVC2015% ^ + -DWITH_KTXIMAGECONVERTER=ON ^ + -DWITH_KTXIMPORTER=ON ^ -DWITH_MESHOPTIMIZERSCENECONVERTER=ON ^ -DWITH_MINIEXRIMAGECONVERTER=ON ^ -DWITH_OPENEXRIMAGECONVERTER=%EXCEPT_MSVC2015% ^ diff --git a/package/ci/appveyor-rt.bat b/package/ci/appveyor-rt.bat index 1d5ed51a1..a0a856013 100644 --- a/package/ci/appveyor-rt.bat +++ b/package/ci/appveyor-rt.bat @@ -78,6 +78,8 @@ cmake .. ^ -DWITH_ICOIMPORTER=ON ^ -DWITH_JPEGIMAGECONVERTER=OFF ^ -DWITH_JPEGIMPORTER=OFF ^ + -DWITH_KTXIMAGECONVERTER=ON ^ + -DWITH_KTXIMPORTER=ON ^ -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF ^ -DWITH_MINIEXRIMAGECONVERTER=ON ^ -DWITH_OPENEXRIMAGECONVERTER=OFF ^ diff --git a/package/ci/emscripten.sh b/package/ci/emscripten.sh index dd315ad82..e0e6fd5d4 100755 --- a/package/ci/emscripten.sh +++ b/package/ci/emscripten.sh @@ -97,6 +97,8 @@ cmake .. \ -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=OFF \ -DWITH_JPEGIMPORTER=OFF \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=OFF \ diff --git a/package/ci/travis-android-arm.sh b/package/ci/travis-android-arm.sh index 821078526..6dcb26d7f 100755 --- a/package/ci/travis-android-arm.sh +++ b/package/ci/travis-android-arm.sh @@ -91,6 +91,8 @@ cmake .. \ -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=OFF \ -DWITH_JPEGIMPORTER=OFF \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=OFF \ diff --git a/package/ci/travis-ios-simulator.sh b/package/ci/travis-ios-simulator.sh index 9fbcf4e51..a243ca900 100755 --- a/package/ci/travis-ios-simulator.sh +++ b/package/ci/travis-ios-simulator.sh @@ -83,6 +83,8 @@ cmake .. \ -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=OFF \ -DWITH_JPEGIMPORTER=OFF \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=OFF \ diff --git a/package/ci/unix-desktop.sh b/package/ci/unix-desktop.sh index 633b468fa..bacf81407 100755 --- a/package/ci/unix-desktop.sh +++ b/package/ci/unix-desktop.sh @@ -67,6 +67,8 @@ cmake .. \ -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=ON \ diff --git a/package/debian/rules b/package/debian/rules index edbd9fd01..ce9737802 100755 --- a/package/debian/rules +++ b/package/debian/rules @@ -29,6 +29,8 @@ override_dh_auto_configure: -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=ON \ diff --git a/package/gentoo/dev-libs/magnum-plugins/magnum-plugins-9999.ebuild b/package/gentoo/dev-libs/magnum-plugins/magnum-plugins-9999.ebuild index df460fcfb..b873200a2 100644 --- a/package/gentoo/dev-libs/magnum-plugins/magnum-plugins-9999.ebuild +++ b/package/gentoo/dev-libs/magnum-plugins/magnum-plugins-9999.ebuild @@ -47,6 +47,8 @@ src_configure() { -DWITH_ICOIMPORTER=ON -DWITH_JPEGIMAGECONVERTER=ON -DWITH_JPEGIMPORTER=ON + -DWITH_KTXIMAGECONVERTER=ON + -DWITH_KTXIMPORTER=ON -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF -DWITH_MINIEXRIMAGECONVERTER=ON -DWITH_OPENEXRIMAGECONVERTER=ON diff --git a/package/homebrew/magnum-plugins.rb b/package/homebrew/magnum-plugins.rb index 38e167d64..4e9b7f11d 100644 --- a/package/homebrew/magnum-plugins.rb +++ b/package/homebrew/magnum-plugins.rb @@ -64,6 +64,8 @@ def install "-DWITH_HARFBUZZFONT=#{(build.with? 'harfbuzz') ? 'ON' : 'OFF'}", "-DWITH_JPEGIMAGECONVERTER=#{(build.with? 'jpeg') ? 'ON' : 'OFF'}", "-DWITH_JPEGIMPORTER=#{(build.with? 'jpeg') ? 'ON' : 'OFF'}", + "-DWITH_KTXIMAGECONVERTER=ON", + "-DWITH_KTXIMAGEIMPORTER=ON", "-DWITH_MESHOPTIMIZERSCENECONVERTER=ON", "-DWITH_MINIEXRIMAGECONVERTER=ON", "-DWITH_OPENEXRIMAGECONVERTER=#{(build.with? 'openexr') ? 'ON' : 'OFF'}", diff --git a/package/msys/PKGBUILD b/package/msys/PKGBUILD index d75f14b9c..f85712257 100644 --- a/package/msys/PKGBUILD +++ b/package/msys/PKGBUILD @@ -51,6 +51,8 @@ build() { -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=ON \ diff --git a/package/msys/magnum-plugins/PKGBUILD b/package/msys/magnum-plugins/PKGBUILD index 1ef5a0d22..0d2c688dd 100644 --- a/package/msys/magnum-plugins/PKGBUILD +++ b/package/msys/magnum-plugins/PKGBUILD @@ -58,6 +58,8 @@ build() { -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=ON \ From cd3a6fccb6d1d97c6e71477d5bb2f6ed43127749 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 23 Jul 2021 01:36:00 +0200 Subject: [PATCH 21/95] KtxImporter: oops --- package/debian/rules | 4 ++-- .../magnum-plugins/magnum-plugins-9999.ebuild | 4 ++-- .../KtxImageConverter/KtxImageConverter.cpp | 6 +++--- .../KtxImageConverter/Test/CMakeLists.txt | 2 +- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 3 +-- .../KtxImporter/Test/CMakeLists.txt | 19 +++++++++++++++++-- .../KtxImporter/Test/KtxImporterTest.cpp | 10 +++++----- 7 files changed, 31 insertions(+), 17 deletions(-) diff --git a/package/debian/rules b/package/debian/rules index ce9737802..c2c308ace 100755 --- a/package/debian/rules +++ b/package/debian/rules @@ -29,8 +29,8 @@ override_dh_auto_configure: -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ - -DWITH_KTXIMAGECONVERTER=ON \ - -DWITH_KTXIMPORTER=ON \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=ON \ diff --git a/package/gentoo/dev-libs/magnum-plugins/magnum-plugins-9999.ebuild b/package/gentoo/dev-libs/magnum-plugins/magnum-plugins-9999.ebuild index b873200a2..86a994c8d 100644 --- a/package/gentoo/dev-libs/magnum-plugins/magnum-plugins-9999.ebuild +++ b/package/gentoo/dev-libs/magnum-plugins/magnum-plugins-9999.ebuild @@ -47,8 +47,8 @@ src_configure() { -DWITH_ICOIMPORTER=ON -DWITH_JPEGIMAGECONVERTER=ON -DWITH_JPEGIMPORTER=ON - -DWITH_KTXIMAGECONVERTER=ON - -DWITH_KTXIMPORTER=ON + -DWITH_KTXIMAGECONVERTER=ON + -DWITH_KTXIMPORTER=ON -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF -DWITH_MINIEXRIMAGECONVERTER=ON -DWITH_OPENEXRIMAGECONVERTER=ON diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 023a96b71..33270eb9c 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -27,8 +27,8 @@ #include "KtxImageConverter.h" #include -#include #include +#include #include #include #include @@ -172,7 +172,7 @@ UnsignedByte channelFormat(Implementation::VkFormatSuffix suffix, Implementation : Implementation::KdfBasicBlockSample::ChannelFormat{}; } - CORRADE_ASSERT_UNREACHABLE("channelFormat(): invalid format suffix" << suffix, {}); + CORRADE_ASSERT_UNREACHABLE("channelFormat(): invalid format suffix" << UnsignedInt(suffix), {}); } Containers::Array2 channelMapping(Implementation::VkFormatSuffix suffix, UnsignedByte typeSize) { @@ -214,7 +214,7 @@ Containers::Array2 channelMapping(Implementation::VkFormatSuffix su return {Corrade::Utility::bitCast(-1.0f), Corrade::Utility::bitCast(1.0f)}; } - CORRADE_ASSERT_UNREACHABLE("channelLimits(): invalid format suffix" << suffix, {}); + CORRADE_ASSERT_UNREACHABLE("channelLimits(): invalid format suffix" << UnsignedInt(suffix), {}); } bool isSrgb(PixelFormat format) { diff --git a/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt b/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt index 7dd83d282..0b5d7378f 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt @@ -42,7 +42,7 @@ file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) corrade_add_test(KtxImageConverterTest KtxImageConverterTest.cpp - LIBRARIES MagnumTrade) + LIBRARIES Magnum::Trade) target_include_directories(KtxImageConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) if(MAGNUM_KTXIMAGECONVERTER_BUILD_STATIC) target_link_libraries(KtxImageConverterTest PRIVATE KtxImageConverter) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 4460cd4ab..955f82360 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -45,7 +45,6 @@ #include #include #include -#include #include "MagnumPlugins/KtxImporter/KtxHeader.h" namespace Magnum { namespace Trade { @@ -613,7 +612,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { if(f->pixelFormat.isCompressed) { /* Block size */ const Vector4i expected = Vector4i::pad(compressedBlockSize(f->pixelFormat.compressed), 1); - const Vector4i actual{Math::max(Vector4ub::from(block.texelBlockDimension), {1})}; + const Vector4i actual{Math::max(Vector4ub::from(block.texelBlockDimension), UnsignedByte(1))}; valid = valid && actual == expected; } else { /* Pixel size. For supercompressed data, bytePlanes is all diff --git a/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt b/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt index be0f649bf..3100f5322 100644 --- a/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt @@ -44,8 +44,23 @@ file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) corrade_add_test(KtxImporterTest KtxImporterTest.cpp - LIBRARIES MagnumTrade - FILES file.tga) + LIBRARIES Magnum::Trade + FILES + 1d.ktx2 + 1d-mipmaps.ktx2 + bgr.ktx2 + bgra.ktx2 + bgra-swizzle-bgra.ktx2 + bgr-swizzle-bgr.ktx2 + orientation-empty.ktx2 + rgb.ktx2 + rgba.ktx2 + rgb-mipmaps.ktx2 + swizzle-bgr.ktx2 + swizzle-bgra.ktx2 + swizzle-identity.ktx2 + swizzle-unsupported.ktx2 + version1.ktx) target_include_directories(KtxImporterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$ ${PROJECT_SOURCE_DIR}/src) diff --git a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp index efef7d55e..d1de19221 100644 --- a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp +++ b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp @@ -112,14 +112,14 @@ constexpr Color3ub Black{0}; constexpr Color3ub White{0xff}; constexpr Color3ub Purple{0x7f, 0, 0x7f}; -constexpr Color3ub PatternRgb2DData[3][4]{ +const Color3ub PatternRgb2DData[3][4]{ /* Origin bottom-left */ {Color3ub::red(), White, Black, Color3ub::green()}, {White, Color3ub::red(), Black, Color3ub::green()}, {Color3ub::blue(), Color3ub::green(), Purple, Purple} }; -constexpr Color4ub PatternRgba2DData[3][4]{ +const Color4ub PatternRgba2DData[3][4] = { {PatternRgb2DData[0][0], PatternRgb2DData[0][1], PatternRgb2DData[0][2], PatternRgb2DData[0][3]}, {PatternRgb2DData[1][0], PatternRgb2DData[1][1], PatternRgb2DData[1][2], PatternRgb2DData[1][3]}, {PatternRgb2DData[2][0], PatternRgb2DData[2][1], PatternRgb2DData[2][2], PatternRgb2DData[2][3]} @@ -252,12 +252,12 @@ const struct { {"BGRA8 format", "bgra.ktx2", PixelFormat::RGBA8Srgb, Implementation::VK_FORMAT_B8G8R8A8_SRGB, "format requires conversion from BGRA to RGBA", Containers::arrayCast(PatternRgba2DData)}, + {"BGR8 format+header cancel", "swizzle-bgr.ktx2", + PixelFormat::RGB8Srgb, Implementation::VK_FORMAT_B8G8R8_SRGB, + nullptr, Containers::arrayCast(PatternRgb2DData)}, {"BGRA8 format+header cancel", "swizzle-bgra.ktx2", PixelFormat::RGBA8Srgb, Implementation::VK_FORMAT_B8G8R8A8_SRGB, nullptr, Containers::arrayCast(PatternRgba2DData)}, - {"BGR8 format+header cancel", "swizzle-bgr.ktx2", - PixelFormat::RGB8Srgb, Implementation::VK_FORMAT_B8G8R8_SRGB, - nullptr, Containers::arrayCast(PatternRgb2DData)} /** @todo Check swizzle of larger formats */ }; From ca92b026662b7363f51fb47390a565f2dd202280 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 28 Jul 2021 18:38:08 +0200 Subject: [PATCH 22/95] KtxImporter: dereference VkFormat values in formatMapping.hpp This means we can remove most of VkFormat from KtxHeader.h, except for the swizzled RGB(A) formats we have to detect manually during import --- src/MagnumPlugins/KtxImporter/KtxHeader.h | 246 +--------------- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 9 +- .../KtxImporter/Test/KtxImporterTest.cpp | 53 +++- .../KtxImporter/formatMapping.hpp | 262 +++++++++--------- .../KtxImporter/formatMapping.py | 30 +- 5 files changed, 208 insertions(+), 392 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxHeader.h b/src/MagnumPlugins/KtxImporter/KtxHeader.h index 1b981129a..15d62c34b 100644 --- a/src/MagnumPlugins/KtxImporter/KtxHeader.h +++ b/src/MagnumPlugins/KtxImporter/KtxHeader.h @@ -35,255 +35,23 @@ namespace Magnum { namespace Trade { namespace Implementation { -/* Taken from magnum/src/MagnumExternal/Vulkan/flextVk.h - (commit 9d4a8b49943a084cff64550792bb2eba223e0e03) - - Contains all formats from: - - 1.2 core (without KHR aliases) - - EXT_texture_compression_astc_hdr - - IMG_format_pvrtc */ -enum VkFormat : UnsignedInt { +typedef UnsignedInt VkFormat; + +/* Selected Vulkan 1.0 formats for detecting implicit swizzling to PixelFormat. + VkFormat is UnsignedInt instead of this enum to prevent warnings when using + arbitrary numeric values from formatMapping.hpp in switches. */ +enum : UnsignedInt { VK_FORMAT_UNDEFINED = 0, - VK_FORMAT_R4G4_UNORM_PACK8 = 1, - VK_FORMAT_R4G4B4A4_UNORM_PACK16 = 2, - VK_FORMAT_B4G4R4A4_UNORM_PACK16 = 3, - VK_FORMAT_R5G6B5_UNORM_PACK16 = 4, - VK_FORMAT_B5G6R5_UNORM_PACK16 = 5, - VK_FORMAT_R5G5B5A1_UNORM_PACK16 = 6, - VK_FORMAT_B5G5R5A1_UNORM_PACK16 = 7, - VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8, - VK_FORMAT_R8_UNORM = 9, - VK_FORMAT_R8_SNORM = 10, - VK_FORMAT_R8_USCALED = 11, - VK_FORMAT_R8_SSCALED = 12, - VK_FORMAT_R8_UINT = 13, - VK_FORMAT_R8_SINT = 14, - VK_FORMAT_R8_SRGB = 15, - VK_FORMAT_R8G8_UNORM = 16, - VK_FORMAT_R8G8_SNORM = 17, - VK_FORMAT_R8G8_USCALED = 18, - VK_FORMAT_R8G8_SSCALED = 19, - VK_FORMAT_R8G8_UINT = 20, - VK_FORMAT_R8G8_SINT = 21, - VK_FORMAT_R8G8_SRGB = 22, - VK_FORMAT_R8G8B8_UNORM = 23, - VK_FORMAT_R8G8B8_SNORM = 24, - VK_FORMAT_R8G8B8_USCALED = 25, - VK_FORMAT_R8G8B8_SSCALED = 26, - VK_FORMAT_R8G8B8_UINT = 27, - VK_FORMAT_R8G8B8_SINT = 28, - VK_FORMAT_R8G8B8_SRGB = 29, VK_FORMAT_B8G8R8_UNORM = 30, VK_FORMAT_B8G8R8_SNORM = 31, - VK_FORMAT_B8G8R8_USCALED = 32, - VK_FORMAT_B8G8R8_SSCALED = 33, VK_FORMAT_B8G8R8_UINT = 34, VK_FORMAT_B8G8R8_SINT = 35, VK_FORMAT_B8G8R8_SRGB = 36, - VK_FORMAT_R8G8B8A8_UNORM = 37, - VK_FORMAT_R8G8B8A8_SNORM = 38, - VK_FORMAT_R8G8B8A8_USCALED = 39, - VK_FORMAT_R8G8B8A8_SSCALED = 40, - VK_FORMAT_R8G8B8A8_UINT = 41, - VK_FORMAT_R8G8B8A8_SINT = 42, - VK_FORMAT_R8G8B8A8_SRGB = 43, VK_FORMAT_B8G8R8A8_UNORM = 44, VK_FORMAT_B8G8R8A8_SNORM = 45, - VK_FORMAT_B8G8R8A8_USCALED = 46, - VK_FORMAT_B8G8R8A8_SSCALED = 47, VK_FORMAT_B8G8R8A8_UINT = 48, VK_FORMAT_B8G8R8A8_SINT = 49, - VK_FORMAT_B8G8R8A8_SRGB = 50, - VK_FORMAT_A8B8G8R8_UNORM_PACK32 = 51, - VK_FORMAT_A8B8G8R8_SNORM_PACK32 = 52, - VK_FORMAT_A8B8G8R8_USCALED_PACK32 = 53, - VK_FORMAT_A8B8G8R8_SSCALED_PACK32 = 54, - VK_FORMAT_A8B8G8R8_UINT_PACK32 = 55, - VK_FORMAT_A8B8G8R8_SINT_PACK32 = 56, - VK_FORMAT_A8B8G8R8_SRGB_PACK32 = 57, - VK_FORMAT_A2R10G10B10_UNORM_PACK32 = 58, - VK_FORMAT_A2R10G10B10_SNORM_PACK32 = 59, - VK_FORMAT_A2R10G10B10_USCALED_PACK32 = 60, - VK_FORMAT_A2R10G10B10_SSCALED_PACK32 = 61, - VK_FORMAT_A2R10G10B10_UINT_PACK32 = 62, - VK_FORMAT_A2R10G10B10_SINT_PACK32 = 63, - VK_FORMAT_A2B10G10R10_UNORM_PACK32 = 64, - VK_FORMAT_A2B10G10R10_SNORM_PACK32 = 65, - VK_FORMAT_A2B10G10R10_USCALED_PACK32 = 66, - VK_FORMAT_A2B10G10R10_SSCALED_PACK32 = 67, - VK_FORMAT_A2B10G10R10_UINT_PACK32 = 68, - VK_FORMAT_A2B10G10R10_SINT_PACK32 = 69, - VK_FORMAT_R16_UNORM = 70, - VK_FORMAT_R16_SNORM = 71, - VK_FORMAT_R16_USCALED = 72, - VK_FORMAT_R16_SSCALED = 73, - VK_FORMAT_R16_UINT = 74, - VK_FORMAT_R16_SINT = 75, - VK_FORMAT_R16_SFLOAT = 76, - VK_FORMAT_R16G16_UNORM = 77, - VK_FORMAT_R16G16_SNORM = 78, - VK_FORMAT_R16G16_USCALED = 79, - VK_FORMAT_R16G16_SSCALED = 80, - VK_FORMAT_R16G16_UINT = 81, - VK_FORMAT_R16G16_SINT = 82, - VK_FORMAT_R16G16_SFLOAT = 83, - VK_FORMAT_R16G16B16_UNORM = 84, - VK_FORMAT_R16G16B16_SNORM = 85, - VK_FORMAT_R16G16B16_USCALED = 86, - VK_FORMAT_R16G16B16_SSCALED = 87, - VK_FORMAT_R16G16B16_UINT = 88, - VK_FORMAT_R16G16B16_SINT = 89, - VK_FORMAT_R16G16B16_SFLOAT = 90, - VK_FORMAT_R16G16B16A16_UNORM = 91, - VK_FORMAT_R16G16B16A16_SNORM = 92, - VK_FORMAT_R16G16B16A16_USCALED = 93, - VK_FORMAT_R16G16B16A16_SSCALED = 94, - VK_FORMAT_R16G16B16A16_UINT = 95, - VK_FORMAT_R16G16B16A16_SINT = 96, - VK_FORMAT_R16G16B16A16_SFLOAT = 97, - VK_FORMAT_R32_UINT = 98, - VK_FORMAT_R32_SINT = 99, - VK_FORMAT_R32_SFLOAT = 100, - VK_FORMAT_R32G32_UINT = 101, - VK_FORMAT_R32G32_SINT = 102, - VK_FORMAT_R32G32_SFLOAT = 103, - VK_FORMAT_R32G32B32_UINT = 104, - VK_FORMAT_R32G32B32_SINT = 105, - VK_FORMAT_R32G32B32_SFLOAT = 106, - VK_FORMAT_R32G32B32A32_UINT = 107, - VK_FORMAT_R32G32B32A32_SINT = 108, - VK_FORMAT_R32G32B32A32_SFLOAT = 109, - VK_FORMAT_R64_UINT = 110, - VK_FORMAT_R64_SINT = 111, - VK_FORMAT_R64_SFLOAT = 112, - VK_FORMAT_R64G64_UINT = 113, - VK_FORMAT_R64G64_SINT = 114, - VK_FORMAT_R64G64_SFLOAT = 115, - VK_FORMAT_R64G64B64_UINT = 116, - VK_FORMAT_R64G64B64_SINT = 117, - VK_FORMAT_R64G64B64_SFLOAT = 118, - VK_FORMAT_R64G64B64A64_UINT = 119, - VK_FORMAT_R64G64B64A64_SINT = 120, - VK_FORMAT_R64G64B64A64_SFLOAT = 121, - VK_FORMAT_B10G11R11_UFLOAT_PACK32 = 122, - VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123, - VK_FORMAT_D16_UNORM = 124, - VK_FORMAT_X8_D24_UNORM_PACK32 = 125, - VK_FORMAT_D32_SFLOAT = 126, - VK_FORMAT_S8_UINT = 127, - VK_FORMAT_D16_UNORM_S8_UINT = 128, - VK_FORMAT_D24_UNORM_S8_UINT = 129, - VK_FORMAT_D32_SFLOAT_S8_UINT = 130, - VK_FORMAT_BC1_RGB_UNORM_BLOCK = 131, - VK_FORMAT_BC1_RGB_SRGB_BLOCK = 132, - VK_FORMAT_BC1_RGBA_UNORM_BLOCK = 133, - VK_FORMAT_BC1_RGBA_SRGB_BLOCK = 134, - VK_FORMAT_BC2_UNORM_BLOCK = 135, - VK_FORMAT_BC2_SRGB_BLOCK = 136, - VK_FORMAT_BC3_UNORM_BLOCK = 137, - VK_FORMAT_BC3_SRGB_BLOCK = 138, - VK_FORMAT_BC4_UNORM_BLOCK = 139, - VK_FORMAT_BC4_SNORM_BLOCK = 140, - VK_FORMAT_BC5_UNORM_BLOCK = 141, - VK_FORMAT_BC5_SNORM_BLOCK = 142, - VK_FORMAT_BC6H_UFLOAT_BLOCK = 143, - VK_FORMAT_BC6H_SFLOAT_BLOCK = 144, - VK_FORMAT_BC7_UNORM_BLOCK = 145, - VK_FORMAT_BC7_SRGB_BLOCK = 146, - VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK = 147, - VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK = 148, - VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK = 149, - VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK = 150, - VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK = 151, - VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK = 152, - VK_FORMAT_EAC_R11_UNORM_BLOCK = 153, - VK_FORMAT_EAC_R11_SNORM_BLOCK = 154, - VK_FORMAT_EAC_R11G11_UNORM_BLOCK = 155, - VK_FORMAT_EAC_R11G11_SNORM_BLOCK = 156, - VK_FORMAT_ASTC_4x4_UNORM_BLOCK = 157, - VK_FORMAT_ASTC_4x4_SRGB_BLOCK = 158, - VK_FORMAT_ASTC_5x4_UNORM_BLOCK = 159, - VK_FORMAT_ASTC_5x4_SRGB_BLOCK = 160, - VK_FORMAT_ASTC_5x5_UNORM_BLOCK = 161, - VK_FORMAT_ASTC_5x5_SRGB_BLOCK = 162, - VK_FORMAT_ASTC_6x5_UNORM_BLOCK = 163, - VK_FORMAT_ASTC_6x5_SRGB_BLOCK = 164, - VK_FORMAT_ASTC_6x6_UNORM_BLOCK = 165, - VK_FORMAT_ASTC_6x6_SRGB_BLOCK = 166, - VK_FORMAT_ASTC_8x5_UNORM_BLOCK = 167, - VK_FORMAT_ASTC_8x5_SRGB_BLOCK = 168, - VK_FORMAT_ASTC_8x6_UNORM_BLOCK = 169, - VK_FORMAT_ASTC_8x6_SRGB_BLOCK = 170, - VK_FORMAT_ASTC_8x8_UNORM_BLOCK = 171, - VK_FORMAT_ASTC_8x8_SRGB_BLOCK = 172, - VK_FORMAT_ASTC_10x5_UNORM_BLOCK = 173, - VK_FORMAT_ASTC_10x5_SRGB_BLOCK = 174, - VK_FORMAT_ASTC_10x6_UNORM_BLOCK = 175, - VK_FORMAT_ASTC_10x6_SRGB_BLOCK = 176, - VK_FORMAT_ASTC_10x8_UNORM_BLOCK = 177, - VK_FORMAT_ASTC_10x8_SRGB_BLOCK = 178, - VK_FORMAT_ASTC_10x10_UNORM_BLOCK = 179, - VK_FORMAT_ASTC_10x10_SRGB_BLOCK = 180, - VK_FORMAT_ASTC_12x10_UNORM_BLOCK = 181, - VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182, - VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183, - VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184, - VK_FORMAT_G8B8G8R8_422_UNORM = 1000156000, - VK_FORMAT_B8G8R8G8_422_UNORM = 1000156001, - VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM = 1000156002, - VK_FORMAT_G8_B8R8_2PLANE_420_UNORM = 1000156003, - VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM = 1000156004, - VK_FORMAT_G8_B8R8_2PLANE_422_UNORM = 1000156005, - VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM = 1000156006, - VK_FORMAT_R10X6_UNORM_PACK16 = 1000156007, - VK_FORMAT_R10X6G10X6_UNORM_2PACK16 = 1000156008, - VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16 = 1000156009, - VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16 = 1000156010, - VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16 = 1000156011, - VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16 = 1000156012, - VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16 = 1000156013, - VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16 = 1000156014, - VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16 = 1000156015, - VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16 = 1000156016, - VK_FORMAT_R12X4_UNORM_PACK16 = 1000156017, - VK_FORMAT_R12X4G12X4_UNORM_2PACK16 = 1000156018, - VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16 = 1000156019, - VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16 = 1000156020, - VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16 = 1000156021, - VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16 = 1000156022, - VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16 = 1000156023, - VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16 = 1000156024, - VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16 = 1000156025, - VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16 = 1000156026, - VK_FORMAT_G16B16G16R16_422_UNORM = 1000156027, - VK_FORMAT_B16G16R16G16_422_UNORM = 1000156028, - VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM = 1000156029, - VK_FORMAT_G16_B16R16_2PLANE_420_UNORM = 1000156030, - VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM = 1000156031, - VK_FORMAT_G16_B16R16_2PLANE_422_UNORM = 1000156032, - VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM = 1000156033, - VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT = 1000066000, - VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT = 1000066001, - VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT = 1000066002, - VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT = 1000066003, - VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT = 1000066004, - VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT = 1000066005, - VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT = 1000066006, - VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT = 1000066007, - VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT = 1000066008, - VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT = 1000066009, - VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT = 1000066010, - VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT = 1000066011, - VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT = 1000066012, - VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT = 1000066013, - VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000, - VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001, - VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002, - VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003, - VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004, - VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005, - VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006, - VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007 + VK_FORMAT_B8G8R8A8_SRGB = 50 }; enum VkFormatSuffix : UnsignedByte { diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 955f82360..b7fedbd31 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -341,7 +341,7 @@ bool KtxImporter::File::Format::decode(Implementation::VkFormat vkFormat) { formatMapping.hpp isn't updated without adding an extra check. */ PixelFormat format{}; switch(vkFormat) { - #define _p(vulkan, magnum, _type) case Implementation::VK_FORMAT_ ## vulkan: format = PixelFormat::magnum; break; + #define _p(vulkan, magnum, _type) case Implementation::VkFormat(vulkan): format = PixelFormat::magnum; break; #include "MagnumPlugins/KtxImporter/formatMapping.hpp" #undef _p default: @@ -396,9 +396,14 @@ bool KtxImporter::File::Format::decode(Implementation::VkFormat vkFormat) { } /* Find block-compressed pixel format, no swizzling possible */ + /** @todo KTX supports 3D ASTC formats through an unreleased extension. + Supposedly the enum values won't change so we could manually map + them, although it'd be easier if Magnum did this. + See https://github.com/KhronosGroup/KTX-Specification/pull/97 and + https://github.com/KhronosGroup/KTX-Software/blob/f99221eb1c5ad92fd859765a0c66517ea4059160/lib/dfdutils/vulkan/vulkan_core.h#L1061*/ CompressedPixelFormat compressedFormat{}; switch(vkFormat) { - #define _c(vulkan, magnum, _type) case Implementation::VK_FORMAT_ ## vulkan: compressedFormat = CompressedPixelFormat::magnum; break; + #define _c(vulkan, magnum, _type) case Implementation::VkFormat(vulkan): compressedFormat = CompressedPixelFormat::magnum; break; #include "MagnumPlugins/KtxImporter/formatMapping.hpp" #undef _c default: diff --git a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp index d1de19221..c04fa415b 100644 --- a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp +++ b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp @@ -241,10 +241,10 @@ const struct { const Containers::ArrayView data; } SwizzleData[] { {"BGR8 header", "bgr-swizzle-bgr.ktx2", - PixelFormat::RGB8Srgb, Implementation::VK_FORMAT_R8G8B8_SRGB, /* Unchanged */ + PixelFormat::RGB8Srgb, Implementation::VK_FORMAT_UNDEFINED, "format requires conversion from BGR to RGB", Containers::arrayCast(PatternRgb2DData)}, {"BGRA8 header", "bgra-swizzle-bgra.ktx2", - PixelFormat::RGBA8Srgb, Implementation::VK_FORMAT_R8G8B8A8_SRGB, /* Unchanged */ + PixelFormat::RGBA8Srgb, Implementation::VK_FORMAT_UNDEFINED, "format requires conversion from BGRA to RGBA", Containers::arrayCast(PatternRgba2DData)}, {"BGR8 format", "bgr.ktx2", PixelFormat::RGB8Srgb, Implementation::VK_FORMAT_B8G8R8_SRGB, @@ -382,20 +382,43 @@ void KtxImporterTest::invalidFormat() { Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); + /* Implementation::VkFormat only contains swizzled 8-bit formats. Taken + from magnum/src/MagnumExternal/Vulkan/flextVk.h (9d4a8b49943a084cff64550792bb2eba223e0e03) */ + enum VkFormat : UnsignedInt { + VK_FORMAT_R4G4_UNORM_PACK8 = 1, + VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8, + VK_FORMAT_R8_USCALED = 11, + VK_FORMAT_R16_SSCALED = 73, + VK_FORMAT_R64_UINT = 110, + VK_FORMAT_R64G64B64A64_SFLOAT = 121, + VK_FORMAT_G8B8G8R8_422_UNORM = 1000156000, + VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM = 1000156002, + VK_FORMAT_R10X6G10X6_UNORM_2PACK16 = 1000156008, + VK_FORMAT_G16B16G16R16_422_UNORM = 1000156027, + VK_FORMAT_B16G16R16G16_422_UNORM = 1000156028, + VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM = 1000156029, + VK_FORMAT_G16_B16R16_2PLANE_420_UNORM = 1000156030, + VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM = 1000156031, + VK_FORMAT_G16_B16R16_2PLANE_422_UNORM = 1000156032, + VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM = 1000156033, + VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006, + }; + constexpr Implementation::VkFormat formats[]{ /* Not allowed by KTX. All of the unsupported formats happen to not be supported by Magnum, either. */ - Implementation::VK_FORMAT_R4G4_UNORM_PACK8, - Implementation::VK_FORMAT_A1R5G5B5_UNORM_PACK16, - Implementation::VK_FORMAT_R8_USCALED, - Implementation::VK_FORMAT_R16_SSCALED, - Implementation::VK_FORMAT_G8B8G8R8_422_UNORM, - Implementation::VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, - Implementation::VK_FORMAT_R10X6G10X6_UNORM_2PACK16, - Implementation::VK_FORMAT_G16B16G16R16_422_UNORM, + VK_FORMAT_R4G4_UNORM_PACK8, + VK_FORMAT_A1R5G5B5_UNORM_PACK16, + VK_FORMAT_R8_USCALED, + VK_FORMAT_R16_SSCALED, + VK_FORMAT_G8B8G8R8_422_UNORM, + VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, + VK_FORMAT_R10X6G10X6_UNORM_2PACK16, + VK_FORMAT_G16B16G16R16_422_UNORM, /* Not supported by Magnum */ - Implementation::VK_FORMAT_R64G64B64A64_SFLOAT, - Implementation::VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG + VK_FORMAT_R64_UINT, + VK_FORMAT_R64G64B64A64_SFLOAT, + VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG }; std::ostringstream out; @@ -690,8 +713,10 @@ void KtxImporterTest::swizzle() { /* toktx lets us swizzle the input data, but doesn't turn the format into a swizzled one. Patch the header manually. */ - auto& header = *reinterpret_cast(fileData.data()); - header.vkFormat = Utility::Endianness::littleEndian(data.vkFormat); + if(data.vkFormat != Implementation::VK_FORMAT_UNDEFINED) { + auto& header = *reinterpret_cast(fileData.data()); + header.vkFormat = Utility::Endianness::littleEndian(data.vkFormat); + } std::ostringstream outDebug; Debug redirectDebug{&outDebug}; diff --git a/src/MagnumPlugins/KtxImporter/formatMapping.hpp b/src/MagnumPlugins/KtxImporter/formatMapping.hpp index d341c7603..80dc0cde7 100644 --- a/src/MagnumPlugins/KtxImporter/formatMapping.hpp +++ b/src/MagnumPlugins/KtxImporter/formatMapping.hpp @@ -27,137 +27,137 @@ /* Autogenerated from formatMapping.py! Do not edit! */ #ifdef _p /* PixelFormat */ -_p(R8_UNORM, R8Unorm, UNORM) -_p(R8G8_UNORM, RG8Unorm, UNORM) -_p(R8G8B8_UNORM, RGB8Unorm, UNORM) -_p(R8G8B8A8_UNORM, RGBA8Unorm, UNORM) -_p(R8_SNORM, R8Snorm, SNORM) -_p(R8G8_SNORM, RG8Snorm, SNORM) -_p(R8G8B8_SNORM, RGB8Snorm, SNORM) -_p(R8G8B8A8_SNORM, RGBA8Snorm, SNORM) -_p(R8_SRGB, R8Srgb, SRGB) -_p(R8G8_SRGB, RG8Srgb, SRGB) -_p(R8G8B8_SRGB, RGB8Srgb, SRGB) -_p(R8G8B8A8_SRGB, RGBA8Srgb, SRGB) -_p(R8_UINT, R8UI, UINT) -_p(R8G8_UINT, RG8UI, UINT) -_p(R8G8B8_UINT, RGB8UI, UINT) -_p(R8G8B8A8_UINT, RGBA8UI, UINT) -_p(R8_SINT, R8I, SINT) -_p(R8G8_SINT, RG8I, SINT) -_p(R8G8B8_SINT, RGB8I, SINT) -_p(R8G8B8A8_SINT, RGBA8I, SINT) -_p(R16_UNORM, R16Unorm, UNORM) -_p(R16G16_UNORM, RG16Unorm, UNORM) -_p(R16G16B16_UNORM, RGB16Unorm, UNORM) -_p(R16G16B16A16_UNORM, RGBA16Unorm, UNORM) -_p(R16_SNORM, R16Snorm, SNORM) -_p(R16G16_SNORM, RG16Snorm, SNORM) -_p(R16G16B16_SNORM, RGB16Snorm, SNORM) -_p(R16G16B16A16_SNORM, RGBA16Snorm, SNORM) -_p(R16_UINT, R16UI, UINT) -_p(R16G16_UINT, RG16UI, UINT) -_p(R16G16B16_UINT, RGB16UI, UINT) -_p(R16G16B16A16_UINT, RGBA16UI, UINT) -_p(R16_SINT, R16I, SINT) -_p(R16G16_SINT, RG16I, SINT) -_p(R16G16B16_SINT, RGB16I, SINT) -_p(R16G16B16A16_SINT, RGBA16I, SINT) -_p(R32_UINT, R32UI, UINT) -_p(R32G32_UINT, RG32UI, UINT) -_p(R32G32B32_UINT, RGB32UI, UINT) -_p(R32G32B32A32_UINT, RGBA32UI, UINT) -_p(R32_SINT, R32I, SINT) -_p(R32G32_SINT, RG32I, SINT) -_p(R32G32B32_SINT, RGB32I, SINT) -_p(R32G32B32A32_SINT, RGBA32I, SINT) -_p(R16_SFLOAT, R16F, SFLOAT) -_p(R16G16_SFLOAT, RG16F, SFLOAT) -_p(R16G16B16_SFLOAT, RGB16F, SFLOAT) -_p(R16G16B16A16_SFLOAT, RGBA16F, SFLOAT) -_p(R32_SFLOAT, R32F, SFLOAT) -_p(R32G32_SFLOAT, RG32F, SFLOAT) -_p(R32G32B32_SFLOAT, RGB32F, SFLOAT) -_p(R32G32B32A32_SFLOAT, RGBA32F, SFLOAT) -_p(D16_UNORM, Depth16Unorm, UNORM) -_p(X8_D24_UNORM_PACK32, Depth24Unorm, UNORM) -_p(D32_SFLOAT, Depth32F, SFLOAT) -_p(S8_UINT, Stencil8UI, UINT) -_p(D16_UNORM_S8_UINT, Depth16UnormStencil8UI, UINT) -_p(D24_UNORM_S8_UINT, Depth24UnormStencil8UI, UINT) -_p(D32_SFLOAT_S8_UINT, Depth32FStencil8UI, UINT) +_p(9, R8Unorm, UNORM) +_p(16, RG8Unorm, UNORM) +_p(23, RGB8Unorm, UNORM) +_p(37, RGBA8Unorm, UNORM) +_p(10, R8Snorm, SNORM) +_p(17, RG8Snorm, SNORM) +_p(24, RGB8Snorm, SNORM) +_p(38, RGBA8Snorm, SNORM) +_p(15, R8Srgb, SRGB) +_p(22, RG8Srgb, SRGB) +_p(29, RGB8Srgb, SRGB) +_p(43, RGBA8Srgb, SRGB) +_p(13, R8UI, UINT) +_p(20, RG8UI, UINT) +_p(27, RGB8UI, UINT) +_p(41, RGBA8UI, UINT) +_p(14, R8I, SINT) +_p(21, RG8I, SINT) +_p(28, RGB8I, SINT) +_p(42, RGBA8I, SINT) +_p(70, R16Unorm, UNORM) +_p(77, RG16Unorm, UNORM) +_p(84, RGB16Unorm, UNORM) +_p(91, RGBA16Unorm, UNORM) +_p(71, R16Snorm, SNORM) +_p(78, RG16Snorm, SNORM) +_p(85, RGB16Snorm, SNORM) +_p(92, RGBA16Snorm, SNORM) +_p(74, R16UI, UINT) +_p(81, RG16UI, UINT) +_p(88, RGB16UI, UINT) +_p(95, RGBA16UI, UINT) +_p(75, R16I, SINT) +_p(82, RG16I, SINT) +_p(89, RGB16I, SINT) +_p(96, RGBA16I, SINT) +_p(98, R32UI, UINT) +_p(101, RG32UI, UINT) +_p(104, RGB32UI, UINT) +_p(107, RGBA32UI, UINT) +_p(99, R32I, SINT) +_p(102, RG32I, SINT) +_p(105, RGB32I, SINT) +_p(108, RGBA32I, SINT) +_p(76, R16F, SFLOAT) +_p(83, RG16F, SFLOAT) +_p(90, RGB16F, SFLOAT) +_p(97, RGBA16F, SFLOAT) +_p(100, R32F, SFLOAT) +_p(103, RG32F, SFLOAT) +_p(106, RGB32F, SFLOAT) +_p(109, RGBA32F, SFLOAT) +_p(124, Depth16Unorm, UNORM) +_p(125, Depth24Unorm, UNORM) +_p(126, Depth32F, SFLOAT) +_p(127, Stencil8UI, UINT) +_p(128, Depth16UnormStencil8UI, UINT) +_p(129, Depth24UnormStencil8UI, UINT) +_p(130, Depth32FStencil8UI, UINT) #endif #ifdef _c /* CompressedPixelFormat */ -_c(BC1_RGB_UNORM_BLOCK, Bc1RGBUnorm, UNORM) -_c(BC1_RGB_SRGB_BLOCK, Bc1RGBSrgb, SRGB) -_c(BC1_RGBA_UNORM_BLOCK, Bc1RGBAUnorm, UNORM) -_c(BC1_RGBA_SRGB_BLOCK, Bc1RGBASrgb, SRGB) -_c(BC2_UNORM_BLOCK, Bc2RGBAUnorm, UNORM) -_c(BC2_SRGB_BLOCK, Bc2RGBASrgb, SRGB) -_c(BC3_UNORM_BLOCK, Bc3RGBAUnorm, UNORM) -_c(BC3_SRGB_BLOCK, Bc3RGBASrgb, SRGB) -_c(BC4_UNORM_BLOCK, Bc4RUnorm, UNORM) -_c(BC4_SNORM_BLOCK, Bc4RSnorm, SNORM) -_c(BC5_UNORM_BLOCK, Bc5RGUnorm, UNORM) -_c(BC5_SNORM_BLOCK, Bc5RGSnorm, SNORM) -_c(BC6H_UFLOAT_BLOCK, Bc6hRGBUfloat, UFLOAT) -_c(BC6H_SFLOAT_BLOCK, Bc6hRGBSfloat, SFLOAT) -_c(BC7_UNORM_BLOCK, Bc7RGBAUnorm, UNORM) -_c(BC7_SRGB_BLOCK, Bc7RGBASrgb, SRGB) -_c(EAC_R11_UNORM_BLOCK, EacR11Unorm, UNORM) -_c(EAC_R11_SNORM_BLOCK, EacR11Snorm, SNORM) -_c(EAC_R11G11_UNORM_BLOCK, EacRG11Unorm, UNORM) -_c(EAC_R11G11_SNORM_BLOCK, EacRG11Snorm, SNORM) -_c(ETC2_R8G8B8_UNORM_BLOCK, Etc2RGB8Unorm, UNORM) -_c(ETC2_R8G8B8_SRGB_BLOCK, Etc2RGB8Srgb, SRGB) -_c(ETC2_R8G8B8A1_UNORM_BLOCK, Etc2RGB8A1Unorm, UNORM) -_c(ETC2_R8G8B8A1_SRGB_BLOCK, Etc2RGB8A1Srgb, SRGB) -_c(ETC2_R8G8B8A8_UNORM_BLOCK, Etc2RGBA8Unorm, UNORM) -_c(ETC2_R8G8B8A8_SRGB_BLOCK, Etc2RGBA8Srgb, SRGB) -_c(ASTC_4x4_UNORM_BLOCK, Astc4x4RGBAUnorm, UNORM) -_c(ASTC_4x4_SRGB_BLOCK, Astc4x4RGBASrgb, SRGB) -_c(ASTC_4x4_SFLOAT_BLOCK_EXT, Astc4x4RGBAF, SFLOAT) -_c(ASTC_5x4_UNORM_BLOCK, Astc5x4RGBAUnorm, UNORM) -_c(ASTC_5x4_SRGB_BLOCK, Astc5x4RGBASrgb, SRGB) -_c(ASTC_5x4_SFLOAT_BLOCK_EXT, Astc5x4RGBAF, SFLOAT) -_c(ASTC_5x5_UNORM_BLOCK, Astc5x5RGBAUnorm, UNORM) -_c(ASTC_5x5_SRGB_BLOCK, Astc5x5RGBASrgb, SRGB) -_c(ASTC_5x5_SFLOAT_BLOCK_EXT, Astc5x5RGBAF, SFLOAT) -_c(ASTC_6x5_UNORM_BLOCK, Astc6x5RGBAUnorm, UNORM) -_c(ASTC_6x5_SRGB_BLOCK, Astc6x5RGBASrgb, SRGB) -_c(ASTC_6x5_SFLOAT_BLOCK_EXT, Astc6x5RGBAF, SFLOAT) -_c(ASTC_6x6_UNORM_BLOCK, Astc6x6RGBAUnorm, UNORM) -_c(ASTC_6x6_SRGB_BLOCK, Astc6x6RGBASrgb, SRGB) -_c(ASTC_6x6_SFLOAT_BLOCK_EXT, Astc6x6RGBAF, SFLOAT) -_c(ASTC_8x5_UNORM_BLOCK, Astc8x5RGBAUnorm, UNORM) -_c(ASTC_8x5_SRGB_BLOCK, Astc8x5RGBASrgb, SRGB) -_c(ASTC_8x5_SFLOAT_BLOCK_EXT, Astc8x5RGBAF, SFLOAT) -_c(ASTC_8x6_UNORM_BLOCK, Astc8x6RGBAUnorm, UNORM) -_c(ASTC_8x6_SRGB_BLOCK, Astc8x6RGBASrgb, SRGB) -_c(ASTC_8x6_SFLOAT_BLOCK_EXT, Astc8x6RGBAF, SFLOAT) -_c(ASTC_8x8_UNORM_BLOCK, Astc8x8RGBAUnorm, UNORM) -_c(ASTC_8x8_SRGB_BLOCK, Astc8x8RGBASrgb, SRGB) -_c(ASTC_8x8_SFLOAT_BLOCK_EXT, Astc8x8RGBAF, SFLOAT) -_c(ASTC_10x5_UNORM_BLOCK, Astc10x5RGBAUnorm, UNORM) -_c(ASTC_10x5_SRGB_BLOCK, Astc10x5RGBASrgb, SRGB) -_c(ASTC_10x5_SFLOAT_BLOCK_EXT, Astc10x5RGBAF, SFLOAT) -_c(ASTC_10x6_UNORM_BLOCK, Astc10x6RGBAUnorm, UNORM) -_c(ASTC_10x6_SRGB_BLOCK, Astc10x6RGBASrgb, SRGB) -_c(ASTC_10x6_SFLOAT_BLOCK_EXT, Astc10x6RGBAF, SFLOAT) -_c(ASTC_10x8_UNORM_BLOCK, Astc10x8RGBAUnorm, UNORM) -_c(ASTC_10x8_SRGB_BLOCK, Astc10x8RGBASrgb, SRGB) -_c(ASTC_10x8_SFLOAT_BLOCK_EXT, Astc10x8RGBAF, SFLOAT) -_c(ASTC_10x10_UNORM_BLOCK, Astc10x10RGBAUnorm, UNORM) -_c(ASTC_10x10_SRGB_BLOCK, Astc10x10RGBASrgb, SRGB) -_c(ASTC_10x10_SFLOAT_BLOCK_EXT, Astc10x10RGBAF, SFLOAT) -_c(ASTC_12x10_UNORM_BLOCK, Astc12x10RGBAUnorm, UNORM) -_c(ASTC_12x10_SRGB_BLOCK, Astc12x10RGBASrgb, SRGB) -_c(ASTC_12x10_SFLOAT_BLOCK_EXT, Astc12x10RGBAF, SFLOAT) -_c(ASTC_12x12_UNORM_BLOCK, Astc12x12RGBAUnorm, UNORM) -_c(ASTC_12x12_SRGB_BLOCK, Astc12x12RGBASrgb, SRGB) -_c(ASTC_12x12_SFLOAT_BLOCK_EXT, Astc12x12RGBAF, SFLOAT) -_c(PVRTC1_2BPP_UNORM_BLOCK_IMG, PvrtcRGBA2bppUnorm, UNORM) -_c(PVRTC1_2BPP_SRGB_BLOCK_IMG, PvrtcRGBA2bppSrgb, SRGB) -_c(PVRTC1_4BPP_UNORM_BLOCK_IMG, PvrtcRGBA4bppUnorm, UNORM) -_c(PVRTC1_4BPP_SRGB_BLOCK_IMG, PvrtcRGBA4bppSrgb, SRGB) +_c(131, Bc1RGBUnorm, UNORM) +_c(132, Bc1RGBSrgb, SRGB) +_c(133, Bc1RGBAUnorm, UNORM) +_c(134, Bc1RGBASrgb, SRGB) +_c(135, Bc2RGBAUnorm, UNORM) +_c(136, Bc2RGBASrgb, SRGB) +_c(137, Bc3RGBAUnorm, UNORM) +_c(138, Bc3RGBASrgb, SRGB) +_c(139, Bc4RUnorm, UNORM) +_c(140, Bc4RSnorm, SNORM) +_c(141, Bc5RGUnorm, UNORM) +_c(142, Bc5RGSnorm, SNORM) +_c(143, Bc6hRGBUfloat, UFLOAT) +_c(144, Bc6hRGBSfloat, SFLOAT) +_c(145, Bc7RGBAUnorm, UNORM) +_c(146, Bc7RGBASrgb, SRGB) +_c(153, EacR11Unorm, UNORM) +_c(154, EacR11Snorm, SNORM) +_c(155, EacRG11Unorm, UNORM) +_c(156, EacRG11Snorm, SNORM) +_c(147, Etc2RGB8Unorm, UNORM) +_c(148, Etc2RGB8Srgb, SRGB) +_c(149, Etc2RGB8A1Unorm, UNORM) +_c(150, Etc2RGB8A1Srgb, SRGB) +_c(151, Etc2RGBA8Unorm, UNORM) +_c(152, Etc2RGBA8Srgb, SRGB) +_c(157, Astc4x4RGBAUnorm, UNORM) +_c(158, Astc4x4RGBASrgb, SRGB) +_c(1000066000, Astc4x4RGBAF, SFLOAT) +_c(159, Astc5x4RGBAUnorm, UNORM) +_c(160, Astc5x4RGBASrgb, SRGB) +_c(1000066001, Astc5x4RGBAF, SFLOAT) +_c(161, Astc5x5RGBAUnorm, UNORM) +_c(162, Astc5x5RGBASrgb, SRGB) +_c(1000066002, Astc5x5RGBAF, SFLOAT) +_c(163, Astc6x5RGBAUnorm, UNORM) +_c(164, Astc6x5RGBASrgb, SRGB) +_c(1000066003, Astc6x5RGBAF, SFLOAT) +_c(165, Astc6x6RGBAUnorm, UNORM) +_c(166, Astc6x6RGBASrgb, SRGB) +_c(1000066004, Astc6x6RGBAF, SFLOAT) +_c(167, Astc8x5RGBAUnorm, UNORM) +_c(168, Astc8x5RGBASrgb, SRGB) +_c(1000066005, Astc8x5RGBAF, SFLOAT) +_c(169, Astc8x6RGBAUnorm, UNORM) +_c(170, Astc8x6RGBASrgb, SRGB) +_c(1000066006, Astc8x6RGBAF, SFLOAT) +_c(171, Astc8x8RGBAUnorm, UNORM) +_c(172, Astc8x8RGBASrgb, SRGB) +_c(1000066007, Astc8x8RGBAF, SFLOAT) +_c(173, Astc10x5RGBAUnorm, UNORM) +_c(174, Astc10x5RGBASrgb, SRGB) +_c(1000066008, Astc10x5RGBAF, SFLOAT) +_c(175, Astc10x6RGBAUnorm, UNORM) +_c(176, Astc10x6RGBASrgb, SRGB) +_c(1000066009, Astc10x6RGBAF, SFLOAT) +_c(177, Astc10x8RGBAUnorm, UNORM) +_c(178, Astc10x8RGBASrgb, SRGB) +_c(1000066010, Astc10x8RGBAF, SFLOAT) +_c(179, Astc10x10RGBAUnorm, UNORM) +_c(180, Astc10x10RGBASrgb, SRGB) +_c(1000066011, Astc10x10RGBAF, SFLOAT) +_c(181, Astc12x10RGBAUnorm, UNORM) +_c(182, Astc12x10RGBASrgb, SRGB) +_c(1000066012, Astc12x10RGBAF, SFLOAT) +_c(183, Astc12x12RGBAUnorm, UNORM) +_c(184, Astc12x12RGBASrgb, SRGB) +_c(1000066013, Astc12x12RGBAF, SFLOAT) +_c(1000054000, PvrtcRGBA2bppUnorm, UNORM) +_c(1000054004, PvrtcRGBA2bppSrgb, SRGB) +_c(1000054001, PvrtcRGBA4bppUnorm, UNORM) +_c(1000054005, PvrtcRGBA4bppSrgb, SRGB) #endif diff --git a/src/MagnumPlugins/KtxImporter/formatMapping.py b/src/MagnumPlugins/KtxImporter/formatMapping.py index 80c884434..1fbc156bd 100644 --- a/src/MagnumPlugins/KtxImporter/formatMapping.py +++ b/src/MagnumPlugins/KtxImporter/formatMapping.py @@ -42,12 +42,27 @@ print('Writing to', file_out) +vulkan_header = os.path.join(magnum_dir, 'src/MagnumExternal/Vulkan/flextVk.h') + +vulkan_formats = {} + +with open(vulkan_header, encoding='utf-8') as f: + lines = f.readlines() + for line in lines: + # Get numeric VkFormat values so we can dereference them directly + # This also finds VK_FORMAT_FEATURE_* but that's no big deal since + # there are no formats that start with FEATURE_ + match = re.search('^\s+VK_FORMAT_(\w+) = (\d+),?$', line) + if match: + assert(not match.group(1) in vulkan_formats) + vulkan_formats[match.group(1)] = match.group(2) + Format = namedtuple('Format', 'compressed magnum vulkan type') formats = [] -file_in = os.path.join(magnum_dir, 'src/Magnum/Vk/PixelFormat.h') +format_header = os.path.join(magnum_dir, 'src/Magnum/Vk/PixelFormat.h') -with open(file_in, encoding='utf-8') as f: +with open(format_header, encoding='utf-8') as f: lines = f.readlines() for line in lines: # Get mapping from VkFormat to Magnum::Vk::PixelFormat @@ -55,12 +70,15 @@ match = re.search('^\s+(Compressed)?(\w+) = VK_FORMAT_(\w+),?$', line) if match: compressed = match.group(1) != None - magnum = match.group(2) - vulkan = match.group(3) - type = re.search('\w+_([U|S](NORM|INT|FLOAT|RGB))\w*', vulkan) + magnum_name = match.group(2) + vulkan_name = match.group(3) + assert(vulkan_name in vulkan_formats) + + type = re.search('\w+_([U|S](NORM|INT|FLOAT|RGB))\w*', vulkan_name) assert type != None assert type.group(1) != 'URGB' - formats.append(Format(compressed, magnum, vulkan, type.group(1))) + + formats.append(Format(compressed, magnum_name, vulkan_formats[vulkan_name], type.group(1))) if len(formats) != 135: print('Unexpected number of formats') From 39900964e8b8d571fb021f247fd15bd3feac9d54 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 28 Jul 2021 19:43:48 +0200 Subject: [PATCH 23/95] KtxImporter: ignore data format descriptor --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 69 +------------------ 1 file changed, 1 insertion(+), 68 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index b7fedbd31..153990577 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -238,20 +238,6 @@ bool validateHeader(const Implementation::KtxHeader& header, std::size_t fileSiz return false; } - const std::size_t dfdEnd = header.dfdByteOffset + header.dfdByteLength; - if(fileSize < dfdEnd) { - Error{} << prefix << "data format descriptor out of bounds, expected at least" << - dfdEnd << "bytes but got" << fileSize; - return false; - } - - const std::size_t dfdMinSize = sizeof(UnsignedInt) + sizeof(Implementation::KdfBasicBlockHeader) + sizeof(Implementation::KdfBasicBlockSample); - if(dfdMinSize > header.dfdByteLength) { - Error{} << prefix << "data format descriptor too short, expected at least" << - dfdMinSize << "bytes but got" << header.dfdByteLength; - return false; - } - const std::size_t kvdEnd = header.kvdByteOffset + header.kvdByteLength; if(fileSize < kvdEnd) { Error{} << prefix << "key/value data out of bounds, expected at least" << @@ -460,9 +446,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { header.imageSize[0], header.imageSize[1], header.imageSize[2], header.layerCount, header.faceCount, header.levelCount, header.supercompressionScheme, - header.dfdByteOffset, header.dfdByteLength, - header.kvdByteOffset, header.kvdByteLength, - header.sgdByteOffset, header.sgdByteLength); + header.kvdByteOffset, header.kvdByteLength); /* Perform some sanity checks on header data, including byte ranges */ if(!validateHeader(header, data.size(), "Trade::KtxImporter::openData():")) @@ -589,58 +573,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { f->numDataDimensions = Math::min(f->numDimensions + UnsignedByte(isLayered || isCubemap), 3); - /* Read data format descriptor (DFD) */ - { - /* Only do some very basic sanity checks, the DFD is terribly - over-engineered and the data is redundant if we have a - (Compressed)PixelFormat */ - bool valid = false; - const auto descriptorData = f->in.suffix(header.dfdByteOffset).prefix(header.dfdByteLength); - const UnsignedInt length = *reinterpret_cast(descriptorData.data()); - Utility::Endianness::littleEndianInPlace(length); - if(length == descriptorData.size()) { - auto& block = *reinterpret_cast(descriptorData.suffix(sizeof(length)).data()); - Utility::Endianness::littleEndianInPlace(block.vendorId, - block.descriptorType, block.versionNumber, - block.descriptorBlockSize); - - /* Basic block must be the first block in the DFD */ - if(block.vendorId == Implementation::KdfBasicBlockHeader::VendorId::Khronos && - block.descriptorType == Implementation::KdfBasicBlockHeader::DescriptorType::Basic && - block.versionNumber == Implementation::KdfBasicBlockHeader::VersionNumber::Kdf1_3 && - block.descriptorBlockSize > sizeof(block) && - block.descriptorBlockSize + sizeof(length) <= length) - { - valid = true; - - /* Check if pixel/block size and channel count match the format */ - if(f->pixelFormat.isCompressed) { - /* Block size */ - const Vector4i expected = Vector4i::pad(compressedBlockSize(f->pixelFormat.compressed), 1); - const Vector4i actual{Math::max(Vector4ub::from(block.texelBlockDimension), UnsignedByte(1))}; - valid = valid && actual == expected; - } else { - /* Pixel size. For supercompressed data, bytePlanes is all - zeros to indicate an unsized format. */ - /** @todo Does this work with depth-stencil formats? */ - if(header.supercompressionScheme == Implementation::SuperCompressionScheme::None) { - const UnsignedInt expected = f->pixelFormat.size; - const UnsignedInt actual = block.bytesPlane[0]; - valid = valid && actual == expected; - } - /* Channel count */ - const UnsignedInt expected = f->pixelFormat.size / f->pixelFormat.typeSize; - const UnsignedInt actual = (block.descriptorBlockSize - sizeof(block))/sizeof(Implementation::KdfBasicBlockSample); - valid = valid && actual == expected; - } - } - } - if(!valid) { - Error{} << "Trade::KtxImporter::openData(): invalid data format descriptor"; - return; - } - } /* Read key/value data, optional */ std::map> keyValueMap; From e9fe3d2dd74745886b5aafaf24d2b625418461cb Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 28 Jul 2021 19:47:32 +0200 Subject: [PATCH 24/95] KtxImporter: avoid std::map for parsing key/value data --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 64 ++++++++++++------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 153990577..67315da96 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -26,7 +26,6 @@ #include "KtxImporter.h" -#include #include #include #include @@ -573,10 +572,22 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { f->numDataDimensions = Math::min(f->numDimensions + UnsignedByte(isLayered || isCubemap), 3); + enum KeyValueType : UnsignedByte { + CubeMapIncomplete, + Orientation, + Swizzle, + Count + }; + struct KeyValueEntry { + Containers::StringView key; + Containers::ArrayView value; + } keyValueEntries[KeyValueType::Count]{ + {"KTXcubemapIncomplete"_s, {}}, + {"KTXorientation"_s, {}}, + {"KTXswizzle"_s, {}}, + }; - /* Read key/value data, optional */ - std::map> keyValueMap; if(header.kvdByteLength > 0) { Containers::ArrayView keyValueData{f->in.suffix(header.kvdByteOffset).prefix(header.kvdByteLength)}; /* Loop through entries, each one consisting of: @@ -585,9 +596,9 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { Byte data[length] Byte padding[...] - data[] begins with a zero-terminated key, the rest of the bytes is the - value content. Value alignment must be implicitly done through key - length, hence the funny KTX keys with multiple underscores. Any + data[] begins with a zero-terminated key, the rest of the bytes is + the value content. Value alignment must be implicitly done through + key length, hence the funny KTX keys with multiple underscores. Any multi-byte numbers in values must be endian-swapped later. */ UnsignedInt current = 0; while(current + sizeof(UnsignedInt) < keyValueData.size()) { @@ -603,11 +614,18 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { const auto value = split[2]; if(key.isEmpty() || value.isEmpty()) - Warning{} << "Trade::KtxImporter::openData(): invalid key/value entry, skipping"; - else if(keyValueMap.count(key) > 0) - Warning{} << "Trade::KtxImporter::openData(): key" << key << "already set, skipping"; - else - keyValueMap[key] = value; + Warning{} << "Trade::KtxImporter::openData(): invalid key/value entry, skipping"; + else { + for(UnsignedInt i = 0; i != Containers::arraySize(keyValueEntries); ++i) { + if(key == keyValueEntries[i].key) { + if(!keyValueEntries[i].value.empty()) + Warning{} << "Trade::KtxImporter::openData(): key" << key << "already set, skipping"; + else + keyValueEntries[i].value = value; + break; + } + } + } } /* Length value is dword-aligned, guaranteed for the first length by the file layout */ @@ -628,9 +646,9 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { constexpr auto targetOrientation = "ruo"_s; bool useDefaultOrientation = true; - const auto found = keyValueMap.find("KTXorientation"_s); - if(found != keyValueMap.end()) { - const Containers::StringView orientation{found->second}; + const Containers::StringView orientation{keyValueEntries[KeyValueType::Orientation].value}; + if(!orientation.isEmpty()) { + /* If it's too short, a warning gets printed and the default is used */ if(orientation.size() >= f->numDimensions) { constexpr Containers::StringView validOrientations[3]{"rl"_s, "du"_s, "io"_s}; for(UnsignedByte i = 0; i != f->numDimensions; ++i) { @@ -665,31 +683,31 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { "will have wrong orientation"; } - /** @todo KTX spec seems to really insist on rd for cubemaps but the + /** @todo KTX spec seems to really insist on rd for cube maps but the wording is odd, I can't tell if they're saying it's mandatory or not: https://github.khronos.org/KTX-Specification/#cubemapOrientation The toktx tool from Khronos Texture Tools also forces rd for - cubemaps, so we should probably do that too. + cube maps, so we should probably do that too. Face orientation (+X, -X, etc.) is based on a left-handed y-up coordinate system, but neither GL nor Vulkan have that. The appendix implies that both need coordinate transformations. Do we have to do anything here? Flip faces/axes to match GL or Vulkan expectations? */ - /* Incomplete cubemaps are a 'feature' of KTX files. We just import them + /* Incomplete cube maps are a 'feature' of KTX files. We just import them as layers (which is how they're exposed to us). */ - if(header.faceCount != 6 && keyValueMap.count("KTXcubemapIncomplete"_s) > 0) { + if(numFaces != 6 && !keyValueEntries[KeyValueType::CubeMapIncomplete].value.empty()) { Warning{} << "Trade::KtxImporter::openData(): image contains incomplete " - "cubemap faces, importing faces as array layers"; + "cube map faces, importing faces as array layers"; } /* Read swizzle information */ if(!f->pixelFormat.isDepth) { - const auto found = keyValueMap.find("KTXswizzle"_s); - if(found != keyValueMap.end()) { - /** @todo This is broken for block-compressed formats. Get numChannels from DFD */ + Containers::StringView swizzle{keyValueEntries[KeyValueType::Swizzle].value}; + if(!swizzle.isEmpty()) { + /** @todo This is broken for block-compressed formats. Get numChannels from DFD? */ const std::size_t numChannels = f->pixelFormat.size / f->pixelFormat.typeSize; - const Containers::StringView swizzle{found->second.prefix(Math::min(numChannels, found->second.size()))}; + swizzle = swizzle.prefix(Math::min(numChannels, swizzle.size())); if(swizzle != "rgba"_s.prefix(numChannels)) { bool handled = false; /* Special cases already supported for 8-bit Vulkan formats */ From 2715a10573828ad84bb25a70bf42d394abd325e9 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 28 Jul 2021 19:50:12 +0200 Subject: [PATCH 25/95] KtxImporter: miscellaneous PR feedback Mostly moving code out of functions only called in one place and deduplicating code --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 534 ++++++++---------- src/MagnumPlugins/KtxImporter/KtxImporter.h | 6 +- .../KtxImporter/Test/KtxImporterTest.cpp | 145 ++--- 3 files changed, 287 insertions(+), 398 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 67315da96..cb8cdfcec 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -50,17 +50,6 @@ namespace Magnum { namespace Trade { namespace { -std::size_t imageLength(const Vector3i& size, PixelFormat format) { - return size.product()*pixelSize(format); -} - -/** @todo Use CompressedPixelStorage::dataProperties for this */ -std::size_t imageLength(const Vector3i& size, CompressedPixelFormat format) { - const Vector3i blockSize = compressedBlockSize(format); - const Vector3i blockCount = (size + (blockSize - Vector3i{1})) / blockSize; - return blockCount.product()*compressedBlockDataSize(format); -} - template struct TypeForSize {}; template<> struct TypeForSize<1> { typedef UnsignedByte Type; }; template<> struct TypeForSize<2> { typedef UnsignedShort Type; }; @@ -131,151 +120,22 @@ void swizzlePixels(SwizzleType type, UnsignedInt typeSize, Containers::ArrayView CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } -template void copyPixels(Math::Vector size, BoolVector3 flip, UnsignedInt texelSize, Containers::ArrayView src, Containers::ArrayView dst) { - static_assert(dimensions >= 1 && dimensions <= 3, ""); - - /* Nothing to flip, just memcpy */ - if(flip.none()) { - Utility::copy(src, dst); - return; - } - - /* Flip selected axes by using StridedArrayView with negative stride. - Ideally we'd just call flipped() on the view but we can't conditionally - call it with a dimension larger than the actual view dimensions. We'd - need a template specialization (more code) or constexpr if (requires - C++ 17), so we manually calculate negative strides and adjust the data - pointer. */ - std::size_t sizes[dimensions + 1]; - std::ptrdiff_t strides[dimensions + 1]; - - sizes[dimensions] = texelSize; - strides[dimensions] = 1; - for(UnsignedInt i = 0; i != dimensions; ++i) { - sizes[dimensions - 1 - i] = size[i]; - strides[dimensions - 1 - i] = strides[dimensions - i]*sizes[dimensions - i]; - } - - const Containers::StridedArrayView dstView{dst, sizes, strides}; - - const char* srcData = src.data(); - for(UnsignedInt i = 0; i != dimensions; ++i) { - if(flip[i]) { - /* Point to the last item of the dimension */ - srcData += strides[dimensions - 1 - i]*(sizes[dimensions - 1 - i] - 1); - strides[dimensions - 1 - i] *= -1; - } - } - - Containers::StridedArrayView srcView{{srcData, src.size()}, sizes, strides}; - - Utility::copy(srcView, dstView); -} - -bool validateHeader(const Implementation::KtxHeader& header, std::size_t fileSize, const char* prefix) { - /* Check magic string */ - const Containers::StringView identifier{header.identifier, sizeof(header.identifier)}; - const Containers::StringView expected{Implementation::KtxFileIdentifier, sizeof(header.identifier)}; - if(identifier != expected) { - /* Print a useful error for a KTX file with an unsupported version. - KTX1 uses the same magic string but with a different version string. */ - if(identifier.hasPrefix(expected.prefix(Implementation::KtxFileVersionOffset))) { - const auto version = identifier.suffix(Implementation::KtxFileVersionOffset).prefix(Implementation::KtxFileVersionLength); - if(version != "20") { - Error() << prefix << "unsupported KTX version, expected 20 but got" << version; - return false; - } - } - - Error() << prefix << "wrong file signature"; - return false; - } - - /* typeSize is the size of the format's underlying type, not the texel - size, e.g. 2 for RG16F. For any sane format it should be a - power-of-two between 1 and 8. */ - if(header.typeSize < 1 || header.typeSize > 8 || - (header.typeSize & (header.typeSize - 1))) - { - Error{} << prefix << "unsupported type size" << header.typeSize; - return false; - } - - if(header.imageSize.x() == 0) { - Error{} << prefix << "invalid image size, width is 0"; - return false; - } - - if(header.imageSize.y() == 0 && header.imageSize.z() > 0) { - Error{} << prefix << "invalid image size, depth is" << header.imageSize.z() << "but height is 0"; - return false; - } - - if(header.faceCount != 1) { - if(header.faceCount != 6) { - Error{} << prefix << "invalid cubemap face count, expected 1 or 6 but got" << header.faceCount; - return false; - } - - if(header.imageSize.z() > 0 || header.imageSize.x() != header.imageSize.y()) { - Error{} << prefix << "invalid cubemap dimensions, must be 2D and square, but got" << header.imageSize; - return false; - } - } - - const UnsignedInt maxLevelCount = Math::log2(header.imageSize.max()) + 1; - if(header.levelCount > maxLevelCount) { - Error{} << prefix << "too many mipmap levels, expected at most" << - maxLevelCount << "but got" << header.levelCount; - return false; - } - - const std::size_t levelIndexEnd = sizeof(header) + Math::max(header.levelCount, 1u)*sizeof(Implementation::KtxLevel); - if(fileSize < levelIndexEnd) { - Error{} << prefix << "level index out of bounds, expected at least" << - levelIndexEnd << "bytes but got" << fileSize; - return false; - } - - const std::size_t kvdEnd = header.kvdByteOffset + header.kvdByteLength; - if(fileSize < kvdEnd) { - Error{} << prefix << "key/value data out of bounds, expected at least" << - kvdEnd << "bytes but got" << fileSize; - return false; - } - - return true; -} - -bool validateLevel(const Implementation::KtxHeader& header, std::size_t fileSize, const Implementation::KtxLevel& level, std::size_t imageLength, const char* prefix) { - CORRADE_INTERNAL_ASSERT(imageLength > 0); +struct Format { + union { + PixelFormat uncompressed; + CompressedPixelFormat compressed; + }; - /* Both lengths should be equal without supercompression. Be lenient here - and only emit a warning in case some shitty exporter gets this wrong. */ - if(header.supercompressionScheme == Implementation::SuperCompressionScheme::None && - level.byteLength != level.uncompressedByteLength) - { - Warning{} << prefix << "mismatching image data sizes, both compressed " - "and uncompressed should be equal but got" << - level.byteLength << "and" << level.uncompressedByteLength; - } + bool isCompressed; + bool isDepth; - const std::size_t levelEnd = level.byteOffset + level.byteLength; - if(fileSize < levelEnd) { - Error{} << prefix << "level data out of bounds, expected at least" << - levelEnd << "bytes but got" << fileSize; - return false; - } + /* Size of entire pixel/block */ + UnsignedInt size; + /* Size of underlying data type, 1 for block-compressed formats */ + UnsignedInt typeSize; - const std::size_t totalLength = imageLength*Math::max(header.layerCount, 1u)*header.faceCount; - if(level.byteLength < totalLength) { - Error{} << prefix << "level data too short, expected at least" << - totalLength << "bytes but got" << level.byteLength; - return false; - } - - return true; -} + SwizzleType swizzle; +}; } @@ -290,36 +150,21 @@ struct KtxImporter::File { /* Dimensions of the source image (1-3) */ UnsignedByte numDimensions; /* Dimensions of the imported image data, including extra dimensions for - array layers or cubemap faces */ + array layers or cube map faces */ UnsignedByte numDataDimensions; - TextureData::Type type; + TextureType type; BoolVector3 flip; - struct Format { - bool decode(Implementation::VkFormat vkFormat); - - union { - PixelFormat uncompressed; - CompressedPixelFormat compressed; - }; - - bool isCompressed; - bool isDepth; - - /* Size of entire pixel/block */ - UnsignedInt size; - /* Size of underlying data type, 1 for block-compressed formats */ - UnsignedInt typeSize; - - SwizzleType swizzle; - } pixelFormat; + Format pixelFormat; /* Usually only one image with n or n+1 dimensions, multiple images for 3D array layers */ Containers::Array> imageData; }; -bool KtxImporter::File::Format::decode(Implementation::VkFormat vkFormat) { +Containers::Optional decodeFormat(Implementation::VkFormat vkFormat) { + Format f{}; + /* Find uncompressed pixel format. Note that none of the formats forbidden by KTX are supported by Magnum, so we filter those at the same time. This might change in the future, but we'll be fine as long as @@ -352,12 +197,12 @@ bool KtxImporter::File::Format::decode(Implementation::VkFormat vkFormat) { } if(format != PixelFormat{}) { - size = pixelSize(format); - CORRADE_INTERNAL_ASSERT(size == 3 || size == 4); - swizzle = (size == 3) ? SwizzleType::BGR : SwizzleType::BGRA; + f.size = pixelSize(format); + CORRADE_INTERNAL_ASSERT(f.size == 3 || f.size == 4); + f.swizzle = (f.size == 3) ? SwizzleType::BGR : SwizzleType::BGRA; } } else - size = pixelSize(format); + f.size = pixelSize(format); if(format != PixelFormat{}) { /* Depth formats are allowed by KTX. We only really use isDepth for @@ -370,14 +215,14 @@ bool KtxImporter::File::Format::decode(Implementation::VkFormat vkFormat) { case PixelFormat::Depth16UnormStencil8UI: case PixelFormat::Depth24UnormStencil8UI: case PixelFormat::Depth32FStencil8UI: - isDepth = true; + f.isDepth = true; default: /* PixelFormat covers all of Vulkan's depth formats */ break; } - uncompressed = format; - return true; + f.uncompressed = format; + return f; } /* Find block-compressed pixel format, no swizzling possible */ @@ -396,10 +241,10 @@ bool KtxImporter::File::Format::decode(Implementation::VkFormat vkFormat) { } if(compressedFormat != CompressedPixelFormat{}) { - size = compressedBlockDataSize(compressedFormat); - compressed = compressedFormat; - isCompressed = true; - return true; + f.size = compressedBlockDataSize(compressedFormat); + f.compressed = compressedFormat; + f.isCompressed = true; + return f; } /** @todo Support all Vulkan formats allowed by the KTX spec. Create custom @@ -412,11 +257,9 @@ bool KtxImporter::File::Format::decode(Implementation::VkFormat vkFormat) { Is this actually worth the effort? Which Vulkan formats are not supported by PixelFormat? */ - return false; + return {}; } -KtxImporter::KtxImporter() = default; - KtxImporter::KtxImporter(PluginManager::AbstractManager& manager, const std::string& plugin): AbstractImporter{manager, plugin} {} KtxImporter::~KtxImporter() = default; @@ -429,11 +272,9 @@ void KtxImporter::doClose() { _f = nullptr; } void KtxImporter::doOpenData(const Containers::ArrayView data) { /* Check if the file is long enough for the header */ - if(data.empty()) { - Error{} << "Trade::KtxImporter::openData(): the file is empty"; - return; - } else if(data.size() < sizeof(Implementation::KtxHeader)) { - Error{} << "Trade::KtxImporter::openData(): file header too short, expected at least" << sizeof(Implementation::KtxHeader) << "bytes but got" << data.size(); + if(data.size() < sizeof(Implementation::KtxHeader)) { + Error{} << "Trade::KtxImporter::openData(): file too short, expected" << + sizeof(Implementation::KtxHeader) << "bytes for the header but got only" << data.size(); return; } @@ -447,13 +288,27 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { header.supercompressionScheme, header.kvdByteOffset, header.kvdByteLength); - /* Perform some sanity checks on header data, including byte ranges */ - if(!validateHeader(header, data.size(), "Trade::KtxImporter::openData():")) + using namespace Containers::Literals; + + /* Check magic string */ + const Containers::StringView identifier{header.identifier, sizeof(header.identifier)}; + const Containers::StringView expected{Implementation::KtxFileIdentifier, sizeof(header.identifier)}; + if(identifier != expected) { + /* Print a useful error for a KTX file with an unsupported version. + KTX1 uses the same magic string but with a different version string. */ + if(identifier.hasPrefix(expected.prefix(Implementation::KtxFileVersionOffset))) { + const auto version = identifier.suffix(Implementation::KtxFileVersionOffset).prefix(Implementation::KtxFileVersionLength); + if(version != "20"_s) { + Error() << "Trade::KtxImporter::openData(): unsupported KTX version, expected 20 but got" << version; + return; + } + } + + Error() << "Trade::KtxImporter::openData(): wrong file signature"; return; + } - Containers::Pointer f{new File{}}; - f->in = Containers::Array{NoInit, data.size()}; - Utility::copy(data, f->in); + /* Read header data and perform some sanity checks, including byte ranges */ /** @todo Support Basis compression */ if(header.vkFormat == Implementation::VK_FORMAT_UNDEFINED) { @@ -461,37 +316,28 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { return; } - /* Get generic format info from Vulkan format */ - if(!f->pixelFormat.decode(header.vkFormat)) { - Error{} << "Trade::KtxImporter::openData(): unsupported format" << header.vkFormat; - return; - } - - /* There is no block-compressed format we can swizzle */ - CORRADE_INTERNAL_ASSERT(!f->pixelFormat.isCompressed || f->pixelFormat.swizzle == SwizzleType::None); - - if(f->pixelFormat.isCompressed && header.typeSize != 1) { - Error{} << "Trade::KtxImporter::openData(): invalid type size for compressed format, expected 1 but got" << header.typeSize; - return; - } - f->pixelFormat.typeSize = header.typeSize; - /** @todo Support supercompression */ if(header.supercompressionScheme != Implementation::SuperCompressionScheme::None) { Error{} << "Trade::KtxImporter::openData(): supercompression is currently not supported"; return; } - f->numDimensions = Math::min(header.imageSize, Vector3ui{1}).sum(); - CORRADE_INTERNAL_ASSERT(f->numDimensions >= 1 && f->numDimensions <= 3); + /* typeSize is the size of the format's underlying type, not the texel + size, e.g. 2 for RG16F. For any sane format it should be a + power-of-two between 1 and 8. */ + if(header.typeSize < 1 || header.typeSize > 8 || + (header.typeSize & (header.typeSize - 1))) + { + Error{} << "Trade::KtxImporter::openData(): unsupported type size" << header.typeSize; + return; + } - if(f->numDimensions == 3 && f->pixelFormat.isDepth) { - Error{} << "Trade::KtxImporter::openData(): 3D images can't have depth/stencil format"; + if(header.imageSize.x() == 0) { + Error{} << "Trade::KtxImporter::openData(): invalid image size, width is 0"; return; } - /* Make sure we don't choke on size calculations using product() */ - const Vector3i size = Math::max(Vector3i{header.imageSize}, 1); + Containers::Pointer f{InPlaceInit}; /* Number of array layers, imported as extra image dimensions (except for 3D images, there it's one Image3D per layer). @@ -502,14 +348,99 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { const bool isLayered = header.layerCount > 0; const UnsignedInt numLayers = Math::max(header.layerCount, 1u); - const bool isCubemap = header.faceCount == 6; + const bool isCubeMap = header.faceCount == 6; const UnsignedInt numFaces = header.faceCount; + /* Get image dimensions and remember the type for doTexture() */ + if(header.imageSize.y() > 0) { + if(header.imageSize.z() > 0) { + f->numDimensions = 3; + f->type = TextureData::Type::Texture3D; + } else { + f->numDimensions = 2; + if(isCubeMap) + f->type = isLayered ? TextureType::CubeMapArray : TextureType::CubeMap; + else + f->type = isLayered ? TextureType::Texture2DArray : TextureType::Texture2D; + } + } else if(header.imageSize.z() > 0) { + Error{} << "Trade::KtxImporter::openData(): invalid image size, depth is" << header.imageSize.z() << "but height is 0"; + return; + } else { + f->numDimensions = 1; + f->type = isLayered ? TextureType::Texture1DArray : TextureType::Texture1D; + } + + CORRADE_INTERNAL_ASSERT(f->numDimensions >= 1 && f->numDimensions <= 3); + f->numDataDimensions = Math::min(f->numDimensions + UnsignedByte(isLayered || isCubeMap), 3); + + /* Make sure we don't choke on size calculations using product() */ + const Vector3i size = Math::max(Vector3i{header.imageSize}, 1); + + if(numFaces != 1) { + if(numFaces != 6) { + Error{} << "Trade::KtxImporter::openData(): expected either 1 or 6 faces for cube maps but got" << numFaces; + return; + } + + if(f->numDimensions != 2 || size.x() != size.y()) { + Error{} << "Trade::KtxImporter::openData(): cube map dimensions must be 2D and square, but got" << header.imageSize; + return; + } + } + /* levelCount == 0 indicates to the user/importer to generate mipmaps. We don't really care either way since we don't generate mipmaps or pass this on to the user. */ const UnsignedInt numMipmaps = Math::max(header.levelCount, 1u); + const UnsignedInt maxLevelCount = Math::log2(size.max()) + 1; + if(numMipmaps > maxLevelCount) { + Error{} << "Trade::KtxImporter::openData(): expected at most" << + maxLevelCount << "mip levels but got" << numMipmaps; + return; + } + + const std::size_t levelIndexEnd = sizeof(header) + numMipmaps*sizeof(Implementation::KtxLevel); + if(data.size() < levelIndexEnd) { + Error{} << "Trade::KtxImporter::openData(): file too short, expected" << + levelIndexEnd << "bytes for level index but got only" << data.size(); + return; + } + + const std::size_t kvdEnd = header.kvdByteOffset + header.kvdByteLength; + if(data.size() < kvdEnd) { + Error{} << "Trade::KtxImporter::openData(): file too short, expected" << + kvdEnd << "bytes for key/value data but got only" << data.size(); + return; + } + + /* Get generic format info from Vulkan format */ + const auto pixelFormat = decodeFormat(header.vkFormat); + if(!pixelFormat) { + Error{} << "Trade::KtxImporter::openData(): unsupported format" << header.vkFormat; + return; + } + f->pixelFormat = *pixelFormat; + + if(f->pixelFormat.isDepth && f->numDimensions == 3) { + Error{} << "Trade::KtxImporter::openData(): 3D images can't have depth/stencil format"; + return; + } + + /* Block-compressed formats have no implicit swizzle */ + CORRADE_INTERNAL_ASSERT(!f->pixelFormat.isCompressed || f->pixelFormat.swizzle == SwizzleType::None); + + if(f->pixelFormat.isCompressed && header.typeSize != 1) { + Error{} << "Trade::KtxImporter::openData(): invalid type size for compressed format, expected 1 but got" << header.typeSize; + return; + } + f->pixelFormat.typeSize = header.typeSize; + + /* Copy file data */ + f->in = Containers::Array{NoInit, data.size()}; + Utility::copy(data, f->in); + /* The level index contains byte ranges for each mipmap, from largest to smallest. Each mipmap contains tightly packed images ordered by layers, faces/slices, rows, columns. */ @@ -517,7 +448,9 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { const auto levelIndex = Containers::arrayCast( f->in.suffix(sizeof(Implementation::KtxHeader)).prefix(levelIndexSize)); - /* Extract image data views */ + /* Extract image data views. Only one image with extra dimensions for array + layers and/or cube map faces, except for 3D array images where it's one + image per layer. */ const UnsignedInt numImages = (f->numDimensions == 3) ? numLayers : 1; f->imageData = Containers::Array>{numImages}; for(UnsignedInt image = 0; image != numImages; ++image) @@ -529,19 +462,45 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { Utility::Endianness::littleEndianInPlace(level.byteOffset, level.byteLength, level.uncompressedByteLength); - const std::size_t partLength = f->pixelFormat.isCompressed - ? imageLength(mipSize, f->pixelFormat.compressed) - : imageLength(mipSize, f->pixelFormat.uncompressed); + /* Both lengths should be equal without supercompression. Be lenient here + and only emit a warning in case some shitty exporter gets this wrong. */ + if(header.supercompressionScheme == Implementation::SuperCompressionScheme::None && + level.byteLength != level.uncompressedByteLength) + { + Warning{} << "Trade::KtxImporter::openData(): byte length" << level.byteLength + << "is not equal to uncompressed byte length" << level.uncompressedByteLength + << "for an image without supercompression, ignoring the latter"; + } - if(!validateLevel(header, data.size(), level, partLength, "Trade::KtxImporter::openData():")) + const std::size_t levelEnd = level.byteOffset + level.byteLength; + if(data.size() < levelEnd) { + Error{} << "Trade::KtxImporter::openData(): file too short, expected" + << levelEnd << "bytes for level data but got only" << data.size(); return; + } + + /* Size of a single layer or face */ + std::size_t partLength; + if (f->pixelFormat.isCompressed) { + const Vector3i blockSize = compressedBlockSize(f->pixelFormat.compressed); + const Vector3i blockCount = (mipSize + (blockSize - Vector3i{1}))/blockSize; + partLength = blockCount.product()*compressedBlockDataSize(f->pixelFormat.compressed); + } else + partLength = mipSize.product()*pixelSize(f->pixelFormat.uncompressed); + + const std::size_t totalLength = partLength*numLayers*numFaces; + if(level.byteLength < totalLength) { + Error{} << "Trade::KtxImporter::openData(): level data too short, " + "expected at least" << totalLength << "bytes but got" << level.byteLength; + return; + } for(UnsignedInt image = 0; image != numImages; ++image) { - std::size_t length = partLength * numFaces; + std::size_t length = partLength*numFaces; std::size_t imageOffset = 0; - if(numImages == numLayers) - imageOffset = image * length; + if(numImages > 1) /* 3D array image, import layers separately */ + imageOffset = image*length; else length *= numLayers; @@ -552,25 +511,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { mipSize = Math::max(mipSize >> 1, 1); } - /* Remember the type for doTexture() */ - switch(f->numDimensions) { - /** @todo Use *Array enums once they're added to Magnum */ - case 1: - f->type = isLayered ? TextureData::Type::Texture1D/*Array*/ : TextureData::Type::Texture1D; - break; - case 2: - if(isCubemap) - f->type = isLayered ? TextureData::Type::Cube/*Array*/ : TextureData::Type::Cube; - else - f->type = isLayered ? TextureData::Type::Texture2D/*Array*/ : TextureData::Type::Texture2D; - break; - case 3: - f->type = TextureData::Type::Texture3D; - break; - default: CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ - } - - f->numDataDimensions = Math::min(f->numDimensions + UnsignedByte(isLayered || isCubemap), 3); + /* Read key/value data, optional */ enum KeyValueType : UnsignedByte { CubeMapIncomplete, @@ -633,8 +574,6 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } } - using namespace Containers::Literals; - /* Read image orientation so we can flip if needed. l/r = left/right @@ -660,17 +599,21 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } if(useDefaultOrientation) { + constexpr auto defaultOrientation = "rdi"_s; constexpr Containers::StringView defaultDirections[3]{ "right"_s, "down"_s, "forward"_s }; - Warning{} << "Trade::KtxImporter::openData(): missing or invalid " - "orientation, assuming" << ", "_s.join(Containers::arrayView(defaultDirections).prefix(f->numDimensions)); - constexpr auto defaultOrientation = "rdi"_s; - const BoolVector3 flip = Math::notEqual(Math::Vector3::from(defaultOrientation.data()), + const BoolVector3 flip = Math::notEqual( + Math::Vector3::from(defaultOrientation.data()), Math::Vector3::from(targetOrientation.data())); + /* Leave non-existing dimensions unset, otherwise they affect the + result of any() and none() */ for(UnsignedByte i = 0; i != f->numDimensions; ++i) f->flip.set(i, flip[i]); + + Warning{} << "Trade::KtxImporter::openData(): missing or invalid " + "orientation, assuming" << ", "_s.join(Containers::arrayView(defaultDirections).prefix(f->numDimensions)); } } @@ -757,81 +700,74 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { _f = std::move(f); } -UnsignedInt KtxImporter::doImage1DCount() const { return (_f->numDataDimensions == 1) ? _f->imageData.size() : 0; } - -UnsignedInt KtxImporter::doImage1DLevelCount(UnsignedInt id) { return _f->imageData[id].size(); } - -Containers::Optional KtxImporter::doImage1D(UnsignedInt id, UnsignedInt level) { +template +ImageData KtxImporter::doImage(UnsignedInt id, UnsignedInt level) { const File::LevelData& levelData = _f->imageData[id][level]; - /* Copy image data. If we don't have to flip any axes, this is just a memcpy. - We already cleared flip for block-compressed data because we can't - reliably flip blocks, so there we always memcpy. */ + /* Copy image data, flipping along axes if necessary */ Containers::Array data{NoInit, levelData.data.size()}; - copyPixels<1>(levelData.size.x(), _f->flip, _f->pixelFormat.size, levelData.data, data); - endianSwap(data, _f->pixelFormat.typeSize); + /* Block-compressed images don't have any flipping performed on them */ + CORRADE_INTERNAL_ASSERT(!_f->pixelFormat.isCompressed || _f->flip.none()); - if(_f->pixelFormat.isCompressed) - return ImageData1D(_f->pixelFormat.compressed, levelData.size.x(), std::move(data)); - - /* Swizzle BGR(A) if necessary */ - swizzlePixels(_f->pixelFormat.swizzle, _f->pixelFormat.typeSize, data); - - /* Adjust pixel storage if row size is not four byte aligned */ - PixelStorage storage; - if((levelData.size.x()*_f->pixelFormat.size)%4 != 0) - storage.setAlignment(1); + /* Assuming src is tightly packed, stride gets calculated implicitly */ + Containers::StridedArrayView4D src{levelData.data, { + std::size_t(levelData.size.z()), + std::size_t(levelData.size.y()), + std::size_t(levelData.size.x()), + _f->pixelFormat.size + }}; + Containers::StridedArrayView4D dst{data, src.size()}; - return ImageData1D{storage, _f->pixelFormat.uncompressed, levelData.size.x(), std::move(data)}; -} + if(_f->flip[2]) src = src.flipped<0>(); + if(_f->flip[1]) src = src.flipped<1>(); + if(_f->flip[0]) src = src.flipped<2>(); -UnsignedInt KtxImporter::doImage2DCount() const { return (_f->numDataDimensions == 2) ? _f->imageData.size() : 0; } + /* Without flipped dimensions this becomes a single memcpy */ + Utility::copy(src, dst); -UnsignedInt KtxImporter::doImage2DLevelCount(UnsignedInt id) { return _f->imageData[id].size(); } + const auto size = Math::Vector::pad(levelData.size); -Containers::Optional KtxImporter::doImage2D(UnsignedInt id, UnsignedInt level) { - const File::LevelData& levelData = _f->imageData[id][level]; + if(_f->pixelFormat.isCompressed) + return ImageData(_f->pixelFormat.compressed, size, std::move(data)); - Containers::Array data{NoInit, levelData.data.size()}; - copyPixels<2>(levelData.size.xy(), _f->flip, _f->pixelFormat.size, levelData.data, data); + /* Uncompressed image */ endianSwap(data, _f->pixelFormat.typeSize); - if(_f->pixelFormat.isCompressed) - return ImageData2D(_f->pixelFormat.compressed, levelData.size.xy(), std::move(data)); - + /* Swizzle BGR(A) if necessary */ swizzlePixels(_f->pixelFormat.swizzle, _f->pixelFormat.typeSize, data); + /* Adjust pixel storage if row size is not four byte aligned */ PixelStorage storage; if((levelData.size.x()*_f->pixelFormat.size)%4 != 0) storage.setAlignment(1); - return ImageData2D{storage, _f->pixelFormat.uncompressed, levelData.size.xy(), std::move(data)}; + return ImageData{storage, _f->pixelFormat.uncompressed, size, std::move(data)}; } -UnsignedInt KtxImporter::doImage3DCount() const { return (_f->numDataDimensions == 3) ? _f->imageData.size() : 0; } +UnsignedInt KtxImporter::doImage1DCount() const { return (_f->numDataDimensions == 1) ? _f->imageData.size() : 0; } -UnsignedInt KtxImporter::doImage3DLevelCount(UnsignedInt id) { return _f->imageData[id].size(); } +UnsignedInt KtxImporter::doImage1DLevelCount(UnsignedInt id) { return _f->imageData[id].size(); } -Containers::Optional KtxImporter::doImage3D(UnsignedInt id, const UnsignedInt level) { - const File::LevelData& levelData = _f->imageData[id][level]; +Containers::Optional KtxImporter::doImage1D(UnsignedInt id, UnsignedInt level) { + return doImage<1>(id, level); +} - Containers::Array data{NoInit, levelData.data.size()}; - copyPixels<3>(levelData.size, _f->flip, _f->pixelFormat.size, levelData.data, data); +UnsignedInt KtxImporter::doImage2DCount() const { return (_f->numDataDimensions == 2) ? _f->imageData.size() : 0; } - endianSwap(data, _f->pixelFormat.typeSize); +UnsignedInt KtxImporter::doImage2DLevelCount(UnsignedInt id) { return _f->imageData[id].size(); } - if(_f->pixelFormat.isCompressed) - return ImageData3D(_f->pixelFormat.compressed, levelData.size, std::move(data)); +Containers::Optional KtxImporter::doImage2D(UnsignedInt id, UnsignedInt level) { + return doImage<2>(id, level); +} - swizzlePixels(_f->pixelFormat.swizzle, _f->pixelFormat.typeSize, data); +UnsignedInt KtxImporter::doImage3DCount() const { return (_f->numDataDimensions == 3) ? _f->imageData.size() : 0; } - PixelStorage storage; - if((levelData.size.x()*_f->pixelFormat.size)%4 != 0) - storage.setAlignment(1); +UnsignedInt KtxImporter::doImage3DLevelCount(UnsignedInt id) { return _f->imageData[id].size(); } - return ImageData3D{storage, _f->pixelFormat.uncompressed, levelData.size, std::move(data)}; +Containers::Optional KtxImporter::doImage3D(UnsignedInt id, const UnsignedInt level) { + return doImage<3>(id, level); } UnsignedInt KtxImporter::doTextureCount() const { return _f->imageData.size(); } diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.h b/src/MagnumPlugins/KtxImporter/KtxImporter.h index 0775173c9..716d99dba 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.h +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.h @@ -50,9 +50,6 @@ namespace Magnum { namespace Trade { class MAGNUM_KTXIMPORTER_EXPORT KtxImporter: public AbstractImporter { public: - /** @brief Default constructor */ - explicit KtxImporter(); - /** @brief Plugin manager constructor */ explicit KtxImporter(PluginManager::AbstractManager& manager, const std::string& plugin); @@ -82,6 +79,9 @@ class MAGNUM_KTXIMPORTER_EXPORT KtxImporter: public AbstractImporter { private: struct File; Containers::Pointer _f; + + template + ImageData doImage(UnsignedInt id, UnsignedInt level); }; }} diff --git a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp index c04fa415b..bd72a4a62 100644 --- a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp +++ b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp @@ -68,7 +68,6 @@ struct KtxImporterTest: TestSuite::Tester { void openShort(); void invalid(); - void invalidDataFormatDescriptor(); void invalidVersion(); void invalidFormat(); @@ -77,13 +76,13 @@ struct KtxImporterTest: TestSuite::Tester { void rgb8(); void rgba8(); - void image1d(); - void image1dMipmaps(); + void image1D(); + void image1DMipmaps(); - void image2dMipmaps(); + void image2DMipmaps(); - //void image3d(); - //void image3dMipmaps(); + //void image3D(); + //void image3DMipmaps(); void keyValueDataEmpty(); void keyValueDataInvalid(); @@ -108,18 +107,15 @@ struct KtxImporterTest: TestSuite::Tester { PluginManager::Manager _manager{"nonexistent"}; }; -constexpr Color3ub Black{0}; -constexpr Color3ub White{0xff}; -constexpr Color3ub Purple{0x7f, 0, 0x7f}; - +using namespace Math::Literals; const Color3ub PatternRgb2DData[3][4]{ /* Origin bottom-left */ - {Color3ub::red(), White, Black, Color3ub::green()}, - {White, Color3ub::red(), Black, Color3ub::green()}, - {Color3ub::blue(), Color3ub::green(), Purple, Purple} + {0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x00ff00_rgb}, + {0xffffff_rgb, 0xff0000_rgb, 0x000000_rgb, 0x00ff00_rgb}, + {0x0000ff_rgb, 0x00ff00_rgb, 0x7f007f_rgb, 0x7f007f_rgb} }; -const Color4ub PatternRgba2DData[3][4] = { +const Color4ub PatternRgba2DData[3][4]{ {PatternRgb2DData[0][0], PatternRgb2DData[0][1], PatternRgb2DData[0][2], PatternRgb2DData[0][3]}, {PatternRgb2DData[1][0], PatternRgb2DData[1][1], PatternRgb2DData[1][2], PatternRgb2DData[1][3]}, {PatternRgb2DData[2][0], PatternRgb2DData[2][1], PatternRgb2DData[2][2], PatternRgb2DData[2][3]} @@ -130,20 +126,16 @@ const struct { const std::size_t length; const char* message; } ShortData[] { - {"empty", 0, - "the file is empty"}, {"identifier", sizeof(Implementation::KtxHeader::identifier) - 1, - "file header too short, expected at least 80 bytes but got 11"}, + "file too short, expected 80 bytes for the header but got only 11"}, {"header", sizeof(Implementation::KtxHeader) - 1, - "file header too short, expected at least 80 bytes but got 79"}, + "file too short, expected 80 bytes for the header but got only 79"}, {"level index", sizeof(Implementation::KtxHeader) + sizeof(Implementation::KtxLevel) - 1, - "level index out of bounds, expected at least 104 bytes but got 103"}, - {"data format descriptor", sizeof(Implementation::KtxHeader) + sizeof(Implementation::KtxLevel) + sizeof(UnsignedInt) + sizeof(Implementation::KdfBasicBlockHeader), - "data format descriptor out of bounds, expected at least 180 bytes but got 132"}, + "file too short, expected 104 bytes for level index but got only 103"}, {"key/value data", sizeof(Implementation::KtxHeader) + sizeof(Implementation::KtxLevel) + sizeof(UnsignedInt) + sizeof(Implementation::KdfBasicBlockHeader) + 3*sizeof(Implementation::KdfBasicBlockSample), - "key/value data out of bounds, expected at least 252 bytes but got 180"}, + "file too short, expected 252 bytes for key/value data but got only 180"}, {"level data", 287, - "level data out of bounds, expected at least 288 bytes but got 287"} + "file too short, expected 288 bytes for level data but got only 287"} }; const struct { @@ -168,17 +160,17 @@ const struct { // "invalid image size, depth is 5 but height is 0"}, {"face count", "rgb.ktx2", offsetof(Implementation::KtxHeader, faceCount), 3, - "invalid cubemap face count, expected 1 or 6 but got 3"}, + "expected either 1 or 6 faces for cube maps but got 3"}, {"cube not square", "rgb.ktx2", offsetof(Implementation::KtxHeader, faceCount), 6, - "invalid cubemap dimensions, must be 2D and square, but got Vector(4, 3, 0)"}, + "cube map dimensions must be 2D and square, but got Vector(4, 3, 0)"}, /** @todo */ //{"cube 3d", "rgb.ktx2", // offsetof(Implementation::KtxHeader, faceCount), 6, // "invalid cubemap dimensions, must be 2D and square, but got Vector(4, 3, 0)"}, {"level count", "rgb.ktx2", offsetof(Implementation::KtxHeader, levelCount), 7, - "too many mipmap levels, expected at most 3 but got 7"}, + "expected at most 3 mip levels but got 7"}, {"custom format", "rgb.ktx2", offsetof(Implementation::KtxHeader, vkFormat), 0, "custom formats are not supported"}, @@ -193,12 +185,6 @@ const struct { //{"3d depth", "rgb.ktx2", // 0, 0, // "3D images can't have depth/stencil format"}, - {"DFD header too short", "rgb.ktx2", - offsetof(Implementation::KtxHeader, dfdByteLength), 1, - "data format descriptor too short, expected at least 44 bytes but got 1"}, - {"DFD no space for block", "rgb.ktx2", - offsetof(Implementation::KtxHeader, dfdByteLength), sizeof(UnsignedInt) + sizeof(Implementation::KdfBasicBlockHeader) + sizeof(Implementation::KdfBasicBlockSample), - "invalid data format descriptor"}, {"level data too short", "rgb.ktx2", sizeof(Implementation::KtxHeader) + offsetof(Implementation::KtxLevel, byteLength), 1, "level data too short, expected at least 36 bytes but got 1"} @@ -207,28 +193,10 @@ const struct { const struct { const char* name; const char* file; - const std::size_t offset; - const char value; -} InvalidDataFormatDescriptorData[] { - {"vendor", "rgb.ktx2", offsetof(Implementation::KdfBasicBlockHeader, vendorId), 1}, - {"type", "rgb.ktx2", offsetof(Implementation::KdfBasicBlockHeader, descriptorType), 1}, - {"version", "rgb.ktx2", offsetof(Implementation::KdfBasicBlockHeader, versionNumber), 0}, - {"block too short", "rgb.ktx2", offsetof(Implementation::KdfBasicBlockHeader, descriptorBlockSize), 1}, - {"block too long", "rgb.ktx2", offsetof(Implementation::KdfBasicBlockHeader, descriptorBlockSize), sizeof(Implementation::KdfBasicBlockHeader) + 4*sizeof(Implementation::KdfBasicBlockSample)}, - {"texel size", "rgb.ktx2", offsetof(Implementation::KdfBasicBlockHeader, bytesPlane), 2}, - {"channel count", "rgb.ktx2", offsetof(Implementation::KdfBasicBlockHeader, descriptorBlockSize), sizeof(Implementation::KdfBasicBlockHeader) + 1*sizeof(Implementation::KdfBasicBlockSample)}, - /** @todo */ - //{"texel block dimensions", "rgb.ktx2", offsetof(Implementation::KdfBasicBlockHeader, texelBlockDimension), 24}, -}; - -const struct { - const char* name; - const char* file; - const Trade::TextureData::Type type; + const Trade::TextureType type; } TextureData[] { - /** @todo Use *Array enums once they're added to Magnum */ - {"1D", "1d.ktx2", Trade::TextureData::Type::Texture1D}, - {"2D", "rgb.ktx2", Trade::TextureData::Type::Texture2D} + {"1D", "1d.ktx2", Trade::TextureType::Texture1D}, + {"2D", "rgb.ktx2", Trade::TextureType::Texture2D} /** @todo one of each texture type */ }; @@ -268,9 +236,6 @@ KtxImporterTest::KtxImporterTest() { addInstancedTests({&KtxImporterTest::invalid}, Containers::arraySize(InvalidData)); - addInstancedTests({&KtxImporterTest::invalidDataFormatDescriptor}, - Containers::arraySize(InvalidDataFormatDescriptorData)); - addTests({&KtxImporterTest::invalidVersion, &KtxImporterTest::invalidFormat}); @@ -280,9 +245,9 @@ KtxImporterTest::KtxImporterTest() { addTests({&KtxImporterTest::rgb8, &KtxImporterTest::rgba8, - & KtxImporterTest::image1d, - & KtxImporterTest::image1dMipmaps, - & KtxImporterTest::image2dMipmaps, + & KtxImporterTest::image1D, + & KtxImporterTest::image1DMipmaps, + & KtxImporterTest::image2DMipmaps, &KtxImporterTest::keyValueDataEmpty, &KtxImporterTest::keyValueDataInvalid, @@ -345,28 +310,9 @@ void KtxImporterTest::invalid() { CORRADE_COMPARE(out.str(), Utility::formatString("Trade::KtxImporter::openData(): {}\n", data.message)); } -void KtxImporterTest::invalidDataFormatDescriptor() { - auto&& data = InvalidDataFormatDescriptorData[testCaseInstanceId()]; - setTestCaseDescription(data.name); - - Containers::Pointer importer = _manager.instantiate("KtxImporter"); - auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, data.file)); - CORRADE_VERIFY(!fileData.empty()); - - const Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); - const std::size_t dfdBlock = header.dfdByteOffset + sizeof(UnsignedInt); - fileData[dfdBlock + data.offset] = data.value; - - std::ostringstream out; - Error redirectError{&out}; - - CORRADE_VERIFY(!importer->openData(fileData)); - CORRADE_COMPARE(out.str(), "Trade::KtxImporter::openData(): invalid data format descriptor\n"); -} - void KtxImporterTest::invalidVersion() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); - + std::ostringstream out; Error redirectError{&out}; @@ -464,20 +410,19 @@ void KtxImporterTest::texture() { CORRADE_COMPARE(texture->type(), data.type); } - /** @todo Use *Array enums once they're added to Magnum */ UnsignedInt dimensions; switch(data.type) { case TextureData::Type::Texture1D: dimensions = 1; break; - //case TextureData::Type::Texture1DArray: + case TextureData::Type::Texture1DArray: case TextureData::Type::Texture2D: dimensions = 2; break; - //case TextureData::Type::Texture2DArray: - //case TextureData::Type::Texture3DArray: + case TextureData::Type::Texture2DArray: case TextureData::Type::Texture3D: - case TextureData::Type::Cube: + case TextureData::Type::CubeMap: + case TextureData::Type::CubeMapArray: dimensions = 3; break; default: CORRADE_INTERNAL_ASSERT_UNREACHABLE(); @@ -531,7 +476,7 @@ void KtxImporterTest::rgba8() { CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(PatternRgba2DData), TestSuite::Compare::Container); } -void KtxImporterTest::image1d() { +void KtxImporterTest::image1D() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d.ktx2"))); @@ -551,20 +496,20 @@ void KtxImporterTest::image1d() { CORRADE_COMPARE(storage.imageHeight(), 0); CORRADE_COMPARE(storage.skip(), Vector3i{}); - constexpr Color3ub data[3]{ - Color3ub::red(), White, Black + const Color3ub data[3]{ + 0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb }; CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(data), TestSuite::Compare::Container); } -void KtxImporterTest::image1dMipmaps() { +void KtxImporterTest::image1DMipmaps() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d-mipmaps.ktx2"))); const Containers::Array mipData[2]{ - {InPlaceInit, {Color3ub::red(), White, Black}}, - {InPlaceInit, {White}} + {InPlaceInit, {0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb}}, + {InPlaceInit, {0xffffff_rgb}} }; CORRADE_COMPARE(importer->image1DCount(), 1); @@ -572,6 +517,8 @@ void KtxImporterTest::image1dMipmaps() { Math::Vector<1, Int> mipSize{3}; for(UnsignedInt i = 0; i != importer->image1DLevelCount(0); ++i) { + CORRADE_ITERATION(i); + auto image = importer->image1D(0, i); CORRADE_VERIFY(image); @@ -591,7 +538,7 @@ void KtxImporterTest::image1dMipmaps() { } } -void KtxImporterTest::image2dMipmaps() { +void KtxImporterTest::image2DMipmaps() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb-mipmaps.ktx2"))); @@ -599,8 +546,8 @@ void KtxImporterTest::image2dMipmaps() { const auto mipData0 = Containers::arrayCast(Containers::arrayView(PatternRgb2DData)); Containers::Array mipData[3]{ Containers::Array{mipData0.size()}, - {InPlaceInit, {Color3ub::red(), Black}}, - {InPlaceInit, {Black}} + {InPlaceInit, {0xff0000_rgb, 0x000000_rgb}}, + {InPlaceInit, {0x000000_rgb}} }; Utility::copy(mipData0, mipData[0]); @@ -609,6 +556,8 @@ void KtxImporterTest::image2dMipmaps() { Vector2i mipSize{4, 3}; for(UnsignedInt i = 0; i != importer->image2DLevelCount(0); ++i) { + CORRADE_ITERATION(i); + auto image = importer->image2D(0, i); CORRADE_VERIFY(image); @@ -723,8 +672,12 @@ void KtxImporterTest::swizzle() { CORRADE_VERIFY(importer->openData(fileData)); + /** @todo Change origin to top-left for the swizzle test files so we can + check the relevant messages only. Relevant for swizzleIdentity(), too */ + std::string expectedMessage = "Trade::KtxImporter::openData(): image will be flipped along y\n"; if(data.message) - CORRADE_VERIFY(Containers::StringView{outDebug.str()}.contains(Utility::formatString("Trade::KtxImporter::openData(): {}\n", data.message))); + expectedMessage += Utility::formatString("Trade::KtxImporter::openData(): {}\n", data.message); + CORRADE_COMPARE(outDebug.str(), expectedMessage); CORRADE_COMPARE(importer->image2DCount(), 1); auto image = importer->image2D(0); @@ -745,8 +698,8 @@ void KtxImporterTest::swizzleIdentity() { count is used, since swizzle is always a constant length 4 in the key/value data. */ CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "swizzle-identity.ktx2"))); - CORRADE_VERIFY(!Containers::StringView{out.str()}.contains("unsupported channel mapping")); - CORRADE_VERIFY(!Containers::StringView{out.str()}.contains("format requires conversion from")); + /* No message about format requiring conversion */ + CORRADE_COMPARE(out.str(), "Trade::KtxImporter::openData(): image will be flipped along y\n"); } void KtxImporterTest::swizzleUnsupported() { From 6c011f1ed6141a5e6b1d6dde7cfd52cc1a511fab Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 28 Jul 2021 19:53:08 +0200 Subject: [PATCH 26/95] Ktx{Importer,ImageConverter}: enable in android package build --- package/archlinux/PKGBUILD-android-arm64 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/archlinux/PKGBUILD-android-arm64 b/package/archlinux/PKGBUILD-android-arm64 index 7216d13a1..a3585de91 100644 --- a/package/archlinux/PKGBUILD-android-arm64 +++ b/package/archlinux/PKGBUILD-android-arm64 @@ -46,8 +46,8 @@ build() { -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=OFF \ -DWITH_JPEGIMPORTER=OFF \ - -DWITH_KTXIMAGECONVERTER=OFF \ - -DWITH_KTXIMPORTER=OFF \ + -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENEXRIMAGECONVERTER=OFF \ From ae4287ca48a771b3178dacd3929cdcc532df37ab Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 30 Jul 2021 21:06:02 +0200 Subject: [PATCH 27/95] KtxImporter: set correct size for images with extra dimensions (cube map and/or array textures) --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index cb8cdfcec..eea5987ee 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -131,6 +131,8 @@ struct Format { /* Size of entire pixel/block */ UnsignedInt size; + /* Compressed block size, (1,1,1) for uncompressed formats */ + Vector3i blockSize; /* Size of underlying data type, 1 for block-compressed formats */ UnsignedInt typeSize; @@ -201,8 +203,7 @@ Containers::Optional decodeFormat(Implementation::VkFormat vkFormat) { CORRADE_INTERNAL_ASSERT(f.size == 3 || f.size == 4); f.swizzle = (f.size == 3) ? SwizzleType::BGR : SwizzleType::BGRA; } - } else - f.size = pixelSize(format); + } if(format != PixelFormat{}) { /* Depth formats are allowed by KTX. We only really use isDepth for @@ -216,11 +217,14 @@ Containers::Optional decodeFormat(Implementation::VkFormat vkFormat) { case PixelFormat::Depth24UnormStencil8UI: case PixelFormat::Depth32FStencil8UI: f.isDepth = true; + break; default: /* PixelFormat covers all of Vulkan's depth formats */ break; } + f.size = pixelSize(format); + f.blockSize = {1, 1, 1}; f.uncompressed = format; return f; } @@ -242,6 +246,7 @@ Containers::Optional decodeFormat(Implementation::VkFormat vkFormat) { if(compressedFormat != CompressedPixelFormat{}) { f.size = compressedBlockDataSize(compressedFormat); + f.blockSize = compressedBlockSize(compressedFormat); f.compressed = compressedFormat; f.isCompressed = true; return f; @@ -451,7 +456,8 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { /* Extract image data views. Only one image with extra dimensions for array layers and/or cube map faces, except for 3D array images where it's one image per layer. */ - const UnsignedInt numImages = (f->numDimensions == 3) ? numLayers : 1; + const bool is3DArrayImage = f->numDimensions == 3 && isLayered; + const UnsignedInt numImages = is3DArrayImage ? numLayers : 1; f->imageData = Containers::Array>{numImages}; for(UnsignedInt image = 0; image != numImages; ++image) f->imageData[image] = Containers::Array{numMipmaps}; @@ -479,32 +485,32 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { return; } - /* Size of a single layer or face */ - std::size_t partLength; - if (f->pixelFormat.isCompressed) { - const Vector3i blockSize = compressedBlockSize(f->pixelFormat.compressed); - const Vector3i blockCount = (mipSize + (blockSize - Vector3i{1}))/blockSize; - partLength = blockCount.product()*compressedBlockDataSize(f->pixelFormat.compressed); + Vector3i levelSize = mipSize; + if(f->numDimensions < f->numDataDimensions) { + CORRADE_INTERNAL_ASSERT(!is3DArrayImage); + CORRADE_INTERNAL_ASSERT(levelSize[f->numDimensions] == 1); + levelSize[f->numDimensions] = numFaces*numLayers; + } + + std::size_t length; + if(f->pixelFormat.isCompressed) { + /** @todo This will probably break for layered 3D block-compressed + data once we support those formats */ + const Vector3i blockSize = f->pixelFormat.blockSize; + const Vector3i blockCount = (levelSize + (blockSize - Vector3i{1}))/blockSize; + length = blockCount.product()*f->pixelFormat.size; } else - partLength = mipSize.product()*pixelSize(f->pixelFormat.uncompressed); + length = levelSize.product()*f->pixelFormat.size; - const std::size_t totalLength = partLength*numLayers*numFaces; - if(level.byteLength < totalLength) { + if(level.byteLength < length) { Error{} << "Trade::KtxImporter::openData(): level data too short, " - "expected at least" << totalLength << "bytes but got" << level.byteLength; + "expected at least" << length << "bytes but got" << level.byteLength; return; } for(UnsignedInt image = 0; image != numImages; ++image) { - std::size_t length = partLength*numFaces; - std::size_t imageOffset = 0; - - if(numImages > 1) /* 3D array image, import layers separately */ - imageOffset = image*length; - else - length *= numLayers; - - f->imageData[image][i] = {mipSize, f->in.suffix(level.byteOffset).suffix(imageOffset).prefix(length)}; + const std::size_t imageOffset = is3DArrayImage ? image*length : 0; + f->imageData[image][i] = {levelSize, f->in.suffix(level.byteOffset + imageOffset).prefix(length)}; } /* Shrink to next power of 2 */ From ca5dee8887b6fcf54858b7335f68b696666d2358 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 30 Jul 2021 21:06:36 +0200 Subject: [PATCH 28/95] KtxImporter: read until the end of the key/value data --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index eea5987ee..31cb5e0b3 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -554,7 +554,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { Utility::Endianness::littleEndianInPlace(length); current += sizeof(length); - if(current + length < keyValueData.size()) { + if(current + length <= keyValueData.size()) { const Containers::StringView entry{keyValueData.suffix(current).prefix(length)}; const Containers::Array3 split = entry.partition('\0'); const auto key = split[0]; From ce95690001723cb573b1e2194622294b0e3d7231 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 30 Jul 2021 21:07:00 +0200 Subject: [PATCH 29/95] KtxImporter: don't warn about empty value data, that's allowed --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 31cb5e0b3..eb7d3923b 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -560,7 +560,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { const auto key = split[0]; const auto value = split[2]; - if(key.isEmpty() || value.isEmpty()) + if(key.isEmpty()) Warning{} << "Trade::KtxImporter::openData(): invalid key/value entry, skipping"; else { for(UnsignedInt i = 0; i != Containers::arraySize(keyValueEntries); ++i) { From 8e8a094ae7c23bbf627d050c4f72bdecbaf61ed2 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 30 Jul 2021 22:58:34 +0200 Subject: [PATCH 30/95] KtxImporter: cleanup --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 84 ++++++++++++------- 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index eb7d3923b..d61703df3 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -173,7 +173,7 @@ Containers::Optional decodeFormat(Implementation::VkFormat vkFormat) { formatMapping.hpp isn't updated without adding an extra check. */ PixelFormat format{}; switch(vkFormat) { - #define _p(vulkan, magnum, _type) case Implementation::VkFormat(vulkan): format = PixelFormat::magnum; break; + #define _p(vulkan, magnum, _type) case vulkan: format = PixelFormat::magnum; break; #include "MagnumPlugins/KtxImporter/formatMapping.hpp" #undef _p default: @@ -237,7 +237,7 @@ Containers::Optional decodeFormat(Implementation::VkFormat vkFormat) { https://github.com/KhronosGroup/KTX-Software/blob/f99221eb1c5ad92fd859765a0c66517ea4059160/lib/dfdutils/vulkan/vulkan_core.h#L1061*/ CompressedPixelFormat compressedFormat{}; switch(vkFormat) { - #define _c(vulkan, magnum, _type) case Implementation::VkFormat(vulkan): compressedFormat = CompressedPixelFormat::magnum; break; + #define _c(vulkan, magnum, _type) case vulkan: compressedFormat = CompressedPixelFormat::magnum; break; #include "MagnumPlugins/KtxImporter/formatMapping.hpp" #undef _c default: @@ -376,9 +376,12 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { f->type = isLayered ? TextureType::Texture1DArray : TextureType::Texture1D; } - CORRADE_INTERNAL_ASSERT(f->numDimensions >= 1 && f->numDimensions <= 3); f->numDataDimensions = Math::min(f->numDimensions + UnsignedByte(isLayered || isCubeMap), 3); + CORRADE_INTERNAL_ASSERT(f->numDimensions >= 1 && f->numDimensions <= 3); + CORRADE_INTERNAL_ASSERT(f->numDataDimensions >= f->numDimensions); + CORRADE_INTERNAL_ASSERT(f->numDataDimensions - f->numDimensions <= 1); + /* Make sure we don't choke on size calculations using product() */ const Vector3i size = Math::max(Vector3i{header.imageSize}, 1); @@ -442,7 +445,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { } f->pixelFormat.typeSize = header.typeSize; - /* Copy file data */ + /* Make an owned copy of the data */ f->in = Containers::Array{NoInit, data.size()}; Utility::copy(data, f->in); @@ -592,15 +595,16 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { bool useDefaultOrientation = true; const Containers::StringView orientation{keyValueEntries[KeyValueType::Orientation].value}; - if(!orientation.isEmpty()) { - /* If it's too short, a warning gets printed and the default is used */ - if(orientation.size() >= f->numDimensions) { - constexpr Containers::StringView validOrientations[3]{"rl"_s, "du"_s, "io"_s}; - for(UnsignedByte i = 0; i != f->numDimensions; ++i) { - useDefaultOrientation = !validOrientations[i].contains(orientation[i]); - if(useDefaultOrientation) break; - f->flip.set(i, orientation[i] != targetOrientation[i]); - } + /* If the orientation string is too short or invalid, a warning gets + printed and the default is used. + Strings that are null-terminated but too short pass the first if + but get caught by the test for valid orientation characters. */ + if(orientation.size() >= f->numDimensions) { + constexpr Containers::StringView validOrientations[3]{"rl"_s, "du"_s, "io"_s}; + for(UnsignedByte i = 0; i != f->numDimensions; ++i) { + useDefaultOrientation = !validOrientations[i].contains(orientation[i]); + if(useDefaultOrientation) break; + f->flip.set(i, orientation[i] != targetOrientation[i]); } } @@ -652,12 +656,23 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { /* Read swizzle information */ if(!f->pixelFormat.isDepth) { - Containers::StringView swizzle{keyValueEntries[KeyValueType::Swizzle].value}; + /* Remove trailing zero and anything after it */ + auto swizzle = Containers::StringView{keyValueEntries[KeyValueType::Swizzle].value}.partition('\0')[0]; if(!swizzle.isEmpty()) { - /** @todo This is broken for block-compressed formats. Get numChannels from DFD? */ - const std::size_t numChannels = f->pixelFormat.size / f->pixelFormat.typeSize; - swizzle = swizzle.prefix(Math::min(numChannels, swizzle.size())); - if(swizzle != "rgba"_s.prefix(numChannels)) { + /* We don't know the channel count of block-compressed formats so + comparing only the necessary prefix of the swizzle string can't + be done for them. The spec says to use 0 and 1 for missing + channels so in theory this is possible, but anyone who wants an + identity swizzle simply won't have an entry in the key/value + data in the first place. Reading the DFD for getting the channel + count is terrible overkill, so try our best for normal formats + and leave it at that. + */ + if(!f->pixelFormat.isCompressed) { + const std::size_t numChannels = f->pixelFormat.size / f->pixelFormat.typeSize; + swizzle = swizzle.prefix(Math::min(numChannels, swizzle.size())); + } + if(!"rgba"_s.hasPrefix(swizzle)) { bool handled = false; /* Special cases already supported for 8-bit Vulkan formats */ if(!f->pixelFormat.isCompressed) { @@ -709,14 +724,26 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { template ImageData KtxImporter::doImage(UnsignedInt id, UnsignedInt level) { const File::LevelData& levelData = _f->imageData[id][level]; - - /* Copy image data, flipping along axes if necessary */ + const auto size = Math::Vector::pad(levelData.size); Containers::Array data{NoInit, levelData.data.size()}; - /* Block-compressed images don't have any flipping performed on them */ - CORRADE_INTERNAL_ASSERT(!_f->pixelFormat.isCompressed || _f->flip.none()); + /* Block-compressed images don't have any flipping, swizzling or endian + swapping performed on them. Special-casing this mainly to avoid having + to calculate the block count for the strided array view. We already know + the entire data size. */ + if(_f->pixelFormat.isCompressed) { + CORRADE_INTERNAL_ASSERT(_f->flip.none()); + CORRADE_INTERNAL_ASSERT(_f->pixelFormat.swizzle == SwizzleType::None); + CORRADE_INTERNAL_ASSERT(_f->pixelFormat.typeSize == 1); + + Utility::copy(levelData.data, data); + return ImageData(_f->pixelFormat.compressed, size, std::move(data)); + } + + /* Uncompressed image */ - /* Assuming src is tightly packed, stride gets calculated implicitly */ + /* Copy image data, flipping along axes if necessary. Assuming src is + tightly packed, stride gets calculated implicitly. */ Containers::StridedArrayView4D src{levelData.data, { std::size_t(levelData.size.z()), std::size_t(levelData.size.y()), @@ -732,18 +759,11 @@ ImageData KtxImporter::doImage(UnsignedInt id, UnsignedInt level) { /* Without flipped dimensions this becomes a single memcpy */ Utility::copy(src, dst); - const auto size = Math::Vector::pad(levelData.size); - - if(_f->pixelFormat.isCompressed) - return ImageData(_f->pixelFormat.compressed, size, std::move(data)); - - /* Uncompressed image */ - - endianSwap(data, _f->pixelFormat.typeSize); - /* Swizzle BGR(A) if necessary */ swizzlePixels(_f->pixelFormat.swizzle, _f->pixelFormat.typeSize, data); + endianSwap(data, _f->pixelFormat.typeSize); + /* Adjust pixel storage if row size is not four byte aligned */ PixelStorage storage; if((levelData.size.x()*_f->pixelFormat.size)%4 != 0) From c35d2477f5d20fcf735590138f4525fa3ccf413a Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 30 Jul 2021 23:04:07 +0200 Subject: [PATCH 31/95] KtxImporter: more tests Tests for layered textures, textures with mipmaps, 3d textures, cube maps, compressed images, key/value data, orientation --- .../KtxImporter/Test/1d-compressed-bc1.ktx2 | Bin 0 -> 216 bytes .../KtxImporter/Test/1d-compressed-etc2.ktx2 | Bin 0 -> 224 bytes .../KtxImporter/Test/1d-layers.ktx2 | Bin 0 -> 276 bytes .../KtxImporter/Test/1d-mipmaps.ktx2 | Bin 273 -> 312 bytes src/MagnumPlugins/KtxImporter/Test/1d.ktx2 | Bin 237 -> 240 bytes .../KtxImporter/Test/2d-compressed-bc1.ktx2 | Bin 0 -> 240 bytes .../KtxImporter/Test/2d-compressed-bc2.ktx2 | Bin 0 -> 288 bytes .../KtxImporter/Test/2d-compressed-bc3.ktx2 | Bin 0 -> 288 bytes .../KtxImporter/Test/2d-compressed-bc4.ktx2 | Bin 0 -> 240 bytes .../KtxImporter/Test/2d-compressed-bc5.ktx2 | Bin 0 -> 288 bytes .../KtxImporter/Test/2d-compressed-etc2.ktx2 | Bin 0 -> 280 bytes .../KtxImporter/Test/2d-compressed-pvrtc.ktx2 | Bin 0 -> 240 bytes .../KtxImporter/Test/2d-layers.ktx2 | Bin 0 -> 348 bytes .../Test/2d-mipmaps-incomplete.ktx2 | Bin 0 -> 324 bytes .../{rgb-mipmaps.ktx2 => 2d-mipmaps.ktx2} | Bin 360 -> 360 bytes .../Test/{rgb.ktx2 => 2d-rgb.ktx2} | Bin .../Test/{rgba.ktx2 => 2d-rgba.ktx2} | Bin .../KtxImporter/Test/3d-files.txt | 3 - .../KtxImporter/Test/3d-layers.ktx2 | Bin 0 -> 456 bytes src/MagnumPlugins/KtxImporter/Test/3d.ktx2 | Bin 0 -> 348 bytes .../KtxImporter/Test/KtxImporterTest.cpp | 918 ++++++++++++++---- src/MagnumPlugins/KtxImporter/Test/README.md | 5 +- .../KtxImporter/Test/black1d.png | Bin 0 -> 119 bytes src/MagnumPlugins/KtxImporter/Test/cube+x.png | Bin 0 -> 128 bytes src/MagnumPlugins/KtxImporter/Test/cube+y.png | Bin 0 -> 128 bytes src/MagnumPlugins/KtxImporter/Test/cube+z.png | Bin 0 -> 128 bytes src/MagnumPlugins/KtxImporter/Test/cube-x.png | Bin 0 -> 128 bytes src/MagnumPlugins/KtxImporter/Test/cube-y.png | Bin 0 -> 128 bytes src/MagnumPlugins/KtxImporter/Test/cube-z.png | Bin 0 -> 128 bytes .../KtxImporter/Test/cubemap-layers.ktx2 | Bin 0 -> 384 bytes .../KtxImporter/Test/cubemap-mipmaps.ktx2 | Bin 0 -> 360 bytes .../KtxImporter/Test/cubemap.ktx2 | Bin 0 -> 312 bytes .../KtxImporter/Test/generate.sh | 81 +- .../KtxImporter/Test/orientation-empty.ktx2 | Bin 264 -> 0 bytes .../KtxImporter/Test/pattern-1d-uneven.png | Bin 0 -> 133 bytes .../KtxImporter/Test/pattern-1d.png | Bin 0 -> 127 bytes .../KtxImporter/Test/pattern-mip1.png | Bin 122 -> 122 bytes .../KtxImporter/Test/pattern-mips.txt | 3 - .../KtxImporter/Test/pattern-pot.png | Bin 0 -> 141 bytes .../KtxImporter/Test/pattern-uneven.png | Bin 0 -> 152 bytes .../KtxImporter/Test/pattern1d-mip1.png | Bin 119 -> 0 bytes .../KtxImporter/Test/pattern1d-mips.txt | 2 - .../KtxImporter/Test/pattern1d.png | Bin 125 -> 0 bytes .../KtxImporter/Test/version1.ktx | Bin 132 -> 132 bytes src/MagnumPlugins/KtxImporter/Test/white.png | Bin 130 -> 0 bytes 45 files changed, 818 insertions(+), 194 deletions(-) create mode 100644 src/MagnumPlugins/KtxImporter/Test/1d-compressed-bc1.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/1d-compressed-etc2.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/1d-layers.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc1.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc2.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc3.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc4.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc5.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-etc2.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-pvrtc.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-layers.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-mipmaps-incomplete.ktx2 rename src/MagnumPlugins/KtxImporter/Test/{rgb-mipmaps.ktx2 => 2d-mipmaps.ktx2} (82%) rename src/MagnumPlugins/KtxImporter/Test/{rgb.ktx2 => 2d-rgb.ktx2} (100%) rename src/MagnumPlugins/KtxImporter/Test/{rgba.ktx2 => 2d-rgba.ktx2} (100%) delete mode 100644 src/MagnumPlugins/KtxImporter/Test/3d-files.txt create mode 100644 src/MagnumPlugins/KtxImporter/Test/3d-layers.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/3d.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/black1d.png create mode 100644 src/MagnumPlugins/KtxImporter/Test/cube+x.png create mode 100644 src/MagnumPlugins/KtxImporter/Test/cube+y.png create mode 100644 src/MagnumPlugins/KtxImporter/Test/cube+z.png create mode 100644 src/MagnumPlugins/KtxImporter/Test/cube-x.png create mode 100644 src/MagnumPlugins/KtxImporter/Test/cube-y.png create mode 100644 src/MagnumPlugins/KtxImporter/Test/cube-z.png create mode 100644 src/MagnumPlugins/KtxImporter/Test/cubemap-layers.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/cubemap-mipmaps.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/cubemap.ktx2 delete mode 100644 src/MagnumPlugins/KtxImporter/Test/orientation-empty.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/pattern-1d-uneven.png create mode 100644 src/MagnumPlugins/KtxImporter/Test/pattern-1d.png delete mode 100644 src/MagnumPlugins/KtxImporter/Test/pattern-mips.txt create mode 100644 src/MagnumPlugins/KtxImporter/Test/pattern-pot.png create mode 100644 src/MagnumPlugins/KtxImporter/Test/pattern-uneven.png delete mode 100644 src/MagnumPlugins/KtxImporter/Test/pattern1d-mip1.png delete mode 100644 src/MagnumPlugins/KtxImporter/Test/pattern1d-mips.txt delete mode 100644 src/MagnumPlugins/KtxImporter/Test/pattern1d.png delete mode 100644 src/MagnumPlugins/KtxImporter/Test/white.png diff --git a/src/MagnumPlugins/KtxImporter/Test/1d-compressed-bc1.ktx2 b/src/MagnumPlugins/KtxImporter/Test/1d-compressed-bc1.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..a5e4539f95f39af293832afe6a68c6c2c72b9d06 GIT binary patch literal 216 zcmZ4O9TK5nWU!l;ONy(Ffq{V$h*^Lb8Gyt<7z8qaSOxSqk{`j0S+`8qz43; z7&I6f7?~KDnHd;hY8V*oVa7B32LeH$I-s-ii!xL5N)k&l^Ya*rKzgMi;^jq|C80|Nsi5VHd@G60EzFbHG-u?`S})LH;BI=BE85J00rdO(1Q zL4#o-BNGENGXnz$La;G4AKzs@}kU=)FOs}u%M9C3ZKj*g)&n;Lp=kK3b6kF|3QW@)Yk(kI2$Aa0|1Gr BB6R=& literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/1d-mipmaps.ktx2 b/src/MagnumPlugins/KtxImporter/Test/1d-mipmaps.ktx2 index e975ba6657d6e99c9c00dc26cfa9773ce1f72974..7bafa83aca3acf2bac6bcafe2a52f58b5a814497 100644 GIT binary patch literal 312 zcmZ4O9TK5nWU!l;ONvXDfq{V$h*^Lb8Gyu?p#n32v=0zJ0b&EF7&4^;QVaw<5DHE! zK*iah;xJkSDh@LrMuY4C0VW0y21Z6EFdHVvz`zdXgNXk?%)!78H3h;KU|@%6fXZtF zX`naDi!w`6ix^7svr8%z%1rbO^bG3E6!aBxGLw)*Oo57#!2kdM8S3jH0zkV!Tp*~g F2LQHpAMOAE delta 118 zcmdnNG?8h7NEkB%DqsY1nV>=qK-ve0&j7Ij5TgSQkYXUKW=8Kuluz0ipRTQ28)%03ARbxBvhE literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc2.ktx2 b/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc2.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..337ca291f5d8436d1114eba74a09ab7235993a02 GIT binary patch literal 288 zcmZ4O9TK5nWU!l;ONy(5fq{V$h&iAbNPz)J41_@-1Bh*acnJ_&05Ljv02OdRqd|H= zfC;Fpk&%gknVEq>03rk=?d4(2|3KitU=QOmfcQc{9?+TjMVYC2C5a`O`FRXQDL^r4 uh03rk=?d4(2|3KitU=QOmfcQc{9?+TjMVYC2C5a`O`FRXQDL^r4 vhxa%NX{zf literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc4.ktx2 b/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc4.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..8f07f23b70a5d7ee496087d604de40a748c0953f GIT binary patch literal 240 zcmZ4O9TK5nWU!l;ONy(Tfq{V$h&iAbNPz)J41_@-1Bi8icnT0(05Lka02NR`qd|H= zfQdnap_!49fteX%4@A@+LIKJDKp+I9fzHY=%1q5GNi50C&toV`0g6dO#LJ5^OHzv% j0>XkqQY(BilN8EK^$hh4fRaG)|38GTuZPeuK8ywcW}zWR literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc5.ktx2 b/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc5.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..b19b3b9f723bc94d28c4b069597dc48b3b1421af GIT binary patch literal 288 zcmZ4O9TK5nWU!l;ONy(Pfq{V$h&iAbNPz)J41_@-1Bh*acnJ_&05Ljv02OdRqd|H= zfC;Fpg^`hgnVEq>03rk=?cuEdK;Xb&53`N|#1{g}0-c#(l$n}Wl30?NpT|&?0u+;m sh?f^-mZTOj1cU{Jq*nN3CMlGe>KW=8K#ctVA40>(`g#}(yF9vj0HiY}UH||9 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-compressed-etc2.ktx2 b/src/MagnumPlugins/KtxImporter/Test/2d-compressed-etc2.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..d91afadab80d3ccae42a4f1fb83236bd779850bf GIT binary patch literal 280 zcmZ4O9TK5nWU!l;ONwg>0|Nsi5OV@C7ZAe$NDPEQAOnbXfEc9K0*KMU1*m`r8V%9| z0!$1V3=0{V7?_zE7&st8K+>KG#{3TiLO>4CS@}hosd*)dC7Jno3`HqGF=>c+c~NFb zY7s*~SWrl6g->RZLYb+ap`HQ6ggOS6dIt6f40a5^_cQ!{AkV_U&&0sbzyK46(J*lk OZEVcIfJ`$0-39=FnI+5s literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-compressed-pvrtc.ktx2 b/src/MagnumPlugins/KtxImporter/Test/2d-compressed-pvrtc.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..5b871d207b3e7070fa5fb20d71253e4bd2a5c4da GIT binary patch literal 240 zcmZ4O9TK5nWU!l;ON#63oY~fl3=9k$PzjJfxSqk|7nfgCg% zqz43;7(5sl8JWOrm>moZ>|j2K_z%P!4D2xLz` zuOzV~Ge3`^Cb zAi%`n!NAA}G8JM6n8Cmfp@8InAm(6Thgk>a3ox)lG(hErfHctA`9+zjc_oP@nfZAP zMJYfrX^41vQD#YM5ko*&P)KTpPiB%rnW>(ko&ivjXz(9sZ#_dj5d8-Nh>I1C$36 OB1R3-77|cM*e(G7bwnco literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/3d.ktx2 b/src/MagnumPlugins/KtxImporter/Test/3d.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..6a441ce8424b8cae6a4d164380bf5a0adfd7526b GIT binary patch literal 348 zcmZ4O9TK5nWU!l;ONvXDfq{V$h*^M`8H#}vSPUc%0vSN;1H@Z^*aC>r!3U^74jK*8 z0|HD89t@0(Okg(14hUd}F&O>>SsV=PFzdj40S0!62A~{BUI>VR&dx8&OwB7vEXmBz zV<<`iib+Gn%ZoBgQi~V@!h%9lD||AO6v|BX4D}3vl0<|5Kzr&L>VfD#5J0qn#2BDF IkPu-t06 size; +} CompressedImage1DData[]{ + {"PVRTC", "1d-compressed-bc1.ktx2", CompressedPixelFormat::Bc1RGBASrgb, {4}}, + {"ETC2", "1d-compressed-etc2.ktx2", CompressedPixelFormat::Etc2RGB8Srgb, {7}} +}; + +const struct { + const char* name; + const char* file; + const CompressedPixelFormat format; + const Vector2i size; +} CompressedImage2DData[]{ + {"PVRTC", "2d-compressed-pvrtc.ktx2", CompressedPixelFormat::PvrtcRGBA4bppSrgb, {8, 8}}, + {"BC1", "2d-compressed-bc1.ktx2", CompressedPixelFormat::Bc1RGBASrgb, {8, 8}}, + {"BC2", "2d-compressed-bc2.ktx2", CompressedPixelFormat::Bc2RGBASrgb, {8, 8}}, + {"BC3", "2d-compressed-bc3.ktx2", CompressedPixelFormat::Bc3RGBASrgb, {8, 8}}, + {"BC4", "2d-compressed-bc4.ktx2", CompressedPixelFormat::Bc4RUnorm, {8, 8}}, + {"BC5", "2d-compressed-bc5.ktx2", CompressedPixelFormat::Bc5RGUnorm, {8, 8}}, + {"ETC2", "2d-compressed-etc2.ktx2", CompressedPixelFormat::Etc2RGB8Srgb, {9, 10}} +}; + +using namespace Containers::Literals; + +const struct { + const char* name; + const Containers::StringView data; + const char* message; +} InvalidKeyValueData[]{ + /* Entry has length 0, followed by a valid entry (with an empty value, that's allowed) */ + {"zero length", "\x00\x00\x00\x00\x02\x00\x00\x00k\x00\x00\x00"_s, "invalid key/value entry, skipping"}, + /* Key has length 0, followed by padding + a valid entry */ + {"empty key", "\x02\x00\x00\x00\x00v\x00\x00\x02\x00\x00\x00k\x00\x00\x00"_s, "invalid key/value entry, skipping"}, + /* Duplicate key check only happens for specific keys used later */ + {"duplicate key", "\x10\x00\x00\x00KTXswizzle\x00rgba\x00\x10\x00\x00\x00KTXswizzle\x00rgba\x00"_s, "key KTXswizzle already set, skipping"}, +}; + +const struct { + const char* name; + const Containers::StringView data; +} IgnoredInvalidKeyValueData[]{ + /* Length extends beyond key/value data */ + {"length out of bounds", "\xff\x00\x00\x00k\x00\x00\x00"_s}, + /* Importer shouldn't care about order of keys */ + {"unsorted keys", "\x02\x00\x00\x00b\x00\x00\x00\x02\x00\x00\x00a\x00\x00\x00"_s} +}; + +const struct { + const char* name; + const char* file; + const UnsignedInt dimensions; + const Containers::StringView orientation; +} InvalidOrientationData[]{ + {"empty", "1d.ktx2", 1, ""_s}, + {"short", "2d-rgb.ktx2", 2, "r"_s}, + {"invalid x", "2d-rgb.ktx2", 2, "xd"_s}, + {"invalid y", "2d-rgb.ktx2", 2, "rx"_s}, + {"invalid z", "3d.ktx2", 3, "rux"_s}, +}; + +const struct { + const char* name; + const char* file; + const UnsignedByte dimensions; +} FlipData[]{ + {"1D", "1D.ktx2", 1}, + {"2D", "2d-rgb.ktx2", 2}, + {"3D", "3d.ktx2", 3} }; const struct { @@ -207,22 +299,22 @@ const struct { const Implementation::VkFormat vkFormat; const char* message; const Containers::ArrayView data; -} SwizzleData[] { +} SwizzleData[]{ {"BGR8 header", "bgr-swizzle-bgr.ktx2", PixelFormat::RGB8Srgb, Implementation::VK_FORMAT_UNDEFINED, - "format requires conversion from BGR to RGB", Containers::arrayCast(PatternRgb2DData)}, + "format requires conversion from BGR to RGB", Containers::arrayCast(PatternRgbData[0])}, {"BGRA8 header", "bgra-swizzle-bgra.ktx2", PixelFormat::RGBA8Srgb, Implementation::VK_FORMAT_UNDEFINED, "format requires conversion from BGRA to RGBA", Containers::arrayCast(PatternRgba2DData)}, {"BGR8 format", "bgr.ktx2", PixelFormat::RGB8Srgb, Implementation::VK_FORMAT_B8G8R8_SRGB, - "format requires conversion from BGR to RGB", Containers::arrayCast(PatternRgb2DData)}, + "format requires conversion from BGR to RGB", Containers::arrayCast(PatternRgbData[0])}, {"BGRA8 format", "bgra.ktx2", PixelFormat::RGBA8Srgb, Implementation::VK_FORMAT_B8G8R8A8_SRGB, "format requires conversion from BGRA to RGBA", Containers::arrayCast(PatternRgba2DData)}, {"BGR8 format+header cancel", "swizzle-bgr.ktx2", PixelFormat::RGB8Srgb, Implementation::VK_FORMAT_B8G8R8_SRGB, - nullptr, Containers::arrayCast(PatternRgb2DData)}, + nullptr, Containers::arrayCast(PatternRgbData[0])}, {"BGRA8 format+header cancel", "swizzle-bgra.ktx2", PixelFormat::RGBA8Srgb, Implementation::VK_FORMAT_B8G8R8A8_SRGB, nullptr, Containers::arrayCast(PatternRgba2DData)}, @@ -242,24 +334,46 @@ KtxImporterTest::KtxImporterTest() { addInstancedTests({&KtxImporterTest::texture}, Containers::arraySize(TextureData)); - addTests({&KtxImporterTest::rgb8, - &KtxImporterTest::rgba8, + addTests({&KtxImporterTest::image1D, + &KtxImporterTest::image1DMipmaps, + &KtxImporterTest::image1DLayers}); + + addInstancedTests({&KtxImporterTest::image1DCompressed}, + Containers::arraySize(CompressedImage1DData)); + + addTests({&KtxImporterTest::image2D, + &KtxImporterTest::image2DRgba, + &KtxImporterTest::image2DMipmaps, + &KtxImporterTest::image2DMipmapsIncomplete, + &KtxImporterTest::image2DLayers}); + + addInstancedTests({&KtxImporterTest::image2DCompressed}, + Containers::arraySize(CompressedImage2DData)); + + addTests({&KtxImporterTest::imageCubeMapIncomplete, + &KtxImporterTest::imageCubeMap, + &KtxImporterTest::imageCubeMapLayers, + &KtxImporterTest::imageCubeMapMipmaps, + + &KtxImporterTest::image3D, + &KtxImporterTest::image3DMipmaps, + &KtxImporterTest::image3DLayers, - & KtxImporterTest::image1D, - & KtxImporterTest::image1DMipmaps, - & KtxImporterTest::image2DMipmaps, + &KtxImporterTest::keyValueDataEmpty}); - &KtxImporterTest::keyValueDataEmpty, - &KtxImporterTest::keyValueDataInvalid, + addInstancedTests({&KtxImporterTest::keyValueDataInvalid}, + Containers::arraySize(InvalidKeyValueData)); - &KtxImporterTest::orientationEmpty, - &KtxImporterTest::orientationInvalid, - &KtxImporterTest::orientationYDown, - &KtxImporterTest::orientationYUp, - &KtxImporterTest::orientationFlipCompressed, + addInstancedTests({&KtxImporterTest::keyValueDataInvalidIgnored}, + Containers::arraySize(IgnoredInvalidKeyValueData)); - &KtxImporterTest::cube, - &KtxImporterTest::cubeIncomplete}); + addInstancedTests({&KtxImporterTest::orientationInvalid}, + Containers::arraySize(InvalidOrientationData)); + + addInstancedTests({&KtxImporterTest::orientationFlip}, + Containers::arraySize(FlipData)); + + addTests({&KtxImporterTest::orientationFlipCompressed}); addInstancedTests({&KtxImporterTest::swizzle}, Containers::arraySize(SwizzleData)); @@ -283,8 +397,8 @@ void KtxImporterTest::openShort() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); - const auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2")); - CORRADE_COMPARE(fileData.size(), 288u); + const auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2")); + CORRADE_INTERNAL_ASSERT(data.length < fileData.size()); std::ostringstream out; Error redirectError{&out}; @@ -299,7 +413,7 @@ void KtxImporterTest::invalid() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, data.file)); - CORRADE_VERIFY(!fileData.empty()); + CORRADE_INTERNAL_ASSERT(data.offset < fileData.size()); fileData[data.offset] = data.value; @@ -323,13 +437,15 @@ void KtxImporterTest::invalidVersion() { void KtxImporterTest::invalidFormat() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); - auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2")); + auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2")); CORRADE_VERIFY(fileData.size() >= sizeof(Implementation::KtxHeader)); Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); - /* Implementation::VkFormat only contains swizzled 8-bit formats. Taken - from magnum/src/MagnumExternal/Vulkan/flextVk.h (9d4a8b49943a084cff64550792bb2eba223e0e03) */ + /* Selected unsupported formats. Implementation::VkFormat only contains + swizzled 8-bit formats so we have to define our own. + Taken from magnum/src/MagnumExternal/Vulkan/flextVk.h + (commit 9d4a8b49943a084cff64550792bb2eba223e0e03) */ enum VkFormat : UnsignedInt { VK_FORMAT_R4G4_UNORM_PACK8 = 1, VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8, @@ -341,13 +457,7 @@ void KtxImporterTest::invalidFormat() { VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM = 1000156002, VK_FORMAT_R10X6G10X6_UNORM_2PACK16 = 1000156008, VK_FORMAT_G16B16G16R16_422_UNORM = 1000156027, - VK_FORMAT_B16G16R16G16_422_UNORM = 1000156028, - VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM = 1000156029, - VK_FORMAT_G16_B16R16_2PLANE_420_UNORM = 1000156030, - VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM = 1000156031, - VK_FORMAT_G16_B16R16_2PLANE_422_UNORM = 1000156032, - VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM = 1000156033, - VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006, + VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006 }; constexpr Implementation::VkFormat formats[]{ @@ -430,19 +540,19 @@ void KtxImporterTest::texture() { CORRADE_COMPARE(counts[dimensions - 1], total); } -void KtxImporterTest::rgb8() { +void KtxImporterTest::image1D() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d.ktx2"))); - CORRADE_COMPARE(importer->image2DCount(), 1); - CORRADE_COMPARE(importer->image2DLevelCount(0), 1); + CORRADE_COMPARE(importer->image1DCount(), 1); + CORRADE_COMPARE(importer->image1DLevelCount(0), 1); - auto image = importer->image2D(0); + auto image = importer->image1D(0); CORRADE_VERIFY(image); CORRADE_VERIFY(!image->isCompressed()); CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); - CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); + CORRADE_COMPARE(image->size(), (Math::Vector<1, Int>{4})); const PixelStorage storage = image->storage(); CORRADE_COMPARE(storage.alignment(), 4); @@ -450,12 +560,59 @@ void KtxImporterTest::rgb8() { CORRADE_COMPARE(storage.imageHeight(), 0); CORRADE_COMPARE(storage.skip(), Vector3i{}); - CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(PatternRgb2DData), TestSuite::Compare::Container); + const Color3ub data[4]{ + 0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x007f7f_rgb + }; + + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(data), TestSuite::Compare::Container); +} + +void KtxImporterTest::image1DMipmaps() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d-mipmaps.ktx2"))); + + const Color3ub mip0[4]{0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x007f7f_rgb}; + const Color3ub mip1[2]{0xffffff_rgb, 0x007f7f_rgb}; + const Color3ub mip2[1]{0x000000_rgb}; + const Containers::ArrayView mipViews[3]{mip0, mip1, mip2}; + + CORRADE_COMPARE(importer->image1DCount(), 1); + CORRADE_COMPARE(importer->image1DLevelCount(0), Containers::arraySize(mipViews)); + + Math::Vector<1, Int> mipSize{4}; + for(UnsignedInt i = 0; i != importer->image1DLevelCount(0); ++i) { + CORRADE_ITERATION(i); + + auto image = importer->image1D(0, i); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); + CORRADE_COMPARE(image->size(), mipSize); + + const PixelStorage storage = image->storage(); + /* Alignment is 4 when row length is a multiple of 4 */ + const Int alignment = ((mipSize[0]*image->pixelSize())%4 == 0) ? 4 : 1; + CORRADE_COMPARE(storage.alignment(), alignment); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(mipViews[i]), TestSuite::Compare::Container); + + mipSize = Math::max(mipSize >> 1, 1); + } } -void KtxImporterTest::rgba8() { +void KtxImporterTest::image1DLayers() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgba.ktx2"))); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d-layers.ktx2"))); + + const Color3ub data[4*3]{ + 0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x007f7f_rgb, + 0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x007f7f_rgb, + 0x000000_rgb, 0x000000_rgb, 0x000000_rgb, 0x000000_rgb + }; CORRADE_COMPARE(importer->image2DCount(), 1); CORRADE_COMPARE(importer->image2DLevelCount(0), 1); @@ -464,7 +621,7 @@ void KtxImporterTest::rgba8() { CORRADE_VERIFY(image); CORRADE_VERIFY(!image->isCompressed()); - CORRADE_COMPARE(image->format(), PixelFormat::RGBA8Srgb); + CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); const PixelStorage storage = image->storage(); @@ -473,12 +630,15 @@ void KtxImporterTest::rgba8() { CORRADE_COMPARE(storage.imageHeight(), 0); CORRADE_COMPARE(storage.skip(), Vector3i{}); - CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(PatternRgba2DData), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(data), TestSuite::Compare::Container); } -void KtxImporterTest::image1D() { +void KtxImporterTest::image1DCompressed() { + auto&& data = CompressedImage1DData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d.ktx2"))); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, data.file))); CORRADE_COMPARE(importer->image1DCount(), 1); CORRADE_COMPARE(importer->image1DLevelCount(0), 1); @@ -486,40 +646,83 @@ void KtxImporterTest::image1D() { auto image = importer->image1D(0); CORRADE_VERIFY(image); + CORRADE_VERIFY(image->isCompressed()); + CORRADE_COMPARE(image->compressedFormat(), data.format); + CORRADE_COMPARE(image->size(), data.size); + + const CompressedPixelStorage storage = image->compressedStorage(); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + /** @todo Can we test ground truth data here? Probably have to generate it + using KtxImageConverter. */ +} + +void KtxImporterTest::image2D() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2"))); + + CORRADE_COMPARE(importer->image2DCount(), 1); + CORRADE_COMPARE(importer->image2DLevelCount(0), 1); + + auto image = importer->image2D(0); + CORRADE_VERIFY(image); + CORRADE_VERIFY(!image->isCompressed()); CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); - CORRADE_COMPARE(image->size(), (Math::Vector<1, Int>{3})); + CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); const PixelStorage storage = image->storage(); - CORRADE_COMPARE(storage.alignment(), 1); + CORRADE_COMPARE(storage.alignment(), 4); CORRADE_COMPARE(storage.rowLength(), 0); CORRADE_COMPARE(storage.imageHeight(), 0); CORRADE_COMPARE(storage.skip(), Vector3i{}); - const Color3ub data[3]{ - 0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb - }; + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(PatternRgbData[0]), TestSuite::Compare::Container); +} - CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(data), TestSuite::Compare::Container); +void KtxImporterTest::image2DRgba() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgba.ktx2"))); + + CORRADE_COMPARE(importer->image2DCount(), 1); + CORRADE_COMPARE(importer->image2DLevelCount(0), 1); + + auto image = importer->image2D(0); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGBA8Srgb); + CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); + + const PixelStorage storage = image->storage(); + CORRADE_COMPARE(storage.alignment(), 4); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(PatternRgba2DData), TestSuite::Compare::Container); } -void KtxImporterTest::image1DMipmaps() { +void KtxImporterTest::image2DMipmaps() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d-mipmaps.ktx2"))); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-mipmaps.ktx2"))); - const Containers::Array mipData[2]{ - {InPlaceInit, {0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb}}, - {InPlaceInit, {0xffffff_rgb}} - }; + /* Is there a nicer way to get a flat view for a multi-dimensional array? */ + const auto mip0 = Containers::arrayCast(Containers::arrayView(PatternRgbData[0])); + const Color3ub mip1[2]{0xffffff_rgb, 0x007f7f_rgb}; + const Color3ub mip2[1]{0x000000_rgb}; + const Containers::ArrayView mipViews[3]{mip0, mip1, mip2}; - CORRADE_COMPARE(importer->image1DCount(), 1); - CORRADE_COMPARE(importer->image1DLevelCount(0), Containers::arraySize(mipData)); + CORRADE_COMPARE(importer->image2DCount(), 1); + CORRADE_COMPARE(importer->image2DLevelCount(0), Containers::arraySize(mipViews)); - Math::Vector<1, Int> mipSize{3}; - for(UnsignedInt i = 0; i != importer->image1DLevelCount(0); ++i) { + Vector2i mipSize{4, 3}; + for(UnsignedInt i = 0; i != importer->image2DLevelCount(0); ++i) { CORRADE_ITERATION(i); - auto image = importer->image1D(0, i); + auto image = importer->image2D(0, i); CORRADE_VERIFY(image); CORRADE_VERIFY(!image->isCompressed()); @@ -527,32 +730,29 @@ void KtxImporterTest::image1DMipmaps() { CORRADE_COMPARE(image->size(), mipSize); const PixelStorage storage = image->storage(); - CORRADE_COMPARE(storage.alignment(), 1); + /* Alignment is 4 when row length is a multiple of 4 */ + const Int alignment = ((mipSize.x()*image->pixelSize())%4 == 0) ? 4 : 1; + CORRADE_COMPARE(storage.alignment(), alignment); CORRADE_COMPARE(storage.rowLength(), 0); CORRADE_COMPARE(storage.imageHeight(), 0); CORRADE_COMPARE(storage.skip(), Vector3i{}); - CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(mipData[i]), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(mipViews[i]), TestSuite::Compare::Container); mipSize = Math::max(mipSize >> 1, 1); } } -void KtxImporterTest::image2DMipmaps() { +void KtxImporterTest::image2DMipmapsIncomplete() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb-mipmaps.ktx2"))); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-mipmaps-incomplete.ktx2"))); - /* Is there a nicer way to get a flat view for a nested array? */ - const auto mipData0 = Containers::arrayCast(Containers::arrayView(PatternRgb2DData)); - Containers::Array mipData[3]{ - Containers::Array{mipData0.size()}, - {InPlaceInit, {0xff0000_rgb, 0x000000_rgb}}, - {InPlaceInit, {0x000000_rgb}} - }; - Utility::copy(mipData0, mipData[0]); + const auto mip0 = Containers::arrayCast(Containers::arrayView(PatternRgbData[0])); + const Color3ub mip1[2]{0xffffff_rgb, 0x007f7f_rgb}; + const Containers::ArrayView mipViews[2]{mip0, mip1}; CORRADE_COMPARE(importer->image2DCount(), 1); - CORRADE_COMPARE(importer->image2DLevelCount(0), Containers::arraySize(mipData)); + CORRADE_COMPARE(importer->image2DLevelCount(0), Containers::arraySize(mipViews)); Vector2i mipSize{4, 3}; for(UnsignedInt i = 0; i != importer->image2DLevelCount(0); ++i) { @@ -560,6 +760,277 @@ void KtxImporterTest::image2DMipmaps() { auto image = importer->image2D(0, i); CORRADE_VERIFY(image); + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); + CORRADE_COMPARE(image->size(), mipSize); + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(mipViews[i]), TestSuite::Compare::Container); + + mipSize = Math::max(mipSize >> 1, 1); + } +} + +void KtxImporterTest::image2DLayers() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-layers.ktx2"))); + + CORRADE_COMPARE(importer->image3DCount(), 1); + CORRADE_COMPARE(importer->image3DLevelCount(0), 1); + + auto image = importer->image3D(0); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); + CORRADE_COMPARE(image->size(), (Vector3i{4, 3, 3})); + + const PixelStorage storage = image->storage(); + CORRADE_COMPARE(storage.alignment(), 4); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(PatternRgbData), TestSuite::Compare::Container); +} + +void KtxImporterTest::image2DCompressed() { + auto&& data = CompressedImage2DData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, data.file))); + + CORRADE_COMPARE(importer->image2DCount(), 1); + CORRADE_COMPARE(importer->image2DLevelCount(0), 1); + + auto image = importer->image2D(0); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(image->isCompressed()); + CORRADE_COMPARE(image->compressedFormat(), data.format); + CORRADE_COMPARE(image->size(), data.size); + + const CompressedPixelStorage storage = image->compressedStorage(); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + /** @todo Can we test ground truth data here? Probably have to generate it + using KtxImageConverter. */ +} + +/* Origin bottom-left. There's some weird color shift happening in the test + files, probably the sampling in PVRTexTool. Non-white pixels in the original + files are multiples of 0x101010. */ +const Color3ub FacesRgbData[6][2][2]{ + /* cube+x.png */ + {{0xffffff_rgb, 0x0d0d0d_rgb}, + {0x0d0d0d_rgb, 0x0d0d0d_rgb}}, + /* cube-x.png */ + {{0xffffff_rgb, 0x222222_rgb}, + {0x222222_rgb, 0x222222_rgb}}, + /* cube+y.png */ + {{0xffffff_rgb, 0x323232_rgb}, + {0x323232_rgb, 0x323232_rgb}}, + /* cube-y.png */ + {{0xffffff_rgb, 0x404040_rgb}, + {0x404040_rgb, 0x404040_rgb}}, + /* cube+z.png */ + {{0xffffff_rgb, 0x4f4f4f_rgb}, + {0x4f4f4f_rgb, 0x4f4f4f_rgb}}, + /* cube-z.png */ + {{0xffffff_rgb, 0x606060_rgb}, + {0x606060_rgb, 0x606060_rgb}} +}; + +void KtxImporterTest::imageCubeMapIncomplete() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "cubemap.ktx2")); + CORRADE_VERIFY(fileData.size() >= sizeof(Implementation::KtxHeader)); + + constexpr auto key = "KTXcubemapIncomplete"_s; + /* Value is a single byte */ + UnsignedInt size = key.size() + 1 + 1; + size = (size + 3)/4*4; + Containers::Array keyValueData{ValueInit, sizeof(UnsignedInt) + size}; + + std::size_t offset = 0; + *reinterpret_cast(keyValueData.data()) = Utility::Endianness::littleEndian(size); + offset += sizeof(size); + Utility::copy(key, keyValueData.suffix(offset).prefix(key.size())); + offset += key.size() + 1; + keyValueData[offset] = 0x3f; + + { + Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); + header.layerCount = Utility::Endianness::littleEndian(6u); + header.faceCount = Utility::Endianness::littleEndian(1u); + + Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); + + CORRADE_VERIFY(header.kvdByteLength >= keyValueData.size()); + header.kvdByteLength = keyValueData.size(); + Utility::copy(keyValueData, fileData.suffix(header.kvdByteOffset).prefix(header.kvdByteLength)); + + Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); + } + + std::ostringstream outWarning; + Warning redirectWarning{&outWarning}; + + CORRADE_VERIFY(importer->openData(fileData)); + CORRADE_COMPARE(outWarning.str(), + "Trade::KtxImporter::openData(): missing or invalid orientation, assuming right, down\n" + "Trade::KtxImporter::openData(): image contains incomplete cube map faces, importing faces as array layers\n"); + + CORRADE_COMPARE(importer->image3DCount(), 1); + CORRADE_COMPARE(importer->image3DLevelCount(0), 1); + + auto image = importer->image3D(0); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); + CORRADE_COMPARE(image->size(), (Vector3i{2, 2, 6})); + + const PixelStorage storage = image->storage(); + CORRADE_COMPARE(storage.alignment(), 1); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(FacesRgbData), TestSuite::Compare::Container); +} + +void KtxImporterTest::imageCubeMap() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "cubemap.ktx2"))); + + CORRADE_COMPARE(importer->image3DCount(), 1); + CORRADE_COMPARE(importer->image3DLevelCount(0), 1); + + auto image = importer->image3D(0); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); + CORRADE_COMPARE(image->size(), (Vector3i{2, 2, 6})); + + const PixelStorage storage = image->storage(); + CORRADE_COMPARE(storage.alignment(), 1); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(FacesRgbData), TestSuite::Compare::Container); +} + +void KtxImporterTest::imageCubeMapMipmaps() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "cubemap-mipmaps.ktx2"))); + + const auto mip0 = Containers::arrayCast(Containers::arrayView(FacesRgbData)); + const Color3ub mip1[1*1*6]{ + FacesRgbData[0][1][0], + FacesRgbData[1][1][0], + FacesRgbData[2][1][0], + FacesRgbData[3][1][0], + FacesRgbData[4][1][0], + FacesRgbData[5][1][0] + }; + const Containers::ArrayView mipViews[2]{mip0, mip1}; + + CORRADE_COMPARE(importer->image3DCount(), 1); + CORRADE_COMPARE(importer->image3DLevelCount(0), Containers::arraySize(mipViews)); + + Vector2i mipSize{2, 2}; + for(UnsignedInt i = 0; i != importer->image3DLevelCount(0); ++i) { + CORRADE_ITERATION(i); + + auto image = importer->image3D(0, i); + CORRADE_VERIFY(image); + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); + CORRADE_COMPARE(image->size(), (Vector3i{mipSize, 6})); + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(mipViews[i]), TestSuite::Compare::Container); + + mipSize = Math::max(mipSize >> 1, 1); + } +} + +void KtxImporterTest::imageCubeMapLayers() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "cubemap-layers.ktx2"))); + + CORRADE_COMPARE(importer->image3DCount(), 1); + CORRADE_COMPARE(importer->image3DLevelCount(0), 1); + + auto image = importer->image3D(0); + CORRADE_VERIFY(image); + + constexpr UnsignedInt NumLayers = 2; + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); + CORRADE_COMPARE(image->size(), (Vector3i{2, 2, NumLayers*6})); + + const PixelStorage storage = image->storage(); + CORRADE_COMPARE(storage.alignment(), 1); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + const UnsignedInt faceSize = image->data().size()/NumLayers; + + for(UnsignedInt i = 0; i != NumLayers; ++i) { + CORRADE_ITERATION(i); + CORRADE_COMPARE_AS(image->data().suffix(i*faceSize).prefix(faceSize), Containers::arrayCast(FacesRgbData), TestSuite::Compare::Container); + } +} + +void KtxImporterTest::image3D() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d.ktx2"))); + + CORRADE_COMPARE(importer->image3DCount(), 1); + CORRADE_COMPARE(importer->image3DLevelCount(0), 1); + + auto image = importer->image3D(0); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); + CORRADE_COMPARE(image->size(), (Vector3i{4, 3, 3})); + + const PixelStorage storage = image->storage(); + CORRADE_COMPARE(storage.alignment(), 4); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(PatternRgbData), TestSuite::Compare::Container); +} + +void KtxImporterTest::image3DMipmaps() { + /** @todo */ + CORRADE_SKIP("Need a valid test file"); + + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d-mipmaps.ktx2"))); + + const auto mip0 = Containers::arrayCast(Containers::arrayView(PatternRgbData)); + const Color3ub mip1[2]{0x000000_rgb, 0x000000_rgb}; + const Color3ub mip2[1]{0x000000_rgb}; + const Containers::ArrayView mipViews[3]{mip0, mip1, mip2}; + + CORRADE_COMPARE(importer->image3DCount(), 1); + CORRADE_COMPARE(importer->image3DLevelCount(0), Containers::arraySize(mipViews)); + + Vector3i mipSize{4, 3, 3}; + for(UnsignedInt i = 0; i != importer->image3DLevelCount(0); ++i) { + CORRADE_ITERATION(i); + + auto image = importer->image3D(0, i); + CORRADE_VERIFY(image); CORRADE_VERIFY(!image->isCompressed()); CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); @@ -573,81 +1044,194 @@ void KtxImporterTest::image2DMipmaps() { CORRADE_COMPARE(storage.imageHeight(), 0); CORRADE_COMPARE(storage.skip(), Vector3i{}); - CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(mipData[i]), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(mipViews[i]), TestSuite::Compare::Container); mipSize = Math::max(mipSize >> 1, 1); } } +void KtxImporterTest::image3DLayers() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d-layers.ktx2"))); + + const auto layer0 = Containers::arrayCast(PatternRgbData); + /* Pattern, black, black*/ + Color3ub layer1Data[3][3][4]{}; + Utility::copy(Containers::arrayView(PatternRgbData[0]), layer1Data[0]); + const auto layer1 = Containers::arrayCast(layer1Data); + + const Containers::ArrayView imageViews[2]{layer0, layer1}; + + CORRADE_COMPARE(importer->image3DCount(), Containers::arraySize(imageViews)); + + for(UnsignedInt i = 0; i != importer->image3DCount(); ++i) { + CORRADE_ITERATION(i); + + CORRADE_COMPARE(importer->image3DLevelCount(i), 1); + auto image = importer->image3D(i); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); + CORRADE_COMPARE(image->size(), (Vector3i{4, 3, 3})); + + const PixelStorage storage = image->storage(); + CORRADE_COMPARE(storage.alignment(), 4); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(imageViews[i]), TestSuite::Compare::Container); + } +} + void KtxImporterTest::keyValueDataEmpty() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2")); + CORRADE_VERIFY(fileData.size() >= sizeof(Implementation::KtxHeader)); - /** @todo */ + { + Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); + header.kvdByteLength = Utility::Endianness::littleEndian(0u); + } + + std::ostringstream outWarning; + Warning redirectWarning{&outWarning}; + + CORRADE_VERIFY(importer->openData(fileData)); + /* This test doubles for empty orientation data, but there should be no + other warnings */ + CORRADE_COMPARE(outWarning.str(), "Trade::KtxImporter::openData(): missing or invalid orientation, assuming right, down\n"); } void KtxImporterTest::keyValueDataInvalid() { + auto&& data = InvalidKeyValueData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + /* Invalid key/value data that might hint at a broken file so the importer + should warn and try to continue the import */ + Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2")); + CORRADE_VERIFY(fileData.size() >= sizeof(Implementation::KtxHeader)); - /** @todo */ + { + Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); + + Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); + + CORRADE_VERIFY(header.kvdByteLength >= data.data.size()); + header.kvdByteLength = data.data.size(); + Utility::copy(data.data, fileData.suffix(header.kvdByteOffset).prefix(header.kvdByteLength)); + + Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); + } + + std::ostringstream outWarning; + Warning redirectWarning{&outWarning}; + + /* Import succeeds with a warning */ + CORRADE_VERIFY(importer->openData(fileData)); + CORRADE_COMPARE(outWarning.str(), Utility::formatString( + "Trade::KtxImporter::openData(): {}\n" + "Trade::KtxImporter::openData(): missing or invalid orientation, assuming right, down\n", + data.message)); } -void KtxImporterTest::orientationEmpty() { +void KtxImporterTest::keyValueDataInvalidIgnored() { + auto&& data = IgnoredInvalidKeyValueData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + /* "Invalid" (according to the spec) key/value data that can just be + ignored without warning because it doesn't affect the import */ + Containers::Pointer importer = _manager.instantiate("KtxImporter"); - importer->addFlags(ImporterFlag::Verbose); + auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2")); + CORRADE_VERIFY(fileData.size() >= sizeof(Implementation::KtxHeader)); - std::ostringstream outDebug; - Debug redirectDebug{&outDebug}; + { + Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); + + Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); + + CORRADE_VERIFY(header.kvdByteLength >= data.data.size()); + header.kvdByteLength = data.data.size(); + Utility::copy(data.data, fileData.suffix(header.kvdByteOffset).prefix(header.kvdByteLength)); + + Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); + } std::ostringstream outWarning; Warning redirectWarning{&outWarning}; - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "orientation-empty.ktx2"))); + /* No warning besides missing orientation */ + CORRADE_VERIFY(importer->openData(fileData)); CORRADE_COMPARE(outWarning.str(), "Trade::KtxImporter::openData(): missing or invalid orientation, assuming right, down\n"); - CORRADE_COMPARE(outDebug.str(), "Trade::KtxImporter::openData(): image will be flipped along y\n"); } void KtxImporterTest::orientationInvalid() { + auto&& data = InvalidOrientationData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, data.file)); + CORRADE_VERIFY(fileData.size() >= sizeof(Implementation::KtxHeader)); - /** @todo */ -} + constexpr auto key = "KTXorientation"_s; + UnsignedInt size = key.size() + 1 + data.orientation.size() + 1; + size = (size + 3)/4*4; + Containers::Array keyValueData{ValueInit, sizeof(UnsignedInt) + size}; -void KtxImporterTest::orientationYDown() { - Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + std::size_t offset = 0; + *reinterpret_cast(keyValueData.data()) = Utility::Endianness::littleEndian(size); + offset += sizeof(size); + Utility::copy(key, keyValueData.suffix(offset).prefix(key.size())); + offset += key.size() + 1; + Utility::copy(data.orientation, keyValueData.suffix(offset).prefix(data.orientation.size())); - /** @todo */ -} + { + Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); -void KtxImporterTest::orientationYUp() { - Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); - /** @todo */ -} + CORRADE_VERIFY(header.kvdByteLength >= keyValueData.size()); + header.kvdByteLength = keyValueData.size(); + Utility::copy(keyValueData, fileData.suffix(header.kvdByteOffset).prefix(header.kvdByteLength)); -void KtxImporterTest::orientationFlipCompressed() { - Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); + } - /** @todo */ + std::ostringstream outWarning; + Warning redirectWarning{&outWarning}; + + constexpr Containers::StringView orientations[]{"right"_s, "down"_s, "forward"_s}; + const Containers::String orientationString = ", "_s.join(Containers::arrayView(orientations).prefix(data.dimensions)); + + CORRADE_VERIFY(importer->openData(fileData)); + CORRADE_COMPARE(outWarning.str(), Utility::formatString("Trade::KtxImporter::openData(): missing or invalid orientation, assuming {}\n", orientationString)); } -void KtxImporterTest::cube() { +void KtxImporterTest::orientationFlip() { + auto&& data = FlipData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2"))); /** @todo */ } -void KtxImporterTest::cubeIncomplete() { +void KtxImporterTest::orientationFlipCompressed() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); - /** @todo */ + std::ostringstream outWarning; + Warning redirectWarning{&outWarning}; + + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-compressed-bc1.ktx2"))); + CORRADE_COMPARE(outWarning.str(), + "Trade::KtxImporter::openData(): block-compressed image " + "was encoded with non-default axis orientations, imported data " + "will have wrong orientation\n"); } void KtxImporterTest::swizzle() { @@ -673,7 +1257,7 @@ void KtxImporterTest::swizzle() { CORRADE_VERIFY(importer->openData(fileData)); /** @todo Change origin to top-left for the swizzle test files so we can - check the relevant messages only. Relevant for swizzleIdentity(), too */ + check the relevant messages only. Also for swizzleIdentity(). */ std::string expectedMessage = "Trade::KtxImporter::openData(): image will be flipped along y\n"; if(data.message) expectedMessage += Utility::formatString("Trade::KtxImporter::openData(): {}\n", data.message); @@ -718,11 +1302,11 @@ void KtxImporterTest::swizzleUnsupported() { void KtxImporterTest::openTwice() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2"))); CORRADE_COMPARE(importer->image2DCount(), 1); CORRADE_COMPARE(importer->textureCount(), 1); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2"))); CORRADE_COMPARE(importer->image2DCount(), 1); CORRADE_COMPARE(importer->textureCount(), 1); @@ -731,7 +1315,7 @@ void KtxImporterTest::openTwice() { void KtxImporterTest::importTwice() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "rgb.ktx2"))); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2"))); /* Verify that everything is working the same way on second use */ { diff --git a/src/MagnumPlugins/KtxImporter/Test/README.md b/src/MagnumPlugins/KtxImporter/Test/README.md index 13cbb5335..4db3ba8ab 100644 --- a/src/MagnumPlugins/KtxImporter/Test/README.md +++ b/src/MagnumPlugins/KtxImporter/Test/README.md @@ -1,7 +1,6 @@ Updating test files =================== -The `*.ktx2?` files are created using the toktx tool from -[Khronos Texture Tools](https://github.com/KhronosGroup/KTX-Software). +The `*.ktx2?` files are created using [Khronos Texture Tools](https://github.com/KhronosGroup/KTX-Software) as well as [PVRTexTool](https://developer.imaginationtech.com/pvrtextool/). -Simply execute the `generate.sh` script to create them. +Install both of those and then execute the `generate.sh` script to create the test files. diff --git a/src/MagnumPlugins/KtxImporter/Test/black1d.png b/src/MagnumPlugins/KtxImporter/Test/black1d.png new file mode 100644 index 0000000000000000000000000000000000000000..8a84c24e3a935cccf8fc045ab1ac159c420b8c21 GIT binary patch literal 119 zcmeAS@N?(olHy`uVBq!ia0vp^EI`c2!2~4B;%3YOaf*Z7ofy`glX(e}O?UKlWMJ6X z&;2Kn70Bl-@Q5sCVBp&i!i*NI!74yO9#0p?5Q*?)2L?7apur4`_Lem^Kye07S3j3^ HP6;OXk;vd$@?2>`3E9YFv9 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/cube+y.png b/src/MagnumPlugins/KtxImporter/Test/cube+y.png new file mode 100644 index 0000000000000000000000000000000000000000..c5ecc105c94a3765c3e7352b5dbe15ce1ae38ddc GIT binary patch literal 128 zcmeAS@N?(olHy`uVBq!ia0vp^Od!kwBL7~QRScvUi-X*q7}lMWc?smOq&xaLGB9lH z=l+w(3gmMZctipf@$Cm;Mhn(p6`-J~r;B5VM0m1^fk6U8)Bpefr6nX77{uPP$Zyzu RS_Y_q!PC{xWt~$(69C7Q9)L*xv Rn*tRuc)I$ztaD0e0sz_m9;pBT literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/cube-z.png b/src/MagnumPlugins/KtxImporter/Test/cube-z.png new file mode 100644 index 0000000000000000000000000000000000000000..b88d5e0b243aefe9c3835b85e9c3a8f8f15d3924 GIT binary patch literal 128 zcmeAS@N?(olHy`uVBq!ia0vp^Od!kwBL7~QRScvUi-X*q7}lMWc?smOq&xaLGB9lH z=l+w(3gmMZctipf@$Cm;Mhn(p6`-J~r;B5VM0m1~herZK)Bpef)m4DPa%WlXUlq^X Q4phM4>FVdQ&MBb@0NJr0UH||9 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/cubemap-layers.ktx2 b/src/MagnumPlugins/KtxImporter/Test/cubemap-layers.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..df4d0a0172fbc1307568bafabdc4708f29aca29d GIT binary patch literal 384 zcmZ4O9TK5nWU!l;ONvXDfq{V$h?$@mNP*dGKpG?t0vSN;1H@Z^*aC>r!3U_o1T-3? z2LzZHJQx@mK{hZjFoQ`D!47Br2eLRA*kRT&fcOFo><|q=Hi$0-#6V}~7iFgAl_ZvA z=I1dKr2xgGA>!plnI)-33;|(5A*mHUnMn#|rh0~Y20%$5;01&K|NjGNB`^T8fwU1A c0NFs=0Stg_AngwZKsJz000ST!NK?xk0O;gZJ^%m! literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/cubemap-mipmaps.ktx2 b/src/MagnumPlugins/KtxImporter/Test/cubemap-mipmaps.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..69865d0ce363ae40f1818745134e790c40937c0c GIT binary patch literal 360 zcmZ9H%?g505QRsB6hVlVMIb(am=Lsd86+4X(b6IpZWKhSW$)7?^a{O`eb>K5AIzC^ zk8?+T?~}6=_#fL^SSu0HD7`OQ%M?|a;zdQ&m)cVQIBEgxx;A#R&*yzUbhS&NGpT9a zTP9tE8l!7)rWpMe5p|x4F??2gRy+BfvdXifzSMbHNOdDycKM^q>#UN)>6m2CD8D-Q vjkorE9)fM#uImOt5QbqK$7!0jvC5%o8k7ORqf7!GDtvfH}4aT3ey6A(S z|C1qPp6lWerO7)C#=(?`Jjiba6Z)t;XRc5Vy`Ux3<b zAi%`n!NADK1ZE@bUU|@$?2j&YfutPLJ<+Xt{(AnihnI)-33?=#5B^3%~ qCVB>X26bi%`U*LjNk}55K*c~){xj4w)C19fAOML##2BEw{|o?CWhE&9 diff --git a/src/MagnumPlugins/KtxImporter/Test/pattern-1d-uneven.png b/src/MagnumPlugins/KtxImporter/Test/pattern-1d-uneven.png new file mode 100644 index 0000000000000000000000000000000000000000..cdfd6b9bc53a5f7b0dcb968325cdc2b6c66ff30c GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)O!2~4dW-8AFaf*Z7ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!GcfS&2Vq7F)?gK&pp>VJV~9j}@{jWeKK!?zkdUxq{eS)xCc(q? Z389ZJ6T-G@yGywprE+75? literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/pattern-mip1.png b/src/MagnumPlugins/KtxImporter/Test/pattern-mip1.png index d57b14659f85743b34e4f99d1c99d2b738a8049f..a08eab059725705923c8095f17807b6ad8a5cf4f 100644 GIT binary patch delta 33 ncmb=bnh+=R@BjaLWoBjufeFk_6(-ZI8Gyjk)z4*}Q$iB})+`G+ delta 33 lcmb=bnh+=Rzmvv4FO#s!K3djHe diff --git a/src/MagnumPlugins/KtxImporter/Test/pattern-mips.txt b/src/MagnumPlugins/KtxImporter/Test/pattern-mips.txt deleted file mode 100644 index 10ee359d2..000000000 --- a/src/MagnumPlugins/KtxImporter/Test/pattern-mips.txt +++ /dev/null @@ -1,3 +0,0 @@ -pattern.png -pattern-mip1.png -pattern-mip2.png diff --git a/src/MagnumPlugins/KtxImporter/Test/pattern-pot.png b/src/MagnumPlugins/KtxImporter/Test/pattern-pot.png new file mode 100644 index 0000000000000000000000000000000000000000..99f8dc837e0f0cee8700de1b4c7d8d51ca1e4cef GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1SGw4HSYi^#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DkxL735kHCP2GsO0J57$Om#{O5cF1250l05N7};VH)$mUu6? h(a5tRjh&g9;l%}J9`2T7K%*EKJYD@<);T3K0RR==A?^SG literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/pattern-uneven.png b/src/MagnumPlugins/KtxImporter/Test/pattern-uneven.png new file mode 100644 index 0000000000000000000000000000000000000000..a14d6939461a35c2f046ca8c78f199059ba398bd GIT binary patch literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^oIuRQ!2~2-=X$VUz sg!3J>OJSI@ptF`|ht84Z{f&$a6HD3oqY^hd12r;uy85}Sb4q9e0B~j}&j0`b literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/pattern1d-mip1.png b/src/MagnumPlugins/KtxImporter/Test/pattern1d-mip1.png deleted file mode 100644 index e3ba2f73621d9f95f881979b7e6018e820fe78d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 119 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1SBVv2j2s6ii6yp7}lMWc?smOq&xaLGB9lH z=l+w(3gmMZctjR6F!1dMVMYtqU=^SskEe@ch(vhukN^Me8Cd@@{abF@*a8%1@O1Ta JS?83{1OTgh9KZko diff --git a/src/MagnumPlugins/KtxImporter/Test/pattern1d-mips.txt b/src/MagnumPlugins/KtxImporter/Test/pattern1d-mips.txt deleted file mode 100644 index 1e2f5ae78..000000000 --- a/src/MagnumPlugins/KtxImporter/Test/pattern1d-mips.txt +++ /dev/null @@ -1,2 +0,0 @@ -pattern1d.png -pattern1d-mip1.png diff --git a/src/MagnumPlugins/KtxImporter/Test/pattern1d.png b/src/MagnumPlugins/KtxImporter/Test/pattern1d.png deleted file mode 100644 index 49df607855bd36487edcc755da831931b54b333c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 125 zcmeAS@N?(olHy`uVBq!ia0vp^%s|Y@!2~3yG&Mg4af*Z7ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!GcfS&2Vq7F)?gK&ppd7FV~9j}@{jWeKK%b*&&I~aApVK@?*}=? Q#XwmGPgg&ebxsLQ0FiqAZc)I$ztaD0e0swsA91Q>f From 89398d1fad44720973cea017ca1dab13bee80260 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 30 Jul 2021 23:09:13 +0200 Subject: [PATCH 32/95] KtxImageConverter: we don't need this --- .../KtxImageConverter/KtxImageConverter.cpp | 62 +------------------ 1 file changed, 1 insertion(+), 61 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 33270eb9c..fe2d6c9c3 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -217,66 +217,6 @@ Containers::Array2 channelMapping(Implementation::VkFormatSuffix su CORRADE_ASSERT_UNREACHABLE("channelLimits(): invalid format suffix" << UnsignedInt(suffix), {}); } -bool isSrgb(PixelFormat format) { - CORRADE_INTERNAL_ASSERT(!isPixelFormatImplementationSpecific(format)); - - switch(format) { - case PixelFormat::R8Srgb: - case PixelFormat::RG8Srgb: - case PixelFormat::RGB8Srgb: - case PixelFormat::RGBA8Srgb: - return true; - default: - return false; - } -} - -bool isSrgb(CompressedPixelFormat format) { - CORRADE_INTERNAL_ASSERT(!isCompressedPixelFormatImplementationSpecific(format)); - - switch(format) { - case CompressedPixelFormat::Bc1RGBSrgb: - case CompressedPixelFormat::Bc1RGBASrgb: - case CompressedPixelFormat::Bc2RGBASrgb: - case CompressedPixelFormat::Bc3RGBASrgb: - case CompressedPixelFormat::Bc7RGBASrgb: - case CompressedPixelFormat::Etc2RGB8Srgb: - case CompressedPixelFormat::Etc2RGB8A1Srgb: - case CompressedPixelFormat::Etc2RGBA8Srgb: - case CompressedPixelFormat::Astc4x4RGBASrgb: - case CompressedPixelFormat::Astc5x4RGBASrgb: - case CompressedPixelFormat::Astc5x5RGBASrgb: - case CompressedPixelFormat::Astc6x5RGBASrgb: - case CompressedPixelFormat::Astc6x6RGBASrgb: - case CompressedPixelFormat::Astc8x5RGBASrgb: - case CompressedPixelFormat::Astc8x6RGBASrgb: - case CompressedPixelFormat::Astc8x8RGBASrgb: - case CompressedPixelFormat::Astc10x5RGBASrgb: - case CompressedPixelFormat::Astc10x6RGBASrgb: - case CompressedPixelFormat::Astc10x8RGBASrgb: - case CompressedPixelFormat::Astc10x10RGBASrgb: - case CompressedPixelFormat::Astc12x10RGBASrgb: - case CompressedPixelFormat::Astc12x12RGBASrgb: - case CompressedPixelFormat::Astc3x3x3RGBASrgb: - case CompressedPixelFormat::Astc4x3x3RGBASrgb: - case CompressedPixelFormat::Astc4x4x3RGBASrgb: - case CompressedPixelFormat::Astc4x4x4RGBASrgb: - case CompressedPixelFormat::Astc5x4x4RGBASrgb: - case CompressedPixelFormat::Astc5x5x4RGBASrgb: - case CompressedPixelFormat::Astc5x5x5RGBASrgb: - case CompressedPixelFormat::Astc6x5x5RGBASrgb: - case CompressedPixelFormat::Astc6x6x5RGBASrgb: - case CompressedPixelFormat::Astc6x6x6RGBASrgb: - case CompressedPixelFormat::PvrtcRGB2bppSrgb: - case CompressedPixelFormat::PvrtcRGBA2bppSrgb: - case CompressedPixelFormat::PvrtcRGB4bppSrgb: - case CompressedPixelFormat::PvrtcRGBA4bppSrgb: - return true; - default: - return false; - } -} - Containers::Array fillDataFormatDescriptor(PixelFormat format, Implementation::VkFormatSuffix suffix) { const UnsignedInt texelSize = pixelSize(format); const UnsignedInt typeSize = componentSize(format); @@ -310,7 +250,7 @@ Containers::Array fillDataFormatDescriptor(PixelFormat format, Implementat header.colorModel = Implementation::KdfBasicBlockHeader::ColorModel::Rgbsda; header.colorPrimaries = Implementation::KdfBasicBlockHeader::ColorPrimaries::Srgb; - header.transferFunction = isSrgb(format) + header.transferFunction = suffix == Implementation::VkFormatSuffix::SRGB ? Implementation::KdfBasicBlockHeader::TransferFunction::Srgb : Implementation::KdfBasicBlockHeader::TransferFunction::Linear; /** @todo Do we ever have premultiplied alpha? */ From bf43072eed351398db846f1b7ddc5ac376a7944b Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 30 Jul 2021 23:15:28 +0200 Subject: [PATCH 33/95] KtxImageConverter: add support for multiple levels --- .../KtxImageConverter/KtxImageConverter.cpp | 283 +++++++++++------- .../KtxImageConverter/KtxImageConverter.h | 15 +- 2 files changed, 189 insertions(+), 109 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index fe2d6c9c3..62881cf31 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -36,21 +36,28 @@ #include #include #include -#include #include -#include #include "MagnumPlugins/KtxImporter/KtxHeader.h" namespace Magnum { namespace Trade { namespace { +/* Need overloaded functions to use different pixel formats in templated code */ +bool isFormatImplementationSpecific(PixelFormat format) { + return isPixelFormatImplementationSpecific(format); +} + +bool isFormatImplementationSpecific(CompressedPixelFormat format) { + return isCompressedPixelFormatImplementationSpecific(format); +} + typedef Containers::Pair FormatPair; FormatPair vulkanFormat(PixelFormat format) { switch(format) { #define _p(vulkan, magnum, type) case PixelFormat::magnum: \ - return {Implementation::VK_FORMAT_ ## vulkan, Implementation::VkFormatSuffix::type}; + return {vulkan, Implementation::VkFormatSuffix::type}; #include "MagnumPlugins/KtxImporter/formatMapping.hpp" #undef _p default: @@ -59,9 +66,32 @@ FormatPair vulkanFormat(PixelFormat format) { } FormatPair vulkanFormat(CompressedPixelFormat format) { + /* In Vulkan there is no distinction between RGB and RGBA PVRTC: + https://github.com/KhronosGroup/Vulkan-Docs/issues/512#issuecomment-307768667 + formatMapping.hpp (generated from Vk::PixelFormat) contains the RGBA + variants, so we manually alias them here. We can't do this inside + formatMapping.hpp because both Magnum and Vulkan formats must be unique + for switch cases. */ + switch(format) { + case CompressedPixelFormat::PvrtcRGB2bppUnorm: + format = CompressedPixelFormat::PvrtcRGBA2bppUnorm; + break; + case CompressedPixelFormat::PvrtcRGB2bppSrgb: + format = CompressedPixelFormat::PvrtcRGBA2bppSrgb; + break; + case CompressedPixelFormat::PvrtcRGB4bppUnorm: + format = CompressedPixelFormat::PvrtcRGBA4bppUnorm; + break; + case CompressedPixelFormat::PvrtcRGB4bppSrgb: + format = CompressedPixelFormat::PvrtcRGBA4bppSrgb; + break; + default: + break; + } + switch(format) { #define _c(vulkan, magnum, type) case CompressedPixelFormat::magnum: \ - return {Implementation::VK_FORMAT_ ## vulkan, Implementation::VkFormatSuffix::type}; + return {vulkan, Implementation::VkFormatSuffix::type}; #include "MagnumPlugins/KtxImporter/formatMapping.hpp" #undef _c default: @@ -70,12 +100,6 @@ FormatPair vulkanFormat(CompressedPixelFormat format) { } UnsignedByte componentSize(PixelFormat format) { - CORRADE_INTERNAL_ASSERT(!isPixelFormatImplementationSpecific(format)); - - #ifdef __GNUC__ - #pragma GCC diagnostic push - #pragma GCC diagnostic error "-Wswitch" - #endif switch(format) { case PixelFormat::R8Unorm: case PixelFormat::RG8Unorm: @@ -139,12 +163,11 @@ UnsignedByte componentSize(PixelFormat format) { case PixelFormat::Depth24UnormStencil8UI: case PixelFormat::Depth32FStencil8UI: return 4; + default: + break; } - #ifdef __GNUC__ - #pragma GCC diagnostic pop - #endif - CORRADE_ASSERT_UNREACHABLE("componentSize(): invalid format" << format, {}); + CORRADE_ASSERT_UNREACHABLE("componentSize(): unsupported format" << format, {}); } UnsignedByte componentSize(CompressedPixelFormat) { @@ -308,35 +331,11 @@ Containers::Array fillDataFormatDescriptor(PixelFormat format, Implementat return data; } -typedef Containers::Pair KeyValuePair; -Containers::Array packKeyValueData(Containers::ArrayView pairs) { /* Calculate size */ - std::size_t kvdSize = 0; - for(const KeyValuePair& entry: pairs) { - const UnsignedInt length = entry.first().size() + 1 + entry.second().size() + 1; - kvdSize += sizeof(length) + (length + 3)/4*4; - } - CORRADE_INTERNAL_ASSERT(kvdSize % 4 == 0); - /* Pack. We assume that values are actual text strings and don't need any - endian-swapping. */ std::size_t offset = 0; - Containers::Array data{ValueInit, kvdSize}; - for(const KeyValuePair& entry: pairs) { - const auto key = entry.first(); - const auto value = entry.second(); - const UnsignedInt length = key.size() + 1 + value.size() + 1; - *reinterpret_cast(data.suffix(offset).data()) = length; - Utility::Endianness::littleEndianInPlace(length); - offset += sizeof(length); - Utility::copy(key, data.suffix(offset).prefix(key.size())); - offset += entry.first().size() + 1; - Utility::copy(value, data.suffix(offset).prefix(value.size())); - offset += entry.second().size() + 1; - offset = (offset + 3)/4*4; } - CORRADE_INTERNAL_ASSERT(offset == kvdSize); return data; } @@ -381,58 +380,151 @@ UnsignedInt leastCommonMultiple(UnsignedInt a, UnsignedInt b) { } template -Containers::Array convertImage(const BasicImageView& image) { - if(isPixelFormatImplementationSpecific(image.format())) { +void copyPixels(const BasicImageView& image, Containers::ArrayView pixels) { + std::size_t sizes[dimensions + 1]; + sizes[dimensions] = image.pixelSize(); + for(UnsignedInt i = 0; i != dimensions; ++i) { + sizes[dimensions - 1 - i] = image.size()[i]; + } + + /* Copy the pixels into output, dropping padding (if any) */ + Utility::copy(image.pixels(), Containers::StridedArrayView{pixels, sizes}); +} + +template +void copyPixels(const BasicCompressedImageView& image, Containers::ArrayView pixels) { + /** @todo How do we deal with CompressedPixelStorage::skip? + ImageView::pixels handles this for non-compressed images. */ + Utility::copy(image.data(), pixels); +} + +/* Using a template template parameter to deduce the image dimensions while + matching both ImageView and CompressedImageView. Matching on the ImageView + typedefs doesn't work, so we need the extra parameter of BasicImageView. */ +template class View> +Containers::Array convertLevels(Containers::ArrayView> imageLevels) { + if(imageLevels.empty()) { + Error() << "Trade::KtxImageConverter::convertToData(): expected at least 1 mip level"; + return {}; + } + + const auto format = imageLevels.front().format(); + + if(isFormatImplementationSpecific(format)) { Error{} << "Trade::KtxImageConverter::convertToData(): implementation-specific formats are not supported"; return {}; } - const auto vkFormat = vulkanFormat(image.format()); + const auto vkFormat = vulkanFormat(format); if(vkFormat.first() == Implementation::VK_FORMAT_UNDEFINED) { - Error{} << "Trade::KtxImageConverter::convertToData(): unsupported format" << image.format(); + Error{} << "Trade::KtxImageConverter::convertToData(): unsupported format" << format; return {}; } - const Containers::Array dataFormatDescriptor = fillDataFormatDescriptor(image.format(), vkFormat.second()); + const Containers::Array dataFormatDescriptor = fillDataFormatDescriptor(format, vkFormat.second()); - /* Fill key/value data. Values can be any byte-string but the values we - write are all constant text strings. Keys must be sorted alphabetically.*/ + /* Fill key/value data. Values can be any byte-string but we only write + constant text strings. Keys must be sorted alphabetically.*/ using namespace Containers::Literals; - const Containers::StaticArray<2, const KeyValuePair> keyValueMap{ + const Containers::Pair keyValueMap[]{ /* Origin left, bottom, back (increasing right, up, out) */ - /** @todo KTX spec says "most other APIs and the majority of texture compression tools use [r, rd, rdi]". - Should we just always flip image data? Maybe make this configurable. */ + /** @todo KTX spec says "most other APIs and the majority of texture + compression tools use [r, rd, rdi]". + Should we just always flip image data? Maybe make this + configurable. */ Containers::pair("KTXorientation"_s, "ruo"_s.prefix(dimensions)), Containers::pair("KTXwriter"_s, "Magnum::KtxImageConverter 1.0"_s) }; - const Containers::Array keyValueData = packKeyValueData(keyValueMap); + /* Calculate size */ + std::size_t kvdSize = 0; + for(const auto& entry: keyValueMap) { + const UnsignedInt length = entry.first().size() + 1 + entry.second().size() + 1; + kvdSize += sizeof(length) + (length + 3)/4*4; + } + CORRADE_INTERNAL_ASSERT(kvdSize % 4 == 0); + + /* Pack. We assume that values are text strings, no endian-swapping needed. */ + std::size_t kvdOffset = 0; + Containers::Array keyValueData{ValueInit, kvdSize}; + for(const auto& entry: keyValueMap) { + const auto key = entry.first(); + const auto value = entry.second(); + const UnsignedInt length = key.size() + 1 + value.size() + 1; + *reinterpret_cast(keyValueData.suffix(kvdOffset).data()) = length; + Utility::Endianness::littleEndianInPlace(length); + kvdOffset += sizeof(length); + Utility::copy(key, keyValueData.suffix(kvdOffset).prefix(key.size())); + kvdOffset += entry.first().size() + 1; + Utility::copy(value, keyValueData.suffix(kvdOffset).prefix(value.size())); + kvdOffset += entry.second().size() + 1; + kvdOffset = (kvdOffset + 3)/4*4; + } + CORRADE_INTERNAL_ASSERT(kvdOffset == kvdSize); + + /* Fill level index */ + const Math::Vector size = imageLevels.front().size(); + if(size.product() == 0) { + Error() << "Trade::KtxImageConverter::convertToData(): image for level 0 is empty"; + return {}; + } + + const UnsignedInt numMipmaps = Math::min(imageLevels.size(), Math::log2(size.max()) + 1); + if(imageLevels.size() > numMipmaps) + Warning{} << "Trade::KtxImageConverter::convertToData(): expected at most" << + numMipmaps << "mip level images but got" << imageLevels.size() << Debug::nospace << + ", extra images will be ignored"; - /* Calculate level offsets. Needs to be aligned to the least common - multiple of the texel/block size and 4. */ - constexpr UnsignedInt numMipmaps = 1; Containers::Array levelIndex{numMipmaps}; - const std::size_t levelIndexSize = levelIndex.size()*sizeof(Implementation::KtxLevel); - const UnsignedInt pixelSize = UnsignedByte(image.pixelSize()); + const std::size_t levelIndexSize = numMipmaps*sizeof(Implementation::KtxLevel); std::size_t levelOffset = sizeof(Implementation::KtxHeader) + levelIndexSize + dataFormatDescriptor.size() + keyValueData.size(); - /* @todo Store mip levels from smallest to lowest for efficient streaming */ + /** @todo Handle block-compressed formats. Need block size and block count + in each dimension. How should skip be handled, if it's not a + multiple of the block size? */ + const UnsignedInt pixelSize = imageLevels.front().pixelSize(); + for(UnsignedInt i = 0; i != levelIndex.size(); ++i) { + /* Store mip levels from smallest to largest for efficient streaming */ + const UnsignedInt mip = levelIndex.size() - 1 - i; + const Math::Vector mipSize = Math::max(size >> mip, 1); + + const View& image = imageLevels[mip]; + + if(image.format() != format) { + Error() << "Trade::KtxImageConverter::convertToData(): expected " + "format" << format << "for level" << mip << "but got" << image.format(); + return {}; + } + + if(image.size() != mipSize) { + Error() << "Trade::KtxImageConverter::convertToData(): expected " + "size" << mipSize << "for level" << mip << "but got" << image.size(); + return {}; + } + + if(!image.data()) { + Error() << "Trade::KtxImageConverter::convertToData(): image data " + "for level" << mip << "is nullptr"; + return {}; + } + + /* Offset needs to be aligned to the least common multiple of the + texel/block size and 4. Not needed with supercompression. */ const std::size_t alignment = leastCommonMultiple(pixelSize, 4); - levelOffset = (levelOffset + (alignment - 1)) / alignment * alignment; + levelOffset = (levelOffset + (alignment - 1))/alignment*alignment; const std::size_t levelSize = pixelSize*image.size().product(); - levelIndex[i].byteOffset = levelOffset; - levelIndex[i].byteLength = levelSize; - levelIndex[i].uncompressedByteLength = levelSize; + levelIndex[mip].byteOffset = levelOffset; + levelIndex[mip].byteLength = levelSize; + levelIndex[mip].uncompressedByteLength = levelSize; levelOffset += levelSize; } - /* Initialize data buffer */ const std::size_t dataSize = levelOffset; Containers::Array data{ValueInit, dataSize}; @@ -444,24 +536,20 @@ Containers::Array convertImage(const BasicImageView& image) { Utility::copy(Containers::arrayView(Implementation::KtxFileIdentifier), Containers::arrayView(header.identifier)); header.vkFormat = vkFormat.first(); - header.typeSize = componentSize(image.format()); - header.imageSize = Vector3ui{Vector3i::pad(image.size(), 0u)}; + header.typeSize = componentSize(format); + header.imageSize = Vector3ui{Vector3i::pad(size, 0u)}; + /** @todo Handle different image types (cube and/or array) once this can be + queried from images */ header.layerCount = 0; header.faceCount = 1; header.levelCount = levelIndex.size(); header.supercompressionScheme = Implementation::SuperCompressionScheme::None; - for(auto& level: levelIndex) { - /* Copy the pixels into output, dropping padding (if any) */ - auto pixels = data.suffix(level.byteOffset).prefix(level.byteLength); - - std::size_t sizes[dimensions + 1]; - sizes[dimensions] = pixelSize; - for(UnsignedInt i = 0; i != dimensions; ++i) { - sizes[dimensions - 1 - i] = image.size()[i]; - } - - Utility::copy(image.pixels(), Containers::StridedArrayView{pixels, sizes}); + for(UnsignedInt i = 0; i != levelIndex.size(); ++i) { + const Implementation::KtxLevel& level = levelIndex[i]; + const View& image = imageLevels[i]; + const auto pixels = data.suffix(level.byteOffset).prefix(level.byteLength); + copyPixels(image, pixels); endianSwap(pixels, header.typeSize); @@ -497,52 +585,47 @@ Containers::Array convertImage(const BasicImageView& image) { return data; } -/** @todo */ -template -Containers::Array convertImage(const BasicCompressedImageView&) { - return {}; -} - } -KtxImageConverter::KtxImageConverter() = default; - KtxImageConverter::KtxImageConverter(PluginManager::AbstractManager& manager, const std::string& plugin): AbstractImageConverter{manager, plugin} {} ImageConverterFeatures KtxImageConverter::doFeatures() const { - return ImageConverterFeature::Convert1DToData | - ImageConverterFeature::Convert2DToData | - ImageConverterFeature::Convert3DToData | - ImageConverterFeature::ConvertCompressed1DToData | - ImageConverterFeature::ConvertCompressed2DToData | - ImageConverterFeature::ConvertCompressed3DToData; + return ImageConverterFeature::ConvertLevels1DToData | + ImageConverterFeature::ConvertLevels2DToData | + ImageConverterFeature::ConvertLevels3DToData | + ImageConverterFeature::ConvertCompressedLevels1DToData | + ImageConverterFeature::ConvertCompressedLevels2DToData | + ImageConverterFeature::ConvertCompressedLevels3DToData; } -Containers::Array KtxImageConverter::doConvertToData(const ImageView1D& image) { - return convertImage(image); +Containers::Array KtxImageConverter::doConvertToData(Containers::ArrayView imageLevels) { + return convertLevels(imageLevels); } -Containers::Array KtxImageConverter::doConvertToData(const ImageView2D& image) { - return convertImage(image); +Containers::Array KtxImageConverter::doConvertToData(Containers::ArrayView imageLevels) { + return convertLevels(imageLevels); } -Containers::Array KtxImageConverter::doConvertToData(const ImageView3D& image) { - return convertImage(image); +Containers::Array KtxImageConverter::doConvertToData(Containers::ArrayView imageLevels) { + return convertLevels(imageLevels); } -Containers::Array KtxImageConverter::doConvertToData(const CompressedImageView1D& image) { - return convertImage(image); +Containers::Array KtxImageConverter::doConvertToData(Containers::ArrayView imageLevels) { + //return convertLevels(imageLevels); + return {}; } -Containers::Array KtxImageConverter::doConvertToData(const CompressedImageView2D& image) { - return convertImage(image); +Containers::Array KtxImageConverter::doConvertToData(Containers::ArrayView imageLevels) { + //return convertLevels(imageLevels); + return {}; } -Containers::Array KtxImageConverter::doConvertToData(const CompressedImageView3D& image) { - return convertImage(image); +Containers::Array KtxImageConverter::doConvertToData(Containers::ArrayView imageLevels) { + //return convertLevels(imageLevels); + return {}; } }} CORRADE_PLUGIN_REGISTER(KtxImageConverter, Magnum::Trade::KtxImageConverter, - "cz.mosra.magnum.Trade.AbstractImageConverter/0.3") + "cz.mosra.magnum.Trade.AbstractImageConverter/0.3.1") diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h index 4c7c53883..2913a3be6 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h @@ -50,22 +50,19 @@ namespace Magnum { namespace Trade { class MAGNUM_KTXIMAGECONVERTER_EXPORT KtxImageConverter: public AbstractImageConverter { public: - /** @brief Default constructor */ - explicit KtxImageConverter(); - /** @brief Plugin manager constructor */ explicit KtxImageConverter(PluginManager::AbstractManager& manager, const std::string& plugin); private: ImageConverterFeatures MAGNUM_KTXIMAGECONVERTER_LOCAL doFeatures() const override; - Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(const ImageView1D& image) override; - Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(const ImageView2D& image) override; - Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(const ImageView3D& image) override; + Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(Containers::ArrayView imageLevels) override; + Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(Containers::ArrayView imageLevels) override; + Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(Containers::ArrayView imageLevels) override; - Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(const CompressedImageView1D& image) override; - Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(const CompressedImageView2D& image) override; - Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(const CompressedImageView3D& image) override; + Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(Containers::ArrayView imageLevels) override; + Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(Containers::ArrayView imageLevels) override; + Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(Containers::ArrayView imageLevels) override; }; }} From 6cda8e6bf5d97fb7bcf51d7d2b61a0d59f661cac Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 30 Jul 2021 23:16:41 +0200 Subject: [PATCH 34/95] KtxImageConverter: write DFD for block-compressed formats This is WIP, needs fixes for the level index code to actually compile --- .../KtxImageConverter/KtxImageConverter.cpp | 347 ++++++++++++++++-- 1 file changed, 324 insertions(+), 23 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 62881cf31..9f5befa78 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -28,7 +28,6 @@ #include #include -#include #include #include #include @@ -170,8 +169,234 @@ UnsignedByte componentSize(PixelFormat format) { CORRADE_ASSERT_UNREACHABLE("componentSize(): unsupported format" << format, {}); } -UnsignedByte componentSize(CompressedPixelFormat) { - return 1; +Containers::Pair> samples(CompressedPixelFormat format) { + /* There is no good way to auto-generate these. The KDF spec has a + format.json (https://github.com/KhronosGroup/KTX-Specification/blob/master/formats.json) + but that doesn't contain any information on how to fill the DFD. + Then there's Khronos' own dfdutils (https://github.com/KhronosGroup/KTX-Software/tree/master/lib/dfdutils) + but that generates headers through Perl scripts, and the headers need + the original VkFormat enum to be defined. + + DFD content is taken directly from the KDF spec: + https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#CompressedFormatModels */ + + constexpr UnsignedInt Min = 0u; + constexpr UnsignedInt Max = ~0u; + constexpr UnsignedInt SignedMin = 1u << 31; + constexpr UnsignedInt SignedMax = ~0u >> 1; + /* BC6h has unsigned floats, but the spec says to use a sampleLower of + -1.0 anyway: + https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#bc6h_channel + So we don't need to distinguish between Ufloat and Sfloat*/ + constexpr UnsignedInt FloatMin = 0xBF800000u; /* -1.0f */ + constexpr UnsignedInt FloatMax = 0x7F800000u; /* 1.0f */ + + /** @todo Remove the upper, lower, ChannelFormat flags and patch it later? + There are a few oddities that need to be treated differently from + the non-compressed DFDs... */ + static constexpr Implementation::KdfBasicBlockSample SamplesBc1[]{ + {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color, + {}, Min, Max} + }; + /* The DFD examples in the spec don't set ChannelFormat::Linear in any of + the block-compressed alpha channels */ + static constexpr Implementation::KdfBasicBlockSample SamplesBc1AlphaPunchThrough[]{ + {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Bc1Alpha, + {}, Min, Max} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesBc2And3[]{ + {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Alpha, + {}, Min, Max}, + {64, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color, + {}, Min, Max} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesBc4[]{ + {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color, + {}, Min, Max} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesBc4Signed[]{ + {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color | Implementation::KdfBasicBlockSample::ChannelFormat::Signed, + {}, SignedMin, SignedMax} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesBc5[]{ + {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Red, + {}, Min, Max}, + {64, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Green, + {}, Min, Max} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesBc5Signed[]{ + {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Red | Implementation::KdfBasicBlockSample::ChannelFormat::Signed, + {}, SignedMin, SignedMax}, + {64, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Green | Implementation::KdfBasicBlockSample::ChannelFormat::Signed, + {}, SignedMin, SignedMax} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesBc6h[]{ + {0, 128 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color | Implementation::KdfBasicBlockSample::ChannelFormat::Float, + {}, FloatMin, FloatMax} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesBc6hSigned[]{ + {0, 128 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color | Implementation::KdfBasicBlockSample::ChannelFormat::Float | + Implementation::KdfBasicBlockSample::ChannelFormat::Signed, + {}, FloatMin, FloatMax} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesBc7[]{ + {0, 128 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color, + {}, Min, Max} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesEacR11[]{ + {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Red, + {}, Min, Max} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesEacR11Signed[]{ + {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Red | Implementation::KdfBasicBlockSample::ChannelFormat::Signed, + {}, SignedMin, SignedMax} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesEacRG11[]{ + {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Red, + {}, Min, Max}, + {64, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Green, + {}, Min, Max} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesEacRG11Signed[]{ + {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Red | Implementation::KdfBasicBlockSample::ChannelFormat::Signed, + {}, SignedMin, SignedMax}, + {64, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Green | Implementation::KdfBasicBlockSample::ChannelFormat::Signed, + {}, SignedMin, SignedMax} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesEtc2[]{ + {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Etc2Color, + {}, Min, Max} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesEtc2AlphaPunchThrough[]{ + {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Etc2Color, + {}, Min, Max}, + {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Alpha, + {}, Min, Max} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesEtc2Alpha[]{ + {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Alpha, + {}, Min, Max}, + {64, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Etc2Color, + {}, Min, Max} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesAstc[]{ + {0, 128 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color, + {}, Min, Max} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesAstcHdr[]{ + {0, 128 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color | Implementation::KdfBasicBlockSample::ChannelFormat::Float | + Implementation::KdfBasicBlockSample::ChannelFormat::Signed, + {}, FloatMin, FloatMax} + }; + static constexpr Implementation::KdfBasicBlockSample SamplesPvrtc[]{ + {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color, + {}, Min, Max} + }; + + switch(format) { + case CompressedPixelFormat::Bc1RGBUnorm: + case CompressedPixelFormat::Bc1RGBSrgb: + return {Implementation::KdfBasicBlockHeader::ColorModel::Bc1, SamplesBc1}; + case CompressedPixelFormat::Bc1RGBAUnorm: + case CompressedPixelFormat::Bc1RGBASrgb: + return {Implementation::KdfBasicBlockHeader::ColorModel::Bc1, SamplesBc1AlphaPunchThrough}; + case CompressedPixelFormat::Bc2RGBAUnorm: + case CompressedPixelFormat::Bc2RGBASrgb: + return {Implementation::KdfBasicBlockHeader::ColorModel::Bc2, SamplesBc2And3}; + case CompressedPixelFormat::Bc3RGBAUnorm: + case CompressedPixelFormat::Bc3RGBASrgb: + return {Implementation::KdfBasicBlockHeader::ColorModel::Bc3, SamplesBc2And3}; + case CompressedPixelFormat::Bc4RUnorm: + return {Implementation::KdfBasicBlockHeader::ColorModel::Bc4, SamplesBc4}; + case CompressedPixelFormat::Bc4RSnorm: + return {Implementation::KdfBasicBlockHeader::ColorModel::Bc4, SamplesBc4Signed}; + case CompressedPixelFormat::Bc5RGUnorm: + return {Implementation::KdfBasicBlockHeader::ColorModel::Bc5, SamplesBc5}; + case CompressedPixelFormat::Bc5RGSnorm: + return {Implementation::KdfBasicBlockHeader::ColorModel::Bc5, SamplesBc5Signed}; + case CompressedPixelFormat::Bc6hRGBUfloat: + return {Implementation::KdfBasicBlockHeader::ColorModel::Bc6h, SamplesBc6h}; + case CompressedPixelFormat::Bc6hRGBSfloat: + return {Implementation::KdfBasicBlockHeader::ColorModel::Bc6h, SamplesBc6hSigned}; + case CompressedPixelFormat::Bc7RGBAUnorm: + case CompressedPixelFormat::Bc7RGBASrgb: + return {Implementation::KdfBasicBlockHeader::ColorModel::Bc7, SamplesBc7}; + case CompressedPixelFormat::EacR11Unorm: + return {Implementation::KdfBasicBlockHeader::ColorModel::Etc2, SamplesEacR11}; + case CompressedPixelFormat::EacR11Snorm: + return {Implementation::KdfBasicBlockHeader::ColorModel::Etc2, SamplesEacR11Signed}; + case CompressedPixelFormat::EacRG11Unorm: + return {Implementation::KdfBasicBlockHeader::ColorModel::Etc2, SamplesEacRG11}; + case CompressedPixelFormat::EacRG11Snorm: + return {Implementation::KdfBasicBlockHeader::ColorModel::Etc2, SamplesEacRG11Signed}; + case CompressedPixelFormat::Etc2RGB8Unorm: + case CompressedPixelFormat::Etc2RGB8Srgb: + return {Implementation::KdfBasicBlockHeader::ColorModel::Etc2, SamplesEtc2}; + case CompressedPixelFormat::Etc2RGB8A1Unorm: + case CompressedPixelFormat::Etc2RGB8A1Srgb: + return {Implementation::KdfBasicBlockHeader::ColorModel::Etc2, SamplesEtc2AlphaPunchThrough}; + case CompressedPixelFormat::Etc2RGBA8Unorm: + case CompressedPixelFormat::Etc2RGBA8Srgb: + return {Implementation::KdfBasicBlockHeader::ColorModel::Etc2, SamplesEtc2Alpha}; + case CompressedPixelFormat::Astc4x4RGBAUnorm: + case CompressedPixelFormat::Astc4x4RGBASrgb: + case CompressedPixelFormat::Astc5x4RGBAUnorm: + case CompressedPixelFormat::Astc5x4RGBASrgb: + case CompressedPixelFormat::Astc5x5RGBAUnorm: + case CompressedPixelFormat::Astc5x5RGBASrgb: + case CompressedPixelFormat::Astc6x5RGBAUnorm: + case CompressedPixelFormat::Astc6x5RGBASrgb: + case CompressedPixelFormat::Astc6x6RGBAUnorm: + case CompressedPixelFormat::Astc6x6RGBASrgb: + case CompressedPixelFormat::Astc8x5RGBAUnorm: + case CompressedPixelFormat::Astc8x5RGBASrgb: + case CompressedPixelFormat::Astc8x6RGBAUnorm: + case CompressedPixelFormat::Astc8x6RGBASrgb: + case CompressedPixelFormat::Astc8x8RGBAUnorm: + case CompressedPixelFormat::Astc8x8RGBASrgb: + case CompressedPixelFormat::Astc10x5RGBAUnorm: + case CompressedPixelFormat::Astc10x5RGBASrgb: + case CompressedPixelFormat::Astc10x6RGBAUnorm: + case CompressedPixelFormat::Astc10x6RGBASrgb: + case CompressedPixelFormat::Astc10x8RGBAUnorm: + case CompressedPixelFormat::Astc10x8RGBASrgb: + case CompressedPixelFormat::Astc10x10RGBAUnorm: + case CompressedPixelFormat::Astc10x10RGBASrgb: + case CompressedPixelFormat::Astc12x10RGBAUnorm: + case CompressedPixelFormat::Astc12x10RGBASrgb: + case CompressedPixelFormat::Astc12x12RGBAUnorm: + case CompressedPixelFormat::Astc12x12RGBASrgb: + return {Implementation::KdfBasicBlockHeader::ColorModel::Astc, SamplesAstc}; + case CompressedPixelFormat::Astc4x4RGBAF: + case CompressedPixelFormat::Astc5x4RGBAF: + case CompressedPixelFormat::Astc5x5RGBAF: + case CompressedPixelFormat::Astc6x5RGBAF: + case CompressedPixelFormat::Astc6x6RGBAF: + case CompressedPixelFormat::Astc8x5RGBAF: + case CompressedPixelFormat::Astc8x6RGBAF: + case CompressedPixelFormat::Astc8x8RGBAF: + case CompressedPixelFormat::Astc10x5RGBAF: + case CompressedPixelFormat::Astc10x6RGBAF: + case CompressedPixelFormat::Astc10x8RGBAF: + case CompressedPixelFormat::Astc10x10RGBAF: + case CompressedPixelFormat::Astc12x10RGBAF: + case CompressedPixelFormat::Astc12x12RGBAF: + return {Implementation::KdfBasicBlockHeader::ColorModel::Astc, SamplesAstcHdr}; + /* 3D ASTC formats are not exposed in Vulkan */ + case CompressedPixelFormat::PvrtcRGB2bppUnorm: + case CompressedPixelFormat::PvrtcRGB2bppSrgb: + case CompressedPixelFormat::PvrtcRGBA2bppUnorm: + case CompressedPixelFormat::PvrtcRGBA2bppSrgb: + case CompressedPixelFormat::PvrtcRGB4bppUnorm: + case CompressedPixelFormat::PvrtcRGB4bppSrgb: + case CompressedPixelFormat::PvrtcRGBA4bppUnorm: + case CompressedPixelFormat::PvrtcRGBA4bppSrgb: + return {Implementation::KdfBasicBlockHeader::ColorModel::Pvrtc, SamplesPvrtc}; + default: + break; + } + + CORRADE_ASSERT_UNREACHABLE("samples(): unsupported format" << format, {}); } UnsignedByte channelFormat(Implementation::VkFormatSuffix suffix, Implementation::KdfBasicBlockSample::ChannelId id) { @@ -198,7 +423,7 @@ UnsignedByte channelFormat(Implementation::VkFormatSuffix suffix, Implementation CORRADE_ASSERT_UNREACHABLE("channelFormat(): invalid format suffix" << UnsignedInt(suffix), {}); } -Containers::Array2 channelMapping(Implementation::VkFormatSuffix suffix, UnsignedByte typeSize) { +Containers::Pair channelMapping(Implementation::VkFormatSuffix suffix, UnsignedByte typeSize) { /* sampleLower and sampleUpper define how to interpret the range of values found in a channel. samplerLower = black value or -1 for signed values @@ -214,7 +439,7 @@ Containers::Array2 channelMapping(Implementation::VkFormatSuffix su Magnum doesn't expose 64-bit formats. */ CORRADE_INTERNAL_ASSERT(typeSize <= 4); - const UnsignedInt typeMask = ~0u >> ((4 - typeSize) * 8); + const UnsignedInt typeMask = ~0u >> ((4 - typeSize)*8); switch(suffix) { case Implementation::VkFormatSuffix::UNORM: @@ -243,11 +468,10 @@ Containers::Array2 channelMapping(Implementation::VkFormatSuffix su Containers::Array fillDataFormatDescriptor(PixelFormat format, Implementation::VkFormatSuffix suffix) { const UnsignedInt texelSize = pixelSize(format); const UnsignedInt typeSize = componentSize(format); - /** @todo numChannels will be wrong for block-compressed formats */ - const UnsignedInt numChannels = texelSize / typeSize; + const UnsignedInt numChannels = texelSize/typeSize; /* Calculate size */ - const std::size_t dfdSamplesSize = numChannels * sizeof(Implementation::KdfBasicBlockSample); + const std::size_t dfdSamplesSize = numChannels*sizeof(Implementation::KdfBasicBlockSample); const std::size_t dfdBlockSize = sizeof(Implementation::KdfBasicBlockHeader) + dfdSamplesSize; const std::size_t dfdSize = sizeof(UnsignedInt) + dfdBlockSize; CORRADE_INTERNAL_ASSERT(dfdSize % 4 == 0); @@ -279,31 +503,50 @@ Containers::Array fillDataFormatDescriptor(PixelFormat format, Implementat /** @todo Do we ever have premultiplied alpha? */ header.bytesPlane[0] = texelSize; - /* Color channels */ + /* Channels */ const auto samples = Containers::arrayCast(data.suffix(offset)); offset += dfdSamplesSize; const UnsignedByte bitLength = typeSize*8; - static const Implementation::KdfBasicBlockSample::ChannelId channelIdsRgba[4]{ + constexpr Implementation::KdfBasicBlockSample::ChannelId UncompressedChannelIds[]{ Implementation::KdfBasicBlockSample::ChannelId::Red, Implementation::KdfBasicBlockSample::ChannelId::Green, Implementation::KdfBasicBlockSample::ChannelId::Blue, - Implementation::KdfBasicBlockSample::ChannelId::Alpha - }; - /* - static const Implementation::KdfBasicBlockSample::ChannelId channelIdsDepthStencil[2]{ + Implementation::KdfBasicBlockSample::ChannelId::Alpha, Implementation::KdfBasicBlockSample::ChannelId::Depth, Implementation::KdfBasicBlockSample::ChannelId::Stencil }; - static const Implementation::KdfBasicBlockSample::ChannelId channelIdsStencil[1]{ - Implementation::KdfBasicBlockSample::ChannelId::Stencil - }; + + /** @todo Special-case depth+stencil formats. Channel count is wrong + for packed formats (only Depth24UnormStencil8UI) and they need + correct stencil offset+length */ + Containers::ArrayView channelIds; + /* + switch(format) { + case PixelFormat::Stencil8UI: + channelIds = {&UncompressedChannelIds[5], 1}; + break; + case PixelFormat::Depth16Unorm: + case PixelFormat::Depth24Unorm: + case PixelFormat::Depth32F: + channelIds = {&UncompressedChannelIds[4], 1}; + break; + case PixelFormat::Depth16UnormStencil8UI: + case PixelFormat::Depth24UnormStencil8UI: + case PixelFormat::Depth32FStencil8UI: + channelIds = {&UncompressedChannelIds[4], 2}; + break; + default: + channelIds = {&UncompressedChannelIds[0], numChannels}; + break; + } */ + channelIds = {&UncompressedChannelIds[0], numChannels}; + + CORRADE_INTERNAL_ASSERT(channelIds.size() == numChannels); - /** @todo detect depth/stencil */ - Containers::ArrayView channelIds = - Containers::arrayView(channelIdsRgba); + const auto mapping = channelMapping(suffix, typeSize); UnsignedShort bitOffset = 0; for(UnsignedInt i = 0; i != numChannels; ++i) { @@ -312,9 +555,10 @@ Containers::Array fillDataFormatDescriptor(PixelFormat format, Implementat sample.bitLength = bitLength - 1; const auto channelId = channelIds[i]; sample.channelType = channelId | channelFormat(suffix, channelId); - const auto mapping = channelMapping(suffix, typeSize); - sample.lower = mapping[0]; - sample.upper = mapping[1]; + if(channelId == Implementation::KdfBasicBlockSample::ChannelId::Alpha) + sample.channelType |= Implementation::KdfBasicBlockSample::ChannelFormat::Linear; + sample.lower = mapping.first(); + sample.upper = mapping.second(); bitOffset += bitLength; @@ -331,12 +575,69 @@ Containers::Array fillDataFormatDescriptor(PixelFormat format, Implementat return data; } +Containers::Array fillDataFormatDescriptor(CompressedPixelFormat format, Implementation::VkFormatSuffix suffix) { + const auto sampleData = samples(format); /* Calculate size */ + const std::size_t dfdSamplesSize = sampleData.second().size()*sizeof(Implementation::KdfBasicBlockSample); + const std::size_t dfdBlockSize = sizeof(Implementation::KdfBasicBlockHeader) + dfdSamplesSize; + const std::size_t dfdSize = sizeof(UnsignedInt) + dfdBlockSize; + CORRADE_INTERNAL_ASSERT(dfdSize % 4 == 0); + + Containers::Array data{ValueInit, dfdSize}; std::size_t offset = 0; + + /* Length */ + UnsignedInt& length = *reinterpret_cast(data.suffix(offset).data()); + offset += sizeof(length); + + length = dfdSize; + + /* Block header */ + Implementation::KdfBasicBlockHeader& header = *reinterpret_cast(data.suffix(offset).data()); + offset += sizeof(header); + + header.vendorId = Implementation::KdfBasicBlockHeader::VendorId::Khronos; + header.descriptorType = Implementation::KdfBasicBlockHeader::DescriptorType::Basic; + header.versionNumber = Implementation::KdfBasicBlockHeader::VersionNumber::Kdf1_3; + header.descriptorBlockSize = dfdBlockSize; + + header.colorModel = sampleData.first(); + header.colorPrimaries = Implementation::KdfBasicBlockHeader::ColorPrimaries::Srgb; + header.transferFunction = suffix == Implementation::VkFormatSuffix::SRGB + ? Implementation::KdfBasicBlockHeader::TransferFunction::Srgb + : Implementation::KdfBasicBlockHeader::TransferFunction::Linear; + /** @todo Do we ever have premultiplied alpha? */ + const Vector3i blockSize = compressedBlockSize(format); + for(UnsignedInt i = 0; i != blockSize.Size; ++i) { + if(blockSize[i] > 1) + header.texelBlockDimension[i] = blockSize[i] - 1; + } + header.bytesPlane[0] = compressedBlockDataSize(format); + + /* Channels */ + auto samples = Containers::arrayCast(data.suffix(offset)); + offset += dfdSamplesSize; + + Utility::copy(sampleData.second(), samples); + + UnsignedShort extent = 0; + for(auto& sample: samples) { + extent = Math::max(sample.bitOffset + sample.bitLength + 1, extent); + Utility::Endianness::littleEndianInPlace(sample.bitOffset, + sample.lower, sample.upper); } + /* Just making sure we didn't make any major mistake in samples() */ + CORRADE_INTERNAL_ASSERT(extent == header.bytesPlane[0]*8); + + Utility::Endianness::littleEndianInPlace(length); + Utility::Endianness::littleEndianInPlace(header.vendorId, header.descriptorType, + header.versionNumber, header.descriptorBlockSize); + + CORRADE_INTERNAL_ASSERT(offset == dfdSize); + return data; } From 976a7dcd8601b26c36f7221ffca67716ce9d647a Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 30 Jul 2021 23:17:26 +0200 Subject: [PATCH 35/95] KtxImageConverter: first set of tests Basic tests that don't require checking file content --- .../Test/KtxImageConverterTest.cpp | 310 ++++++++++++++++++ 1 file changed, 310 insertions(+) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index 0669ee3c0..f2c45cf01 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -24,18 +24,23 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include #include #include #include #include #include #include +#include #include #include +#include #include #include #include +#include #include "configure.h" @@ -44,12 +49,63 @@ namespace Magnum { namespace Trade { namespace Test { namespace { struct KtxImageConverterTest: TestSuite::Tester { explicit KtxImageConverterTest(); + /** @todo - depth/stencil + */ + + void zeroSize(); + void emptyData(); + + void noLevels(); + void tooManyLevels(); + + void supportedFormat(); + void supportedCompressedFormat(); + void unsupportedCompressedFormat(); + void implementationSpecificFormat(); + + void levelWrongFormat(); + void levelWrongSize(); + void levelZeroSize(); + void levelEmptyData(); + + void pvrtcRgb(); + /* Explicitly forbid system-wide plugin dependencies */ PluginManager::Manager _converterManager{"nonexistent"}; PluginManager::Manager _importerManager{"nonexistent"}; }; +const struct { + const char* name; + CompressedPixelFormat inputFormat; + CompressedPixelFormat outputFormat; +} PvrtcRgbData[]{ + {"2bppUnorm", CompressedPixelFormat::PvrtcRGB2bppUnorm, CompressedPixelFormat::PvrtcRGBA2bppUnorm}, + {"2bppSrgb", CompressedPixelFormat::PvrtcRGB2bppSrgb, CompressedPixelFormat::PvrtcRGBA2bppSrgb}, + {"4bppUnorm", CompressedPixelFormat::PvrtcRGB4bppUnorm, CompressedPixelFormat::PvrtcRGBA4bppUnorm}, + {"4bppSrgb", CompressedPixelFormat::PvrtcRGB4bppSrgb, CompressedPixelFormat::PvrtcRGBA4bppSrgb}, +}; + KtxImageConverterTest::KtxImageConverterTest() { + addTests({&KtxImageConverterTest::zeroSize, + &KtxImageConverterTest::emptyData, + + &KtxImageConverterTest::noLevels, + &KtxImageConverterTest::tooManyLevels, + + &KtxImageConverterTest::supportedFormat, + &KtxImageConverterTest::supportedCompressedFormat, + &KtxImageConverterTest::unsupportedCompressedFormat, + &KtxImageConverterTest::implementationSpecificFormat, + + &KtxImageConverterTest::levelWrongFormat, + &KtxImageConverterTest::levelWrongSize, + &KtxImageConverterTest::levelZeroSize, + &KtxImageConverterTest::levelEmptyData}); + + addInstancedTests({&KtxImageConverterTest::pvrtcRgb}, + Containers::arraySize(PvrtcRgbData)); + /* Load the plugin directly from the build tree. Otherwise it's static and already loaded. */ #ifdef KTXIMAGECONVERTER_PLUGIN_FILENAME @@ -61,6 +117,260 @@ KtxImageConverterTest::KtxImageConverterTest() { #endif } +void KtxImageConverterTest::zeroSize() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertToData(ImageView2D{PixelFormat::RGB8Unorm, {1, 0}})); + CORRADE_COMPARE(out.str(), + "Trade::KtxImageConverter::convertToData(): image for level 0 is empty\n"); +} + +void KtxImageConverterTest::emptyData() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertToData(ImageView2D{PixelFormat::RGB8Unorm, {1, 1}})); + CORRADE_COMPARE(out.str(), + "Trade::KtxImageConverter::convertToData(): image data for level 0 is nullptr\n"); +} + +void KtxImageConverterTest::noLevels() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertToData(Containers::ArrayView{})); + CORRADE_COMPARE(out.str(), + "Trade::KtxImageConverter::convertToData(): expected at least 1 mip level\n"); +} + +void KtxImageConverterTest::tooManyLevels() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + const UnsignedByte bytes[4]{}; + + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(converter->convertToData({ + ImageView2D{PixelFormat::RGB8Unorm, {1, 1}, bytes}, + ImageView2D{PixelFormat::RGB8Unorm, {1, 1}, bytes} + })); + CORRADE_COMPARE(out.str(), + "Trade::KtxImageConverter::convertToData(): expected at most 1 mip " + "level images but got 2, extra images will be ignored\n"); +} + +void KtxImageConverterTest::supportedFormat() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + const UnsignedByte data[32]{}; + + /* All the formats in PixelFormat are supported */ + /** @todo This needs to be extended when formats are added to PixelFormat */ + constexpr PixelFormat start = PixelFormat::R8Unorm; + constexpr PixelFormat end = PixelFormat::Depth32FStencil8UI; + + for(UnsignedInt format = UnsignedInt(start); format != UnsignedInt(end); ++format) { + CORRADE_ITERATION(format); + CORRADE_INTERNAL_ASSERT(Containers::arraySize(data) >= pixelSize(PixelFormat(format))); + CORRADE_VERIFY(converter->convertToData(ImageView2D{PixelFormat(format), {1, 1}, data})); + } +} + +const CompressedPixelFormat UnsupportedCompressedFormats[]{ + /* Vulkan has no support (core or extension) for 3D ASTC formats. + KTX supports them, but through an unreleased extension. */ + CompressedPixelFormat::Astc3x3x3RGBAUnorm, + CompressedPixelFormat::Astc3x3x3RGBASrgb, + CompressedPixelFormat::Astc3x3x3RGBAF, + CompressedPixelFormat::Astc4x3x3RGBAUnorm, + CompressedPixelFormat::Astc4x3x3RGBASrgb, + CompressedPixelFormat::Astc4x3x3RGBAF, + CompressedPixelFormat::Astc4x4x3RGBAUnorm, + CompressedPixelFormat::Astc4x4x3RGBASrgb, + CompressedPixelFormat::Astc4x4x3RGBAF, + CompressedPixelFormat::Astc4x4x4RGBAUnorm, + CompressedPixelFormat::Astc4x4x4RGBASrgb, + CompressedPixelFormat::Astc4x4x4RGBAF, + CompressedPixelFormat::Astc5x4x4RGBAUnorm, + CompressedPixelFormat::Astc5x4x4RGBASrgb, + CompressedPixelFormat::Astc5x4x4RGBAF, + CompressedPixelFormat::Astc5x5x4RGBAUnorm, + CompressedPixelFormat::Astc5x5x4RGBASrgb, + CompressedPixelFormat::Astc5x5x4RGBAF, + CompressedPixelFormat::Astc5x5x5RGBAUnorm, + CompressedPixelFormat::Astc5x5x5RGBASrgb, + CompressedPixelFormat::Astc5x5x5RGBAF, + CompressedPixelFormat::Astc6x5x5RGBAUnorm, + CompressedPixelFormat::Astc6x5x5RGBASrgb, + CompressedPixelFormat::Astc6x5x5RGBAF, + CompressedPixelFormat::Astc6x6x5RGBAUnorm, + CompressedPixelFormat::Astc6x6x5RGBASrgb, + CompressedPixelFormat::Astc6x6x5RGBAF, + CompressedPixelFormat::Astc6x6x6RGBAUnorm, + CompressedPixelFormat::Astc6x6x6RGBASrgb, + CompressedPixelFormat::Astc6x6x6RGBAF, +}; + +void KtxImageConverterTest::supportedCompressedFormat() { + CORRADE_SKIP("Compressed image export isn't implemented yet"); + + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + const UnsignedByte bytes[32]{}; + const auto unsupported = Containers::arrayView(UnsupportedCompressedFormats); + + /** @todo This needs to be extended when formats are added to CompressedPixelFormat */ + constexpr CompressedPixelFormat start = CompressedPixelFormat::Bc1RGBUnorm; + constexpr CompressedPixelFormat end = CompressedPixelFormat::PvrtcRGBA4bppSrgb; + + for(UnsignedInt format = UnsignedInt(start); format != UnsignedInt(end); ++format) { + if(std::find(unsupported.begin(), unsupported.end(), CompressedPixelFormat(format)) == unsupported.end()) { + CORRADE_ITERATION(format); + CORRADE_INTERNAL_ASSERT(Containers::arraySize(bytes) >= compressedBlockDataSize(CompressedPixelFormat(format))); + CORRADE_VERIFY(converter->convertToData(CompressedImageView2D{CompressedPixelFormat(format), {1, 1}, bytes})); + } + } +} + +void KtxImageConverterTest::unsupportedCompressedFormat() { + CORRADE_SKIP("Compressed image export isn't implemented yet"); + + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + std::ostringstream out; + Error redirectError{&out}; + + for(CompressedPixelFormat format: UnsupportedCompressedFormats) { + CORRADE_ITERATION(format); + out.str({}); /* Reset stream */ + CORRADE_VERIFY(!converter->convertToData(CompressedImageView2D{format, {1, 1}, {}})); + + /** @todo Is there no better way to do this? */ + std::ostringstream formattedOut; + Debug redirectDebug{&formattedOut}; + Debug{} << "Trade::KtxImageConverter::convertToData(): unsupported format" << format; + + CORRADE_COMPARE(out.str(), formattedOut.str()); + } +} + +/* Actual type needed for ADL, typedef of integer type isn't enough */ +enum CustomPixelFormat : UnsignedInt { + Value +}; + +UnsignedInt pixelSize(CustomPixelFormat) { + return 1u; +} + +void KtxImageConverterTest::implementationSpecificFormat() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + const UnsignedByte bytes[]{1}; + + std::ostringstream out; + Error redirectError{&out}; + + PixelStorage storage; + storage.setAlignment(1); + CORRADE_VERIFY(!converter->convertToData(ImageView2D{storage, CustomPixelFormat{}, {1, 1}, bytes})); + CORRADE_COMPARE(out.str(), + "Trade::KtxImageConverter::convertToData(): implementation-specific formats are not supported\n"); +} + +void KtxImageConverterTest::levelWrongFormat() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + const UnsignedByte bytes[16]{}; + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertToData({ + ImageView2D{PixelFormat::RGB8Unorm, {2, 2}, bytes}, + ImageView2D{PixelFormat::RGB8Snorm, {1, 1}, bytes} + })); + CORRADE_COMPARE(out.str(), + "Trade::KtxImageConverter::convertToData(): expected format PixelFormat::RGB8Unorm " + "for level 1 but got PixelFormat::RGB8Snorm\n"); +} + +void KtxImageConverterTest::levelWrongSize() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + const UnsignedByte bytes[16]{}; + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertToData({ + ImageView2D{PixelFormat::RGB8Unorm, {2, 2}, bytes}, + ImageView2D{PixelFormat::RGB8Unorm, {2, 1}, bytes} + })); + CORRADE_COMPARE(out.str(), + "Trade::KtxImageConverter::convertToData(): expected size Vector(1, 1) for level 1 but got Vector(2, 1)\n"); +} + +void KtxImageConverterTest::pvrtcRgb() { + auto&& data = PvrtcRgbData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + CORRADE_SKIP("Compressed image export isn't implemented yet"); + + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + const UnsignedByte bytes[16]{}; + CORRADE_INTERNAL_ASSERT(Containers::arraySize(bytes) >= compressedBlockDataSize(data.inputFormat)); + + const CompressedImageView2D inputImage{data.inputFormat, {2, 2}, bytes}; + const auto output = converter->convertToData(inputImage); + CORRADE_VERIFY(output); + + if(_importerManager.loadState("KtxImporter") == PluginManager::LoadState::NotFound) + CORRADE_SKIP("KtxImporter plugin not found, cannot test"); + + Containers::Pointer importer = _importerManager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openData(output)); + + const auto image = importer->image2D(0); + CORRADE_VERIFY(image); + CORRADE_VERIFY(image->isCompressed()); + CORRADE_COMPARE(image->compressedFormat(), data.outputFormat); + CORRADE_COMPARE(image->data(), inputImage.data()); +} + +void KtxImageConverterTest::levelZeroSize() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + const UnsignedByte data[16]{}; + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertToData({ + ImageView2D{PixelFormat::RGB8Unorm, {2, 2}, data}, + ImageView2D{PixelFormat::RGB8Unorm, {0, 1}, data} + })); + CORRADE_COMPARE(out.str(), + "Trade::KtxImageConverter::convertToData(): expected size Vector(1, 1) for level 1 but got Vector(0, 1)\n"); +} + +void KtxImageConverterTest::levelEmptyData() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + const UnsignedByte data[16]{}; + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertToData({ + ImageView2D{PixelFormat::RGB8Unorm, {2, 2}, data}, + ImageView2D{PixelFormat::RGB8Unorm, {1, 1}} + })); + CORRADE_COMPARE(out.str(), + "Trade::KtxImageConverter::convertToData(): image data for level 1 is nullptr\n"); +} + }}}} CORRADE_TEST_MAIN(Magnum::Trade::Test::KtxImageConverterTest) From a08c0e44a38e0169b0bf263c56b9b3ee4269bec6 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 30 Jul 2021 23:18:33 +0200 Subject: [PATCH 36/95] KtxImageConverter: oops --- src/MagnumPlugins/KtxImporter/KtxHeader.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxHeader.h b/src/MagnumPlugins/KtxImporter/KtxHeader.h index 15d62c34b..ea9f083e9 100644 --- a/src/MagnumPlugins/KtxImporter/KtxHeader.h +++ b/src/MagnumPlugins/KtxImporter/KtxHeader.h @@ -199,8 +199,9 @@ struct KdfBasicBlockSample { Alpha = 15, /* Compressed color models. Some use Red/Green/Alpha from Rgbsda if applicable. */ - Data = 0, - AlphaPresent = 0 /* DXT1 */ + Color = 0, + Bc1Alpha = 1, + Etc2Color = 2 }; /* Channel data type bit mask encoded in upper half of channelType */ From d7586f7c7ef40e668ff53b91e92fca3c1b083109 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Sat, 31 Jul 2021 17:15:08 +0200 Subject: [PATCH 37/95] KtxImporter: check entire level size for 3D layered images --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 24 +++++++------------ .../KtxImporter/Test/KtxImporterTest.cpp | 5 +++- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index d61703df3..a95e4dca3 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -495,25 +495,24 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { levelSize[f->numDimensions] = numFaces*numLayers; } - std::size_t length; + std::size_t imageLength; if(f->pixelFormat.isCompressed) { - /** @todo This will probably break for layered 3D block-compressed - data once we support those formats */ const Vector3i blockSize = f->pixelFormat.blockSize; const Vector3i blockCount = (levelSize + (blockSize - Vector3i{1}))/blockSize; - length = blockCount.product()*f->pixelFormat.size; + imageLength = blockCount.product()*f->pixelFormat.size; } else - length = levelSize.product()*f->pixelFormat.size; + imageLength = levelSize.product()*f->pixelFormat.size; + const std::size_t totalLength = imageLength*numImages; - if(level.byteLength < length) { + if(level.byteLength < totalLength) { Error{} << "Trade::KtxImporter::openData(): level data too short, " - "expected at least" << length << "bytes but got" << level.byteLength; + "expected at least" << totalLength << "bytes but got" << level.byteLength; return; } for(UnsignedInt image = 0; image != numImages; ++image) { - const std::size_t imageOffset = is3DArrayImage ? image*length : 0; - f->imageData[image][i] = {levelSize, f->in.suffix(level.byteOffset + imageOffset).prefix(length)}; + const std::size_t offset = level.byteOffset + image*imageLength; + f->imageData[image][i] = {levelSize, f->in.suffix(offset).prefix(imageLength)}; } /* Shrink to next power of 2 */ @@ -640,12 +639,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { wording is odd, I can't tell if they're saying it's mandatory or not: https://github.khronos.org/KTX-Specification/#cubemapOrientation The toktx tool from Khronos Texture Tools also forces rd for - cube maps, so we should probably do that too. - Face orientation (+X, -X, etc.) is based on a left-handed y-up - coordinate system, but neither GL nor Vulkan have that. The - appendix implies that both need coordinate transformations. Do we - have to do anything here? Flip faces/axes to match GL or Vulkan - expectations? */ + cube maps, so we might want to do that in the converter as well. */ /* Incomplete cube maps are a 'feature' of KTX files. We just import them as layers (which is how they're exposed to us). */ diff --git a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp index bbe48b5fd..e8d95897a 100644 --- a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp +++ b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp @@ -201,7 +201,10 @@ const struct { "3D images can't have depth/stencil format"}, {"level data too short", "2d-rgb.ktx2", sizeof(Implementation::KtxHeader) + offsetof(Implementation::KtxLevel, byteLength), 1, - "level data too short, expected at least 36 bytes but got 1"} + "level data too short, expected at least 36 bytes but got 1"}, + {"3D layered level data too short", "3d-layers.ktx2", + sizeof(Implementation::KtxHeader) + offsetof(Implementation::KtxLevel, byteLength), 108, + "level data too short, expected at least 216 bytes but got 108"} }; const struct { From 11fc2d346eb93842a505c7c6d53781c59878c7de Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Sat, 31 Jul 2021 21:10:37 +0200 Subject: [PATCH 38/95] KtxImageConverter: write block-compressed formats --- .../KtxImageConverter/KtxImageConverter.cpp | 114 ++++++++++-------- .../Test/KtxImageConverterTest.cpp | 17 ++- 2 files changed, 73 insertions(+), 58 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 9f5befa78..6d01049f6 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -42,7 +42,7 @@ namespace Magnum { namespace Trade { namespace { -/* Need overloaded functions to use different pixel formats in templated code */ +/* Overloaded functions to use different pixel formats in templated code */ bool isFormatImplementationSpecific(PixelFormat format) { return isPixelFormatImplementationSpecific(format); } @@ -98,7 +98,23 @@ FormatPair vulkanFormat(CompressedPixelFormat format) { } } -UnsignedByte componentSize(PixelFormat format) { +Vector3i formatUnitSize(PixelFormat) { + return {1, 1, 1}; +} + +Vector3i formatUnitSize(CompressedPixelFormat format) { + return compressedBlockSize(format); +} + +UnsignedInt formatUnitDataSize(PixelFormat format) { + return pixelSize(format); +} + +UnsignedInt formatUnitDataSize(CompressedPixelFormat format) { + return compressedBlockDataSize(format); +} + +UnsignedByte formatTypeSize(PixelFormat format) { switch(format) { case PixelFormat::R8Unorm: case PixelFormat::RG8Unorm: @@ -169,6 +185,10 @@ UnsignedByte componentSize(PixelFormat format) { CORRADE_ASSERT_UNREACHABLE("componentSize(): unsupported format" << format, {}); } +UnsignedByte formatTypeSize(CompressedPixelFormat) { + return 1; +} + Containers::Pair> samples(CompressedPixelFormat format) { /* There is no good way to auto-generate these. The KDF spec has a format.json (https://github.com/KhronosGroup/KTX-Specification/blob/master/formats.json) @@ -399,7 +419,7 @@ Containers::Pair channelMapping(Implementation::VkForm Containers::Array fillDataFormatDescriptor(PixelFormat format, Implementation::VkFormatSuffix suffix) { const UnsignedInt texelSize = pixelSize(format); - const UnsignedInt typeSize = componentSize(format); + const UnsignedInt typeSize = formatTypeSize(format); const UnsignedInt numChannels = texelSize/typeSize; /* Calculate size */ @@ -554,7 +572,7 @@ Containers::Array fillDataFormatDescriptor(PixelFormat format, Implementat sample.bitOffset = bitOffset; sample.bitLength = bitLength - 1; const auto channelId = channelIds[i]; - sample.channelType = channelId | channelFormat(suffix, channelId); + sample.channelType = channelId | channelFormat(suffix); if(channelId == Implementation::KdfBasicBlockSample::ChannelId::Alpha) sample.channelType |= Implementation::KdfBasicBlockSample::ChannelFormat::Linear; sample.lower = mapping.first(); @@ -641,31 +659,6 @@ Containers::Array fillDataFormatDescriptor(CompressedPixelFormat format, I return data; } -template struct TypeForSize {}; -template<> struct TypeForSize<1> { typedef UnsignedByte Type; }; -template<> struct TypeForSize<2> { typedef UnsignedShort Type; }; -template<> struct TypeForSize<4> { typedef UnsignedInt Type; }; -template<> struct TypeForSize<8> { typedef UnsignedLong Type; }; - -void endianSwap(Containers::ArrayView data, UnsignedInt typeSize) { - switch(typeSize) { - case 1: - /* Single-byte or block-compressed format, nothing to do */ - return; - case 2: - Utility::Endianness::littleEndianInPlace(Containers::arrayCast::Type>(data)); - return; - case 4: - Utility::Endianness::littleEndianInPlace(Containers::arrayCast::Type>(data)); - return; - case 8: - Utility::Endianness::littleEndianInPlace(Containers::arrayCast::Type>(data)); - return; - } - - CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ -} - UnsignedInt leastCommonMultiple(UnsignedInt a, UnsignedInt b) { const UnsignedInt product = a*b; @@ -696,7 +689,32 @@ template void copyPixels(const BasicCompressedImageView& image, Containers::ArrayView pixels) { /** @todo How do we deal with CompressedPixelStorage::skip? ImageView::pixels handles this for non-compressed images. */ - Utility::copy(image.data(), pixels); + Utility::copy(image.data().prefix(pixels.size()), pixels); +} + +template struct TypeForSize {}; +template<> struct TypeForSize<1> { typedef UnsignedByte Type; }; +template<> struct TypeForSize<2> { typedef UnsignedShort Type; }; +template<> struct TypeForSize<4> { typedef UnsignedInt Type; }; +template<> struct TypeForSize<8> { typedef UnsignedLong Type; }; + +void endianSwap(Containers::ArrayView data, UnsignedInt typeSize) { + switch(typeSize) { + case 1: + /* Single-byte or block-compressed format, nothing to do */ + return; + case 2: + Utility::Endianness::littleEndianInPlace(Containers::arrayCast::Type>(data)); + return; + case 4: + Utility::Endianness::littleEndianInPlace(Containers::arrayCast::Type>(data)); + return; + case 8: + Utility::Endianness::littleEndianInPlace(Containers::arrayCast::Type>(data)); + return; + } + + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } /* Using a template template parameter to deduce the image dimensions while @@ -783,10 +801,9 @@ Containers::Array convertLevels(Containers::ArrayView convertLevels(Containers::ArrayView convertLevels(Containers::ArrayView KtxImageConverter::doConvertToData(Containers::ArrayView } Containers::Array KtxImageConverter::doConvertToData(Containers::ArrayView imageLevels) { - //return convertLevels(imageLevels); - return {}; + return convertLevels(imageLevels); } Containers::Array KtxImageConverter::doConvertToData(Containers::ArrayView imageLevels) { - //return convertLevels(imageLevels); - return {}; + return convertLevels(imageLevels); } Containers::Array KtxImageConverter::doConvertToData(Containers::ArrayView imageLevels) { - //return convertLevels(imageLevels); - return {}; + return convertLevels(imageLevels); } }} diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index f2c45cf01..80816af9e 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -216,8 +216,6 @@ const CompressedPixelFormat UnsupportedCompressedFormats[]{ }; void KtxImageConverterTest::supportedCompressedFormat() { - CORRADE_SKIP("Compressed image export isn't implemented yet"); - Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); const UnsignedByte bytes[32]{}; @@ -237,8 +235,6 @@ void KtxImageConverterTest::supportedCompressedFormat() { } void KtxImageConverterTest::unsupportedCompressedFormat() { - CORRADE_SKIP("Compressed image export isn't implemented yet"); - Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); std::ostringstream out; @@ -317,14 +313,17 @@ void KtxImageConverterTest::pvrtcRgb() { auto&& data = PvrtcRgbData[testCaseInstanceId()]; setTestCaseDescription(data.name); - CORRADE_SKIP("Compressed image export isn't implemented yet"); - Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); const UnsignedByte bytes[16]{}; - CORRADE_INTERNAL_ASSERT(Containers::arraySize(bytes) >= compressedBlockDataSize(data.inputFormat)); - const CompressedImageView2D inputImage{data.inputFormat, {2, 2}, bytes}; + const UnsignedInt dataSize = compressedBlockDataSize(data.inputFormat); + CORRADE_INTERNAL_ASSERT(Containers::arraySize(bytes) >= dataSize); + + const Vector2i imageSize = {2, 2}; + CORRADE_INTERNAL_ASSERT((Vector3i{imageSize, 1}) <= compressedBlockSize(data.inputFormat)); + + const CompressedImageView2D inputImage{data.inputFormat, imageSize, Containers::arrayView(bytes).prefix(dataSize)}; const auto output = converter->convertToData(inputImage); CORRADE_VERIFY(output); @@ -338,7 +337,7 @@ void KtxImageConverterTest::pvrtcRgb() { CORRADE_VERIFY(image); CORRADE_VERIFY(image->isCompressed()); CORRADE_COMPARE(image->compressedFormat(), data.outputFormat); - CORRADE_COMPARE(image->data(), inputImage.data()); + CORRADE_COMPARE_AS(image->data(), inputImage.data(), TestSuite::Compare::Container); } void KtxImageConverterTest::levelZeroSize() { From b33e7a292b02504ea14ceadb4508e51d0289e580 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Sat, 31 Jul 2021 21:57:05 +0200 Subject: [PATCH 39/95] Ktx{Importer, ImageConverter}: split formatMapping.hpp into compressed and uncompressed and add the original Vulkan format name as a comment --- .../KtxImageConverter/KtxImageConverter.cpp | 14 +- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 6 +- .../KtxImporter/compressedFormatMapping.hpp | 103 +++++++++ .../KtxImporter/formatMapping.hpp | 195 ++++++------------ .../KtxImporter/formatMapping.py | 40 ++-- 5 files changed, 193 insertions(+), 165 deletions(-) create mode 100644 src/MagnumPlugins/KtxImporter/compressedFormatMapping.hpp diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 6d01049f6..322630834 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -55,10 +55,10 @@ typedef Containers::Pair decodeFormat(Implementation::VkFormat vkFormat) { formatMapping.hpp isn't updated without adding an extra check. */ PixelFormat format{}; switch(vkFormat) { - #define _p(vulkan, magnum, _type) case vulkan: format = PixelFormat::magnum; break; + #define _c(vulkan, magnum, _type) case vulkan: format = PixelFormat::magnum; break; #include "MagnumPlugins/KtxImporter/formatMapping.hpp" - #undef _p + #undef _c default: break; } @@ -238,7 +238,7 @@ Containers::Optional decodeFormat(Implementation::VkFormat vkFormat) { CompressedPixelFormat compressedFormat{}; switch(vkFormat) { #define _c(vulkan, magnum, _type) case vulkan: compressedFormat = CompressedPixelFormat::magnum; break; - #include "MagnumPlugins/KtxImporter/formatMapping.hpp" + #include "MagnumPlugins/KtxImporter/compressedFormatMapping.hpp" #undef _c default: break; diff --git a/src/MagnumPlugins/KtxImporter/compressedFormatMapping.hpp b/src/MagnumPlugins/KtxImporter/compressedFormatMapping.hpp new file mode 100644 index 000000000..5c3f3024e --- /dev/null +++ b/src/MagnumPlugins/KtxImporter/compressedFormatMapping.hpp @@ -0,0 +1,103 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + Copyright © 2021 Pablo Escobar + + 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. +*/ + +/* Autogenerated from formatMapping.py! Do not edit! */ + +/* VkFormat, CompressedPixelFormat, Implementation::VkFormatSuffix */ +#ifdef _c +_c(131, Bc1RGBUnorm, UNORM) /* VK_FORMAT_BC1_RGB_UNORM_BLOCK */ +_c(132, Bc1RGBSrgb, SRGB) /* VK_FORMAT_BC1_RGB_SRGB_BLOCK */ +_c(133, Bc1RGBAUnorm, UNORM) /* VK_FORMAT_BC1_RGBA_UNORM_BLOCK */ +_c(134, Bc1RGBASrgb, SRGB) /* VK_FORMAT_BC1_RGBA_SRGB_BLOCK */ +_c(135, Bc2RGBAUnorm, UNORM) /* VK_FORMAT_BC2_UNORM_BLOCK */ +_c(136, Bc2RGBASrgb, SRGB) /* VK_FORMAT_BC2_SRGB_BLOCK */ +_c(137, Bc3RGBAUnorm, UNORM) /* VK_FORMAT_BC3_UNORM_BLOCK */ +_c(138, Bc3RGBASrgb, SRGB) /* VK_FORMAT_BC3_SRGB_BLOCK */ +_c(139, Bc4RUnorm, UNORM) /* VK_FORMAT_BC4_UNORM_BLOCK */ +_c(140, Bc4RSnorm, SNORM) /* VK_FORMAT_BC4_SNORM_BLOCK */ +_c(141, Bc5RGUnorm, UNORM) /* VK_FORMAT_BC5_UNORM_BLOCK */ +_c(142, Bc5RGSnorm, SNORM) /* VK_FORMAT_BC5_SNORM_BLOCK */ +_c(143, Bc6hRGBUfloat, UFLOAT) /* VK_FORMAT_BC6H_UFLOAT_BLOCK */ +_c(144, Bc6hRGBSfloat, SFLOAT) /* VK_FORMAT_BC6H_SFLOAT_BLOCK */ +_c(145, Bc7RGBAUnorm, UNORM) /* VK_FORMAT_BC7_UNORM_BLOCK */ +_c(146, Bc7RGBASrgb, SRGB) /* VK_FORMAT_BC7_SRGB_BLOCK */ +_c(153, EacR11Unorm, UNORM) /* VK_FORMAT_EAC_R11_UNORM_BLOCK */ +_c(154, EacR11Snorm, SNORM) /* VK_FORMAT_EAC_R11_SNORM_BLOCK */ +_c(155, EacRG11Unorm, UNORM) /* VK_FORMAT_EAC_R11G11_UNORM_BLOCK */ +_c(156, EacRG11Snorm, SNORM) /* VK_FORMAT_EAC_R11G11_SNORM_BLOCK */ +_c(147, Etc2RGB8Unorm, UNORM) /* VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK */ +_c(148, Etc2RGB8Srgb, SRGB) /* VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK */ +_c(149, Etc2RGB8A1Unorm, UNORM) /* VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK */ +_c(150, Etc2RGB8A1Srgb, SRGB) /* VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK */ +_c(151, Etc2RGBA8Unorm, UNORM) /* VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK */ +_c(152, Etc2RGBA8Srgb, SRGB) /* VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK */ +_c(157, Astc4x4RGBAUnorm, UNORM) /* VK_FORMAT_ASTC_4x4_UNORM_BLOCK */ +_c(158, Astc4x4RGBASrgb, SRGB) /* VK_FORMAT_ASTC_4x4_SRGB_BLOCK */ +_c(1000066000, Astc4x4RGBAF, SFLOAT) /* VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT */ +_c(159, Astc5x4RGBAUnorm, UNORM) /* VK_FORMAT_ASTC_5x4_UNORM_BLOCK */ +_c(160, Astc5x4RGBASrgb, SRGB) /* VK_FORMAT_ASTC_5x4_SRGB_BLOCK */ +_c(1000066001, Astc5x4RGBAF, SFLOAT) /* VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT */ +_c(161, Astc5x5RGBAUnorm, UNORM) /* VK_FORMAT_ASTC_5x5_UNORM_BLOCK */ +_c(162, Astc5x5RGBASrgb, SRGB) /* VK_FORMAT_ASTC_5x5_SRGB_BLOCK */ +_c(1000066002, Astc5x5RGBAF, SFLOAT) /* VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT */ +_c(163, Astc6x5RGBAUnorm, UNORM) /* VK_FORMAT_ASTC_6x5_UNORM_BLOCK */ +_c(164, Astc6x5RGBASrgb, SRGB) /* VK_FORMAT_ASTC_6x5_SRGB_BLOCK */ +_c(1000066003, Astc6x5RGBAF, SFLOAT) /* VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT */ +_c(165, Astc6x6RGBAUnorm, UNORM) /* VK_FORMAT_ASTC_6x6_UNORM_BLOCK */ +_c(166, Astc6x6RGBASrgb, SRGB) /* VK_FORMAT_ASTC_6x6_SRGB_BLOCK */ +_c(1000066004, Astc6x6RGBAF, SFLOAT) /* VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT */ +_c(167, Astc8x5RGBAUnorm, UNORM) /* VK_FORMAT_ASTC_8x5_UNORM_BLOCK */ +_c(168, Astc8x5RGBASrgb, SRGB) /* VK_FORMAT_ASTC_8x5_SRGB_BLOCK */ +_c(1000066005, Astc8x5RGBAF, SFLOAT) /* VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT */ +_c(169, Astc8x6RGBAUnorm, UNORM) /* VK_FORMAT_ASTC_8x6_UNORM_BLOCK */ +_c(170, Astc8x6RGBASrgb, SRGB) /* VK_FORMAT_ASTC_8x6_SRGB_BLOCK */ +_c(1000066006, Astc8x6RGBAF, SFLOAT) /* VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT */ +_c(171, Astc8x8RGBAUnorm, UNORM) /* VK_FORMAT_ASTC_8x8_UNORM_BLOCK */ +_c(172, Astc8x8RGBASrgb, SRGB) /* VK_FORMAT_ASTC_8x8_SRGB_BLOCK */ +_c(1000066007, Astc8x8RGBAF, SFLOAT) /* VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT */ +_c(173, Astc10x5RGBAUnorm, UNORM) /* VK_FORMAT_ASTC_10x5_UNORM_BLOCK */ +_c(174, Astc10x5RGBASrgb, SRGB) /* VK_FORMAT_ASTC_10x5_SRGB_BLOCK */ +_c(1000066008, Astc10x5RGBAF, SFLOAT) /* VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT */ +_c(175, Astc10x6RGBAUnorm, UNORM) /* VK_FORMAT_ASTC_10x6_UNORM_BLOCK */ +_c(176, Astc10x6RGBASrgb, SRGB) /* VK_FORMAT_ASTC_10x6_SRGB_BLOCK */ +_c(1000066009, Astc10x6RGBAF, SFLOAT) /* VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT */ +_c(177, Astc10x8RGBAUnorm, UNORM) /* VK_FORMAT_ASTC_10x8_UNORM_BLOCK */ +_c(178, Astc10x8RGBASrgb, SRGB) /* VK_FORMAT_ASTC_10x8_SRGB_BLOCK */ +_c(1000066010, Astc10x8RGBAF, SFLOAT) /* VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT */ +_c(179, Astc10x10RGBAUnorm, UNORM) /* VK_FORMAT_ASTC_10x10_UNORM_BLOCK */ +_c(180, Astc10x10RGBASrgb, SRGB) /* VK_FORMAT_ASTC_10x10_SRGB_BLOCK */ +_c(1000066011, Astc10x10RGBAF, SFLOAT) /* VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT */ +_c(181, Astc12x10RGBAUnorm, UNORM) /* VK_FORMAT_ASTC_12x10_UNORM_BLOCK */ +_c(182, Astc12x10RGBASrgb, SRGB) /* VK_FORMAT_ASTC_12x10_SRGB_BLOCK */ +_c(1000066012, Astc12x10RGBAF, SFLOAT) /* VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT */ +_c(183, Astc12x12RGBAUnorm, UNORM) /* VK_FORMAT_ASTC_12x12_UNORM_BLOCK */ +_c(184, Astc12x12RGBASrgb, SRGB) /* VK_FORMAT_ASTC_12x12_SRGB_BLOCK */ +_c(1000066013, Astc12x12RGBAF, SFLOAT) /* VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT */ +_c(1000054000, PvrtcRGBA2bppUnorm, UNORM) /* VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG */ +_c(1000054004, PvrtcRGBA2bppSrgb, SRGB) /* VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG */ +_c(1000054001, PvrtcRGBA4bppUnorm, UNORM) /* VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG */ +_c(1000054005, PvrtcRGBA4bppSrgb, SRGB) /* VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG */ +#endif diff --git a/src/MagnumPlugins/KtxImporter/formatMapping.hpp b/src/MagnumPlugins/KtxImporter/formatMapping.hpp index 80dc0cde7..d13b01627 100644 --- a/src/MagnumPlugins/KtxImporter/formatMapping.hpp +++ b/src/MagnumPlugins/KtxImporter/formatMapping.hpp @@ -26,138 +26,65 @@ /* Autogenerated from formatMapping.py! Do not edit! */ -#ifdef _p /* PixelFormat */ -_p(9, R8Unorm, UNORM) -_p(16, RG8Unorm, UNORM) -_p(23, RGB8Unorm, UNORM) -_p(37, RGBA8Unorm, UNORM) -_p(10, R8Snorm, SNORM) -_p(17, RG8Snorm, SNORM) -_p(24, RGB8Snorm, SNORM) -_p(38, RGBA8Snorm, SNORM) -_p(15, R8Srgb, SRGB) -_p(22, RG8Srgb, SRGB) -_p(29, RGB8Srgb, SRGB) -_p(43, RGBA8Srgb, SRGB) -_p(13, R8UI, UINT) -_p(20, RG8UI, UINT) -_p(27, RGB8UI, UINT) -_p(41, RGBA8UI, UINT) -_p(14, R8I, SINT) -_p(21, RG8I, SINT) -_p(28, RGB8I, SINT) -_p(42, RGBA8I, SINT) -_p(70, R16Unorm, UNORM) -_p(77, RG16Unorm, UNORM) -_p(84, RGB16Unorm, UNORM) -_p(91, RGBA16Unorm, UNORM) -_p(71, R16Snorm, SNORM) -_p(78, RG16Snorm, SNORM) -_p(85, RGB16Snorm, SNORM) -_p(92, RGBA16Snorm, SNORM) -_p(74, R16UI, UINT) -_p(81, RG16UI, UINT) -_p(88, RGB16UI, UINT) -_p(95, RGBA16UI, UINT) -_p(75, R16I, SINT) -_p(82, RG16I, SINT) -_p(89, RGB16I, SINT) -_p(96, RGBA16I, SINT) -_p(98, R32UI, UINT) -_p(101, RG32UI, UINT) -_p(104, RGB32UI, UINT) -_p(107, RGBA32UI, UINT) -_p(99, R32I, SINT) -_p(102, RG32I, SINT) -_p(105, RGB32I, SINT) -_p(108, RGBA32I, SINT) -_p(76, R16F, SFLOAT) -_p(83, RG16F, SFLOAT) -_p(90, RGB16F, SFLOAT) -_p(97, RGBA16F, SFLOAT) -_p(100, R32F, SFLOAT) -_p(103, RG32F, SFLOAT) -_p(106, RGB32F, SFLOAT) -_p(109, RGBA32F, SFLOAT) -_p(124, Depth16Unorm, UNORM) -_p(125, Depth24Unorm, UNORM) -_p(126, Depth32F, SFLOAT) -_p(127, Stencil8UI, UINT) -_p(128, Depth16UnormStencil8UI, UINT) -_p(129, Depth24UnormStencil8UI, UINT) -_p(130, Depth32FStencil8UI, UINT) -#endif -#ifdef _c /* CompressedPixelFormat */ -_c(131, Bc1RGBUnorm, UNORM) -_c(132, Bc1RGBSrgb, SRGB) -_c(133, Bc1RGBAUnorm, UNORM) -_c(134, Bc1RGBASrgb, SRGB) -_c(135, Bc2RGBAUnorm, UNORM) -_c(136, Bc2RGBASrgb, SRGB) -_c(137, Bc3RGBAUnorm, UNORM) -_c(138, Bc3RGBASrgb, SRGB) -_c(139, Bc4RUnorm, UNORM) -_c(140, Bc4RSnorm, SNORM) -_c(141, Bc5RGUnorm, UNORM) -_c(142, Bc5RGSnorm, SNORM) -_c(143, Bc6hRGBUfloat, UFLOAT) -_c(144, Bc6hRGBSfloat, SFLOAT) -_c(145, Bc7RGBAUnorm, UNORM) -_c(146, Bc7RGBASrgb, SRGB) -_c(153, EacR11Unorm, UNORM) -_c(154, EacR11Snorm, SNORM) -_c(155, EacRG11Unorm, UNORM) -_c(156, EacRG11Snorm, SNORM) -_c(147, Etc2RGB8Unorm, UNORM) -_c(148, Etc2RGB8Srgb, SRGB) -_c(149, Etc2RGB8A1Unorm, UNORM) -_c(150, Etc2RGB8A1Srgb, SRGB) -_c(151, Etc2RGBA8Unorm, UNORM) -_c(152, Etc2RGBA8Srgb, SRGB) -_c(157, Astc4x4RGBAUnorm, UNORM) -_c(158, Astc4x4RGBASrgb, SRGB) -_c(1000066000, Astc4x4RGBAF, SFLOAT) -_c(159, Astc5x4RGBAUnorm, UNORM) -_c(160, Astc5x4RGBASrgb, SRGB) -_c(1000066001, Astc5x4RGBAF, SFLOAT) -_c(161, Astc5x5RGBAUnorm, UNORM) -_c(162, Astc5x5RGBASrgb, SRGB) -_c(1000066002, Astc5x5RGBAF, SFLOAT) -_c(163, Astc6x5RGBAUnorm, UNORM) -_c(164, Astc6x5RGBASrgb, SRGB) -_c(1000066003, Astc6x5RGBAF, SFLOAT) -_c(165, Astc6x6RGBAUnorm, UNORM) -_c(166, Astc6x6RGBASrgb, SRGB) -_c(1000066004, Astc6x6RGBAF, SFLOAT) -_c(167, Astc8x5RGBAUnorm, UNORM) -_c(168, Astc8x5RGBASrgb, SRGB) -_c(1000066005, Astc8x5RGBAF, SFLOAT) -_c(169, Astc8x6RGBAUnorm, UNORM) -_c(170, Astc8x6RGBASrgb, SRGB) -_c(1000066006, Astc8x6RGBAF, SFLOAT) -_c(171, Astc8x8RGBAUnorm, UNORM) -_c(172, Astc8x8RGBASrgb, SRGB) -_c(1000066007, Astc8x8RGBAF, SFLOAT) -_c(173, Astc10x5RGBAUnorm, UNORM) -_c(174, Astc10x5RGBASrgb, SRGB) -_c(1000066008, Astc10x5RGBAF, SFLOAT) -_c(175, Astc10x6RGBAUnorm, UNORM) -_c(176, Astc10x6RGBASrgb, SRGB) -_c(1000066009, Astc10x6RGBAF, SFLOAT) -_c(177, Astc10x8RGBAUnorm, UNORM) -_c(178, Astc10x8RGBASrgb, SRGB) -_c(1000066010, Astc10x8RGBAF, SFLOAT) -_c(179, Astc10x10RGBAUnorm, UNORM) -_c(180, Astc10x10RGBASrgb, SRGB) -_c(1000066011, Astc10x10RGBAF, SFLOAT) -_c(181, Astc12x10RGBAUnorm, UNORM) -_c(182, Astc12x10RGBASrgb, SRGB) -_c(1000066012, Astc12x10RGBAF, SFLOAT) -_c(183, Astc12x12RGBAUnorm, UNORM) -_c(184, Astc12x12RGBASrgb, SRGB) -_c(1000066013, Astc12x12RGBAF, SFLOAT) -_c(1000054000, PvrtcRGBA2bppUnorm, UNORM) -_c(1000054004, PvrtcRGBA2bppSrgb, SRGB) -_c(1000054001, PvrtcRGBA4bppUnorm, UNORM) -_c(1000054005, PvrtcRGBA4bppSrgb, SRGB) +/* VkFormat, PixelFormat, Implementation::VkFormatSuffix */ +#ifdef _c +_c(9, R8Unorm, UNORM) /* VK_FORMAT_R8_UNORM */ +_c(16, RG8Unorm, UNORM) /* VK_FORMAT_R8G8_UNORM */ +_c(23, RGB8Unorm, UNORM) /* VK_FORMAT_R8G8B8_UNORM */ +_c(37, RGBA8Unorm, UNORM) /* VK_FORMAT_R8G8B8A8_UNORM */ +_c(10, R8Snorm, SNORM) /* VK_FORMAT_R8_SNORM */ +_c(17, RG8Snorm, SNORM) /* VK_FORMAT_R8G8_SNORM */ +_c(24, RGB8Snorm, SNORM) /* VK_FORMAT_R8G8B8_SNORM */ +_c(38, RGBA8Snorm, SNORM) /* VK_FORMAT_R8G8B8A8_SNORM */ +_c(15, R8Srgb, SRGB) /* VK_FORMAT_R8_SRGB */ +_c(22, RG8Srgb, SRGB) /* VK_FORMAT_R8G8_SRGB */ +_c(29, RGB8Srgb, SRGB) /* VK_FORMAT_R8G8B8_SRGB */ +_c(43, RGBA8Srgb, SRGB) /* VK_FORMAT_R8G8B8A8_SRGB */ +_c(13, R8UI, UINT) /* VK_FORMAT_R8_UINT */ +_c(20, RG8UI, UINT) /* VK_FORMAT_R8G8_UINT */ +_c(27, RGB8UI, UINT) /* VK_FORMAT_R8G8B8_UINT */ +_c(41, RGBA8UI, UINT) /* VK_FORMAT_R8G8B8A8_UINT */ +_c(14, R8I, SINT) /* VK_FORMAT_R8_SINT */ +_c(21, RG8I, SINT) /* VK_FORMAT_R8G8_SINT */ +_c(28, RGB8I, SINT) /* VK_FORMAT_R8G8B8_SINT */ +_c(42, RGBA8I, SINT) /* VK_FORMAT_R8G8B8A8_SINT */ +_c(70, R16Unorm, UNORM) /* VK_FORMAT_R16_UNORM */ +_c(77, RG16Unorm, UNORM) /* VK_FORMAT_R16G16_UNORM */ +_c(84, RGB16Unorm, UNORM) /* VK_FORMAT_R16G16B16_UNORM */ +_c(91, RGBA16Unorm, UNORM) /* VK_FORMAT_R16G16B16A16_UNORM */ +_c(71, R16Snorm, SNORM) /* VK_FORMAT_R16_SNORM */ +_c(78, RG16Snorm, SNORM) /* VK_FORMAT_R16G16_SNORM */ +_c(85, RGB16Snorm, SNORM) /* VK_FORMAT_R16G16B16_SNORM */ +_c(92, RGBA16Snorm, SNORM) /* VK_FORMAT_R16G16B16A16_SNORM */ +_c(74, R16UI, UINT) /* VK_FORMAT_R16_UINT */ +_c(81, RG16UI, UINT) /* VK_FORMAT_R16G16_UINT */ +_c(88, RGB16UI, UINT) /* VK_FORMAT_R16G16B16_UINT */ +_c(95, RGBA16UI, UINT) /* VK_FORMAT_R16G16B16A16_UINT */ +_c(75, R16I, SINT) /* VK_FORMAT_R16_SINT */ +_c(82, RG16I, SINT) /* VK_FORMAT_R16G16_SINT */ +_c(89, RGB16I, SINT) /* VK_FORMAT_R16G16B16_SINT */ +_c(96, RGBA16I, SINT) /* VK_FORMAT_R16G16B16A16_SINT */ +_c(98, R32UI, UINT) /* VK_FORMAT_R32_UINT */ +_c(101, RG32UI, UINT) /* VK_FORMAT_R32G32_UINT */ +_c(104, RGB32UI, UINT) /* VK_FORMAT_R32G32B32_UINT */ +_c(107, RGBA32UI, UINT) /* VK_FORMAT_R32G32B32A32_UINT */ +_c(99, R32I, SINT) /* VK_FORMAT_R32_SINT */ +_c(102, RG32I, SINT) /* VK_FORMAT_R32G32_SINT */ +_c(105, RGB32I, SINT) /* VK_FORMAT_R32G32B32_SINT */ +_c(108, RGBA32I, SINT) /* VK_FORMAT_R32G32B32A32_SINT */ +_c(76, R16F, SFLOAT) /* VK_FORMAT_R16_SFLOAT */ +_c(83, RG16F, SFLOAT) /* VK_FORMAT_R16G16_SFLOAT */ +_c(90, RGB16F, SFLOAT) /* VK_FORMAT_R16G16B16_SFLOAT */ +_c(97, RGBA16F, SFLOAT) /* VK_FORMAT_R16G16B16A16_SFLOAT */ +_c(100, R32F, SFLOAT) /* VK_FORMAT_R32_SFLOAT */ +_c(103, RG32F, SFLOAT) /* VK_FORMAT_R32G32_SFLOAT */ +_c(106, RGB32F, SFLOAT) /* VK_FORMAT_R32G32B32_SFLOAT */ +_c(109, RGBA32F, SFLOAT) /* VK_FORMAT_R32G32B32A32_SFLOAT */ +_c(124, Depth16Unorm, UNORM) /* VK_FORMAT_D16_UNORM */ +_c(125, Depth24Unorm, UNORM) /* VK_FORMAT_X8_D24_UNORM_PACK32 */ +_c(126, Depth32F, SFLOAT) /* VK_FORMAT_D32_SFLOAT */ +_c(127, Stencil8UI, UINT) /* VK_FORMAT_S8_UINT */ +_c(128, Depth16UnormStencil8UI, UINT) /* VK_FORMAT_D16_UNORM_S8_UINT */ +_c(129, Depth24UnormStencil8UI, UINT) /* VK_FORMAT_D24_UNORM_S8_UINT */ +_c(130, Depth32FStencil8UI, UINT) /* VK_FORMAT_D32_SFLOAT_S8_UINT */ #endif diff --git a/src/MagnumPlugins/KtxImporter/formatMapping.py b/src/MagnumPlugins/KtxImporter/formatMapping.py index 1fbc156bd..f73ba4ba5 100644 --- a/src/MagnumPlugins/KtxImporter/formatMapping.py +++ b/src/MagnumPlugins/KtxImporter/formatMapping.py @@ -34,14 +34,9 @@ parser = argparse.ArgumentParser() parser.add_argument('magnum_source') -parser.add_argument('-o', '--output', default='formatMapping.hpp') args = parser.parse_args() magnum_dir = args.magnum_source -file_out = args.output - -print('Writing to', file_out) - vulkan_header = os.path.join(magnum_dir, 'src/MagnumExternal/Vulkan/flextVk.h') vulkan_formats = {} @@ -57,7 +52,7 @@ assert(not match.group(1) in vulkan_formats) vulkan_formats[match.group(1)] = match.group(2) -Format = namedtuple('Format', 'compressed magnum vulkan type') +Format = namedtuple('Format', 'compressed magnum vulkan_name vulkan suffix') formats = [] format_header = os.path.join(magnum_dir, 'src/Magnum/Vk/PixelFormat.h') @@ -74,11 +69,11 @@ vulkan_name = match.group(3) assert(vulkan_name in vulkan_formats) - type = re.search('\w+_([U|S](NORM|INT|FLOAT|RGB))\w*', vulkan_name) - assert type != None - assert type.group(1) != 'URGB' + suffix = re.search('\w+_([U|S](NORM|INT|FLOAT|RGB))\w*', vulkan_name) + assert suffix != None + assert suffix.group(1) != 'URGB' - formats.append(Format(compressed, magnum_name, vulkan_formats[vulkan_name], type.group(1))) + formats.append(Format(compressed, magnum_name, vulkan_name, vulkan_formats[vulkan_name], suffix.group(1))) if len(formats) != 135: print('Unexpected number of formats') @@ -88,8 +83,11 @@ def partition(pred, iterable): t1, t2 = itertools.tee(iterable) return itertools.filterfalse(pred, t1), filter(pred, t2) -# There's no PVRTC2 in Magnum::PixelFormat -formats = [f for f in formats if not f.magnum.startswith('Pvrtc2')] +compressed = lambda f : f.compressed +formats, compressed_formats = partition(compressed, formats) + +# There's no PVRTC2 in CompressedPixelFormat +compressed_formats = [f for f in compressed_formats if not f.magnum.startswith('Pvrtc2')] header = '''/* This file is part of Magnum. @@ -121,18 +119,18 @@ def partition(pred, iterable): ''' -with open(file_out, 'w', encoding='utf-8') as outfile: - compressed = lambda f : f.compressed - formats, compressed_formats = partition(compressed, formats) - +with open('formatMapping.hpp', 'w', encoding='utf-8') as outfile: outfile.write(header) - - outfile.write('#ifdef _p /* PixelFormat */\n') + outfile.write('/* VkFormat, PixelFormat, Implementation::VkFormatSuffix */\n') + outfile.write('#ifdef _c\n') for format in formats: - outfile.write('_p(' + format.vulkan + ', ' + format.magnum + ', ' + format.type + ')\n') + outfile.write('_c({}, {}, {}) /* VK_FORMAT_{} */\n'.format(format.vulkan , format.magnum, format.suffix, format.vulkan_name)) outfile.write('#endif\n') - outfile.write('#ifdef _c /* CompressedPixelFormat */\n') +with open('compressedFormatMapping.hpp', 'w', encoding='utf-8') as outfile: + outfile.write(header) + outfile.write('/* VkFormat, CompressedPixelFormat, Implementation::VkFormatSuffix */\n') + outfile.write('#ifdef _c\n') for format in compressed_formats: - outfile.write('_c(' + format.vulkan + ', ' + format.magnum + ', ' + format.type + ')\n') + outfile.write('_c({}, {}, {}) /* VK_FORMAT_{} */\n'.format(format.vulkan , format.magnum, format.suffix, format.vulkan_name)) outfile.write('#endif\n') From b2d65f04c6d7c227eced96b219e9181b86b1ccbc Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Sat, 31 Jul 2021 21:58:46 +0200 Subject: [PATCH 40/95] KtxImageConverter: remove unneeded template parameter --- src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 322630834..37a054f82 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -720,8 +720,8 @@ void endianSwap(Containers::ArrayView data, UnsignedInt typeSize) { /* Using a template template parameter to deduce the image dimensions while matching both ImageView and CompressedImageView. Matching on the ImageView typedefs doesn't work, so we need the extra parameter of BasicImageView. */ -template class View> -Containers::Array convertLevels(Containers::ArrayView> imageLevels) { +template class View> +Containers::Array convertLevels(Containers::ArrayView> imageLevels) { if(imageLevels.empty()) { Error() << "Trade::KtxImageConverter::convertToData(): expected at least 1 mip level"; return {}; @@ -810,7 +810,7 @@ Containers::Array convertLevels(Containers::ArrayView mipSize = Math::max(size >> mip, 1); - const View& image = imageLevels[mip]; + const auto& image = imageLevels[mip]; if(image.format() != format) { Error() << "Trade::KtxImageConverter::convertToData(): expected " @@ -867,7 +867,7 @@ Containers::Array convertLevels(Containers::ArrayView& image = imageLevels[i]; + const auto& image = imageLevels[i]; const auto pixels = data.suffix(level.byteOffset).prefix(level.byteLength); copyPixels(image, pixels); From 84485e5f1679951e27aac8ef1910bae5e86ce4bf Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Sun, 1 Aug 2021 01:59:25 +0200 Subject: [PATCH 41/95] KtxImageConverter: read swizzle and writer name key/value data from configuration --- .../KtxImageConverter/KtxImageConverter.conf | 9 ++ .../KtxImageConverter/KtxImageConverter.cpp | 82 ++++++++++++------- .../KtxImageConverter/KtxImageConverter.h | 4 + 3 files changed, 66 insertions(+), 29 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.conf b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.conf index e69de29bb..f4c7b1354 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.conf +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.conf @@ -0,0 +1,9 @@ +# [configuration_] +[configuration] +# Name of the tool writing the image file, saved in the file header +writerName=Magnum::KtxImageConverter +# Format swizzle string to save in the file header. This doesn't save swizzled +# data, it only tells readers the desired channel mapping during import. Must +# be empty or 4 characters long, valid characters are r,g,b,a,0,1. +swizzle= +# [configuration_] diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 37a054f82..82b74763b 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -26,10 +26,13 @@ #include "KtxImageConverter.h" +#include #include #include +#include #include #include +#include #include #include #include @@ -717,16 +720,29 @@ void endianSwap(Containers::ArrayView data, UnsignedInt typeSize) { CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } +} + +KtxImageConverter::KtxImageConverter(PluginManager::AbstractManager& manager, const std::string& plugin): AbstractImageConverter{manager, plugin} {} + +ImageConverterFeatures KtxImageConverter::doFeatures() const { + return ImageConverterFeature::ConvertLevels1DToData | + ImageConverterFeature::ConvertLevels2DToData | + ImageConverterFeature::ConvertLevels3DToData | + ImageConverterFeature::ConvertCompressedLevels1DToData | + ImageConverterFeature::ConvertCompressedLevels2DToData | + ImageConverterFeature::ConvertCompressedLevels3DToData; +} + /* Using a template template parameter to deduce the image dimensions while matching both ImageView and CompressedImageView. Matching on the ImageView typedefs doesn't work, so we need the extra parameter of BasicImageView. */ template class View> -Containers::Array convertLevels(Containers::ArrayView> imageLevels) { if(imageLevels.empty()) { Error() << "Trade::KtxImageConverter::convertToData(): expected at least 1 mip level"; return {}; } +Containers::Array KtxImageConverter::convertLevels(Containers::ArrayView> imageLevels) { const auto format = imageLevels.front().format(); if(isFormatImplementationSpecific(format)) { @@ -743,9 +759,24 @@ Containers::Array convertLevels(Containers::ArrayView dataFormatDescriptor = fillDataFormatDescriptor(format, vkFormat.second()); /* Fill key/value data. Values can be any byte-string but we only write - constant text strings. Keys must be sorted alphabetically.*/ + constant text strings. Keys must be sorted alphabetically. + Entries with an empty value won't be written. */ using namespace Containers::Literals; + const std::string writerName = configuration().value("writerName"); + const std::string swizzle = configuration().value("swizzle"); + + if(!swizzle.empty() && swizzle.size() != 4) { + Error{} << "Trade::KtxImageConverter::convertToData(): invalid swizzle length, expected 4 but got" << swizzle.size(); + return {}; + } + + if(swizzle.find_first_not_of("rgba01") != std::string::npos) { + /* operator << is ambiguous, could be Containers::String or Containers::StringView */ + Error{} << "Trade::KtxImageConverter::convertToData(): invalid characters in swizzle" << Containers::StringView{swizzle}; + return {}; + } + const Containers::Pair keyValueMap[]{ /* Origin left, bottom, back (increasing right, up, out) */ /** @todo KTX spec says "most other APIs and the majority of texture @@ -753,14 +784,18 @@ Containers::Array convertLevels(Containers::ArrayView convertLevels(Containers::ArrayView keyValueData{ValueInit, kvdSize}; for(const auto& entry: keyValueMap) { - const auto key = entry.first(); - const auto value = entry.second(); - const UnsignedInt length = key.size() + 1 + value.size() + 1; - *reinterpret_cast(keyValueData.suffix(kvdOffset).data()) = length; - Utility::Endianness::littleEndianInPlace(length); - kvdOffset += sizeof(length); - Utility::copy(key, keyValueData.suffix(kvdOffset).prefix(key.size())); - kvdOffset += entry.first().size() + 1; - Utility::copy(value, keyValueData.suffix(kvdOffset).prefix(value.size())); - kvdOffset += entry.second().size() + 1; - kvdOffset = (kvdOffset + 3)/4*4; + if(!entry.second().isEmpty()) { + const auto key = entry.first(); + const auto value = entry.second(); + const UnsignedInt length = key.size() + 1 + value.size() + 1; + *reinterpret_cast(keyValueData.suffix(kvdOffset).data()) = length; + Utility::Endianness::littleEndianInPlace(length); + kvdOffset += sizeof(length); + Utility::copy(key, keyValueData.suffix(kvdOffset).prefix(key.size())); + kvdOffset += entry.first().size() + 1; + Utility::copy(value, keyValueData.suffix(kvdOffset).prefix(value.size())); + kvdOffset += entry.second().size() + 1; + kvdOffset = (kvdOffset + 3)/4*4; + } } CORRADE_INTERNAL_ASSERT(kvdOffset == kvdSize); @@ -905,19 +942,6 @@ Containers::Array convertLevels(Containers::ArrayView KtxImageConverter::doConvertToData(Containers::ArrayView imageLevels) { return convertLevels(imageLevels); } diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h index 2913a3be6..29d65d1ae 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h @@ -63,6 +63,10 @@ class MAGNUM_KTXIMAGECONVERTER_EXPORT KtxImageConverter: public AbstractImageCon Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(Containers::ArrayView imageLevels) override; Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(Containers::ArrayView imageLevels) override; Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(Containers::ArrayView imageLevels) override; + + private: + template class View> + Containers::Array convertLevels(Containers::ArrayView> imageLevels); }; }} From 7d14569f4b1df8e4b05f5d7a2a8a30608cfbbf9e Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Sun, 1 Aug 2021 02:03:10 +0200 Subject: [PATCH 42/95] KtxImageConverter: remove unneeded image checks --- .../KtxImageConverter/KtxImageConverter.cpp | 15 --- .../Test/KtxImageConverterTest.cpp | 112 ++++-------------- 2 files changed, 20 insertions(+), 107 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 82b74763b..e7074f799 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -737,11 +737,6 @@ ImageConverterFeatures KtxImageConverter::doFeatures() const { matching both ImageView and CompressedImageView. Matching on the ImageView typedefs doesn't work, so we need the extra parameter of BasicImageView. */ template class View> - if(imageLevels.empty()) { - Error() << "Trade::KtxImageConverter::convertToData(): expected at least 1 mip level"; - return {}; - } - Containers::Array KtxImageConverter::convertLevels(Containers::ArrayView> imageLevels) { const auto format = imageLevels.front().format(); @@ -821,10 +816,6 @@ Containers::Array KtxImageConverter::convertLevels(Containers::ArrayView size = imageLevels.front().size(); - if(size.product() == 0) { - Error() << "Trade::KtxImageConverter::convertToData(): image for level 0 is empty"; - return {}; - } const UnsignedInt numMipmaps = Math::min(imageLevels.size(), Math::log2(size.max()) + 1); if(imageLevels.size() > numMipmaps) @@ -861,12 +852,6 @@ Containers::Array KtxImageConverter::convertLevels(Containers::ArrayView converter = _converterManager.instantiate("KtxImageConverter"); - - std::ostringstream out; - Error redirectError{&out}; - CORRADE_VERIFY(!converter->convertToData(ImageView2D{PixelFormat::RGB8Unorm, {1, 0}})); - CORRADE_COMPARE(out.str(), - "Trade::KtxImageConverter::convertToData(): image for level 0 is empty\n"); -} - -void KtxImageConverterTest::emptyData() { - Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); - - std::ostringstream out; - Error redirectError{&out}; - CORRADE_VERIFY(!converter->convertToData(ImageView2D{PixelFormat::RGB8Unorm, {1, 1}})); - CORRADE_COMPARE(out.str(), - "Trade::KtxImageConverter::convertToData(): image data for level 0 is nullptr\n"); -} - -void KtxImageConverterTest::noLevels() { - Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); - - std::ostringstream out; - Error redirectError{&out}; - CORRADE_VERIFY(!converter->convertToData(Containers::ArrayView{})); - CORRADE_COMPARE(out.str(), - "Trade::KtxImageConverter::convertToData(): expected at least 1 mip level\n"); -} - -void KtxImageConverterTest::tooManyLevels() { - Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); - - const UnsignedByte bytes[4]{}; - - std::ostringstream out; - Warning redirectWarning{&out}; - CORRADE_VERIFY(converter->convertToData({ - ImageView2D{PixelFormat::RGB8Unorm, {1, 1}, bytes}, - ImageView2D{PixelFormat::RGB8Unorm, {1, 1}, bytes} - })); - CORRADE_COMPARE(out.str(), - "Trade::KtxImageConverter::convertToData(): expected at most 1 mip " - "level images but got 2, extra images will be ignored\n"); -} void KtxImageConverterTest::supportedFormat() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); @@ -278,6 +220,22 @@ void KtxImageConverterTest::implementationSpecificFormat() { "Trade::KtxImageConverter::convertToData(): implementation-specific formats are not supported\n"); } +void KtxImageConverterTest::tooManyLevels() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + const UnsignedByte bytes[4]{}; + + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(converter->convertToData({ + ImageView2D{PixelFormat::RGB8Unorm, {1, 1}, bytes}, + ImageView2D{PixelFormat::RGB8Unorm, {1, 1}, bytes} + })); + CORRADE_COMPARE(out.str(), + "Trade::KtxImageConverter::convertToData(): expected at most 1 mip " + "level images but got 2, extra images will be ignored\n"); +} + void KtxImageConverterTest::levelWrongFormat() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); @@ -340,36 +298,6 @@ void KtxImageConverterTest::pvrtcRgb() { CORRADE_COMPARE_AS(image->data(), inputImage.data(), TestSuite::Compare::Container); } -void KtxImageConverterTest::levelZeroSize() { - Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); - - const UnsignedByte data[16]{}; - - std::ostringstream out; - Error redirectError{&out}; - CORRADE_VERIFY(!converter->convertToData({ - ImageView2D{PixelFormat::RGB8Unorm, {2, 2}, data}, - ImageView2D{PixelFormat::RGB8Unorm, {0, 1}, data} - })); - CORRADE_COMPARE(out.str(), - "Trade::KtxImageConverter::convertToData(): expected size Vector(1, 1) for level 1 but got Vector(0, 1)\n"); -} - -void KtxImageConverterTest::levelEmptyData() { - Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); - - const UnsignedByte data[16]{}; - - std::ostringstream out; - Error redirectError{&out}; - CORRADE_VERIFY(!converter->convertToData({ - ImageView2D{PixelFormat::RGB8Unorm, {2, 2}, data}, - ImageView2D{PixelFormat::RGB8Unorm, {1, 1}} - })); - CORRADE_COMPARE(out.str(), - "Trade::KtxImageConverter::convertToData(): image data for level 1 is nullptr\n"); -} - }}}} CORRADE_TEST_MAIN(Magnum::Trade::Test::KtxImageConverterTest) From 5c15bef5bc5909f8bf38dd3d38c66fc4edf8c273 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Sun, 1 Aug 2021 02:17:55 +0200 Subject: [PATCH 43/95] KtxImageConverter: test configuration options --- .../KtxImageConverter/Test/CMakeLists.txt | 4 +- .../Test/KtxImageConverterTest.cpp | 111 +++++++++++++++++- 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt b/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt index 0b5d7378f..14f6bb2fd 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt @@ -43,7 +43,9 @@ file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h corrade_add_test(KtxImageConverterTest KtxImageConverterTest.cpp LIBRARIES Magnum::Trade) -target_include_directories(KtxImageConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) +target_include_directories(KtxImageConverterTest PRIVATE + ${CMAKE_CURRENT_BINARY_DIR}/$ + ${PROJECT_SOURCE_DIR}/src) if(MAGNUM_KTXIMAGECONVERTER_BUILD_STATIC) target_link_libraries(KtxImageConverterTest PRIVATE KtxImageConverter) if(WITH_KTXIMPORTER) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index 92e27deaa..3f087847a 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -30,8 +30,11 @@ #include #include #include +#include +#include #include #include +#include #include #include @@ -42,6 +45,8 @@ #include #include +#include "MagnumPlugins/KtxImporter/KtxHeader.h" + #include "configure.h" namespace Magnum { namespace Trade { namespace Test { namespace { @@ -63,6 +68,12 @@ struct KtxImageConverterTest: TestSuite::Tester { void pvrtcRgb(); + void configurationWriterName(); + void configurationWriterNameEmpty(); + void configurationSwizzle(); + void configurationSwizzleEmpty(); + void configurationSwizzleInvalid(); + /* Explicitly forbid system-wide plugin dependencies */ PluginManager::Manager _converterManager{"nonexistent"}; PluginManager::Manager _importerManager{"nonexistent"}; @@ -79,8 +90,16 @@ const struct { {"4bppSrgb", CompressedPixelFormat::PvrtcRGB4bppSrgb, CompressedPixelFormat::PvrtcRGBA4bppSrgb}, }; -KtxImageConverterTest::KtxImageConverterTest() { +const struct { + const char* name; + const char* value; + const char* message; +} InvalidSwizzleData[]{ + {"too short", "r", "invalid swizzle length, expected 4 but got 1"}, + {"invalid characters", "rxba", "invalid characters in swizzle rxba"} +}; +KtxImageConverterTest::KtxImageConverterTest() { addTests({&KtxImageConverterTest::supportedFormat, &KtxImageConverterTest::supportedCompressedFormat, &KtxImageConverterTest::unsupportedCompressedFormat, @@ -93,6 +112,14 @@ KtxImageConverterTest::KtxImageConverterTest() { addInstancedTests({&KtxImageConverterTest::pvrtcRgb}, Containers::arraySize(PvrtcRgbData)); + addTests({&KtxImageConverterTest::configurationWriterName, + &KtxImageConverterTest::configurationWriterNameEmpty, + &KtxImageConverterTest::configurationSwizzle, + &KtxImageConverterTest::configurationSwizzleEmpty}); + + addInstancedTests({&KtxImageConverterTest::configurationSwizzleInvalid}, + Containers::arraySize(InvalidSwizzleData)); + /* Load the plugin directly from the build tree. Otherwise it's static and already loaded. */ #ifdef KTXIMAGECONVERTER_PLUGIN_FILENAME @@ -104,6 +131,7 @@ KtxImageConverterTest::KtxImageConverterTest() { #endif } +using namespace Containers::Literals; void KtxImageConverterTest::supportedFormat() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); @@ -298,6 +326,87 @@ void KtxImageConverterTest::pvrtcRgb() { CORRADE_COMPARE_AS(image->data(), inputImage.data(), TestSuite::Compare::Container); } +Containers::String readKeyValueData(Containers::ArrayView fileData) { + CORRADE_INTERNAL_ASSERT(fileData.size() >= sizeof(Implementation::KtxHeader)); + const Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); + + const UnsignedInt offset = Utility::Endianness::littleEndian(header.kvdByteOffset); + const UnsignedInt length = Utility::Endianness::littleEndian(header.kvdByteLength); + Containers::String data{ValueInit, length}; + Utility::copy(fileData.suffix(offset).prefix(length), data); + + return data; +} + +void KtxImageConverterTest::configurationWriterName() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + /* Default value */ + CORRADE_COMPARE(converter->configuration().value("writerName"), "Magnum::KtxImageConverter"); + CORRADE_VERIFY(converter->configuration().setValue("writerName", "KtxImageConverterTest&$%1234@\x02\n\r\t\x15!")); + + const UnsignedByte bytes[4]{}; + const auto data = converter->convertToData(ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, bytes}); + CORRADE_VERIFY(data); + + const auto keyValueData = readKeyValueData(data); + CORRADE_VERIFY(keyValueData.contains("KTXwriter\0KtxImageConverterTest&$%1234@\x02\n\r\t\x15!"_s)); +} + +void KtxImageConverterTest::configurationWriterNameEmpty() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + CORRADE_VERIFY(converter->configuration().setValue("writerName", "")); + + const UnsignedByte bytes[4]{}; + const auto data = converter->convertToData(ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, bytes}); + CORRADE_VERIFY(data); + + /* Empty writer name doesn't write the key to the key/value data at all */ + const auto keyValueData = readKeyValueData(data); + CORRADE_VERIFY(!keyValueData.contains("KTXwriter"_s)); +} + +void KtxImageConverterTest::configurationSwizzle() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + /* Default value */ + CORRADE_COMPARE(converter->configuration().value("swizzle"), ""); + CORRADE_VERIFY(converter->configuration().setValue("swizzle", "rgba")); + + const UnsignedByte bytes[4]{}; + const auto data = converter->convertToData(ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, bytes}); + CORRADE_VERIFY(data); + + const auto keyValueData = readKeyValueData(data); + CORRADE_VERIFY(keyValueData.contains("KTXswizzle\0rgba"_s)); +} + +void KtxImageConverterTest::configurationSwizzleEmpty() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + /* Swizzle is empty by default, tested in configurationSwizzle() */ + + const UnsignedByte bytes[4]{}; + const auto data = converter->convertToData(ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, bytes}); + CORRADE_VERIFY(data); + + /* Empty swizzle doesn't write the key to the key/value data at all */ + const auto keyValueData = readKeyValueData(data); + CORRADE_VERIFY(!keyValueData.contains("KTXswizzle"_s)); +} + +void KtxImageConverterTest::configurationSwizzleInvalid() { + auto&& data = InvalidSwizzleData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + CORRADE_VERIFY(converter->configuration().setValue("swizzle", data.value)); + + std::ostringstream out; + Error redirectError{&out}; + + const UnsignedByte bytes[4]{}; + CORRADE_VERIFY(!converter->convertToData(ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, bytes})); + CORRADE_COMPARE(out.str(), Utility::formatString("Trade::KtxImageConverter::convertToData(): {}\n", data.message)); +} + }}}} CORRADE_TEST_MAIN(Magnum::Trade::Test::KtxImageConverterTest) From 601a6a355cab96d3fe608e99fb270a7cd1eeaa2a Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Sun, 1 Aug 2021 02:18:45 +0200 Subject: [PATCH 44/95] KtxImageConverter: test cleanup --- .../Test/KtxImageConverterTest.cpp | 28 ++++++------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index 3f087847a..ecf807e5b 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -81,8 +81,8 @@ struct KtxImageConverterTest: TestSuite::Tester { const struct { const char* name; - CompressedPixelFormat inputFormat; - CompressedPixelFormat outputFormat; + const CompressedPixelFormat inputFormat; + const CompressedPixelFormat outputFormat; } PvrtcRgbData[]{ {"2bppUnorm", CompressedPixelFormat::PvrtcRGB2bppUnorm, CompressedPixelFormat::PvrtcRGBA2bppUnorm}, {"2bppSrgb", CompressedPixelFormat::PvrtcRGB2bppSrgb, CompressedPixelFormat::PvrtcRGBA2bppSrgb}, @@ -139,7 +139,7 @@ void KtxImageConverterTest::supportedFormat() { const UnsignedByte data[32]{}; /* All the formats in PixelFormat are supported */ - /** @todo This needs to be extended when formats are added to PixelFormat */ + /** @todo This needs to be extended when new formats are added to PixelFormat */ constexpr PixelFormat start = PixelFormat::R8Unorm; constexpr PixelFormat end = PixelFormat::Depth32FStencil8UI; @@ -152,7 +152,7 @@ void KtxImageConverterTest::supportedFormat() { const CompressedPixelFormat UnsupportedCompressedFormats[]{ /* Vulkan has no support (core or extension) for 3D ASTC formats. - KTX supports them, but through an unreleased extension. */ + KTX supports them, but through an unreleased extension. */ CompressedPixelFormat::Astc3x3x3RGBAUnorm, CompressedPixelFormat::Astc3x3x3RGBASrgb, CompressedPixelFormat::Astc3x3x3RGBAF, @@ -191,7 +191,7 @@ void KtxImageConverterTest::supportedCompressedFormat() { const UnsignedByte bytes[32]{}; const auto unsupported = Containers::arrayView(UnsupportedCompressedFormats); - /** @todo This needs to be extended when formats are added to CompressedPixelFormat */ + /** @todo This needs to be extended when new formats are added to CompressedPixelFormat */ constexpr CompressedPixelFormat start = CompressedPixelFormat::Bc1RGBUnorm; constexpr CompressedPixelFormat end = CompressedPixelFormat::PvrtcRGBA4bppSrgb; @@ -207,12 +207,11 @@ void KtxImageConverterTest::supportedCompressedFormat() { void KtxImageConverterTest::unsupportedCompressedFormat() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); - std::ostringstream out; - Error redirectError{&out}; - for(CompressedPixelFormat format: UnsupportedCompressedFormats) { CORRADE_ITERATION(format); - out.str({}); /* Reset stream */ + + std::ostringstream out; + Error redirectError{&out}; CORRADE_VERIFY(!converter->convertToData(CompressedImageView2D{format, {1, 1}, {}})); /** @todo Is there no better way to do this? */ @@ -224,15 +223,6 @@ void KtxImageConverterTest::unsupportedCompressedFormat() { } } -/* Actual type needed for ADL, typedef of integer type isn't enough */ -enum CustomPixelFormat : UnsignedInt { - Value -}; - -UnsignedInt pixelSize(CustomPixelFormat) { - return 1u; -} - void KtxImageConverterTest::implementationSpecificFormat() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); @@ -243,7 +233,7 @@ void KtxImageConverterTest::implementationSpecificFormat() { PixelStorage storage; storage.setAlignment(1); - CORRADE_VERIFY(!converter->convertToData(ImageView2D{storage, CustomPixelFormat{}, {1, 1}, bytes})); + CORRADE_VERIFY(!converter->convertToData(ImageView2D{storage, 0, 0, 1, {1, 1}, bytes})); CORRADE_COMPARE(out.str(), "Trade::KtxImageConverter::convertToData(): implementation-specific formats are not supported\n"); } From 8daa1fd0de2a343d493db1bac85eec92fb2624cd Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Sun, 1 Aug 2021 02:20:34 +0200 Subject: [PATCH 45/95] KtxImporter: test cleanup --- .../KtxImporter/Test/KtxImporterTest.cpp | 80 ++++++------------- 1 file changed, 26 insertions(+), 54 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp index e8d95897a..634bfa494 100644 --- a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp +++ b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp @@ -154,7 +154,7 @@ const struct { "file too short, expected 288 bytes for level data but got only 287"} }; -constexpr UnsignedByte VK_FORMAT_S8_UINT = 127; +constexpr UnsignedByte VK_FORMAT_D32_SFLOAT = 126; const struct { const char* name; @@ -197,7 +197,7 @@ const struct { offsetof(Implementation::KtxHeader, supercompressionScheme), 1, "supercompression is currently not supported"}, {"3d depth", "3d.ktx2", - offsetof(Implementation::KtxHeader, vkFormat), VK_FORMAT_S8_UINT, + offsetof(Implementation::KtxHeader, vkFormat), VK_FORMAT_D32_SFLOAT, "3D images can't have depth/stencil format"}, {"level data too short", "2d-rgb.ktx2", sizeof(Implementation::KtxHeader) + offsetof(Implementation::KtxLevel, byteLength), 1, @@ -324,6 +324,19 @@ const struct { /** @todo Check swizzle of larger formats */ }; +void patchKeyValueData(Containers::ArrayView keyValueData, Containers::ArrayView fileData) { + CORRADE_INTERNAL_ASSERT(fileData.size() >= sizeof(Implementation::KtxHeader)); + Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); + Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); + + CORRADE_INTERNAL_ASSERT(header.kvdByteOffset + keyValueData.size() <= fileData.size()); + CORRADE_INTERNAL_ASSERT(header.kvdByteLength >= keyValueData.size()); + header.kvdByteLength = keyValueData.size(); + Utility::copy(keyValueData, fileData.suffix(header.kvdByteOffset).prefix(header.kvdByteLength)); + + Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); +} + KtxImporterTest::KtxImporterTest() { addInstancedTests({&KtxImporterTest::openShort}, Containers::arraySize(ShortData)); @@ -480,13 +493,12 @@ void KtxImporterTest::invalidFormat() { VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG }; - std::ostringstream out; - Error redirectError{&out}; - for(UnsignedInt i = 0; i != Containers::arraySize(formats); ++i) { CORRADE_ITERATION(i); - out.str({}); /* Reset stream content */ header.vkFormat = Utility::Endianness::littleEndian(formats[i]); + + std::ostringstream out; + Error redirectError{&out}; CORRADE_VERIFY(!importer->openData(fileData)); CORRADE_COMPARE(out.str(), Utility::formatString("Trade::KtxImporter::openData(): unsupported format {}\n", UnsignedInt(formats[i]))); } @@ -868,13 +880,7 @@ void KtxImporterTest::imageCubeMapIncomplete() { header.layerCount = Utility::Endianness::littleEndian(6u); header.faceCount = Utility::Endianness::littleEndian(1u); - Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); - - CORRADE_VERIFY(header.kvdByteLength >= keyValueData.size()); - header.kvdByteLength = keyValueData.size(); - Utility::copy(keyValueData, fileData.suffix(header.kvdByteOffset).prefix(header.kvdByteLength)); - - Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); + patchKeyValueData(keyValueData, fileData); } std::ostringstream outWarning; @@ -1093,10 +1099,8 @@ void KtxImporterTest::keyValueDataEmpty() { auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2")); CORRADE_VERIFY(fileData.size() >= sizeof(Implementation::KtxHeader)); - { - Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); - header.kvdByteLength = Utility::Endianness::littleEndian(0u); - } + Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); + header.kvdByteLength = Utility::Endianness::littleEndian(0u); std::ostringstream outWarning; Warning redirectWarning{&outWarning}; @@ -1116,19 +1120,8 @@ void KtxImporterTest::keyValueDataInvalid() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2")); - CORRADE_VERIFY(fileData.size() >= sizeof(Implementation::KtxHeader)); - - { - Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); - - Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); - - CORRADE_VERIFY(header.kvdByteLength >= data.data.size()); - header.kvdByteLength = data.data.size(); - Utility::copy(data.data, fileData.suffix(header.kvdByteOffset).prefix(header.kvdByteLength)); - - Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); - } + + patchKeyValueData(data.data, fileData); std::ostringstream outWarning; Warning redirectWarning{&outWarning}; @@ -1150,19 +1143,8 @@ void KtxImporterTest::keyValueDataInvalidIgnored() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2")); - CORRADE_VERIFY(fileData.size() >= sizeof(Implementation::KtxHeader)); - - { - Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); - - Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); - CORRADE_VERIFY(header.kvdByteLength >= data.data.size()); - header.kvdByteLength = data.data.size(); - Utility::copy(data.data, fileData.suffix(header.kvdByteOffset).prefix(header.kvdByteLength)); - - Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); - } + patchKeyValueData(data.data, fileData); std::ostringstream outWarning; Warning redirectWarning{&outWarning}; @@ -1192,24 +1174,14 @@ void KtxImporterTest::orientationInvalid() { offset += key.size() + 1; Utility::copy(data.orientation, keyValueData.suffix(offset).prefix(data.orientation.size())); - { - Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); - - Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); - - CORRADE_VERIFY(header.kvdByteLength >= keyValueData.size()); - header.kvdByteLength = keyValueData.size(); - Utility::copy(keyValueData, fileData.suffix(header.kvdByteOffset).prefix(header.kvdByteLength)); - - Utility::Endianness::littleEndianInPlace(header.kvdByteOffset, header.kvdByteLength); - } + patchKeyValueData(keyValueData, fileData); std::ostringstream outWarning; Warning redirectWarning{&outWarning}; constexpr Containers::StringView orientations[]{"right"_s, "down"_s, "forward"_s}; const Containers::String orientationString = ", "_s.join(Containers::arrayView(orientations).prefix(data.dimensions)); - + CORRADE_VERIFY(importer->openData(fileData)); CORRADE_COMPARE(outWarning.str(), Utility::formatString("Trade::KtxImporter::openData(): missing or invalid orientation, assuming {}\n", orientationString)); } From 447721b0aaa2211d7e6a8e73c1db9e3aa17e81c7 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Mon, 2 Aug 2021 23:11:06 +0200 Subject: [PATCH 46/95] KtxImageConverter: remove yet another unneeded image check --- .../KtxImageConverter/KtxImageConverter.cpp | 6 ------ .../Test/KtxImageConverterTest.cpp | 18 ------------------ 2 files changed, 24 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index e7074f799..42a3dee26 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -840,12 +840,6 @@ Containers::Array KtxImageConverter::convertLevels(Containers::ArrayView converter = _converterManager.instantiate("KtxImageConverter"); - - const UnsignedByte bytes[16]{}; - - std::ostringstream out; - Error redirectError{&out}; - CORRADE_VERIFY(!converter->convertToData({ - ImageView2D{PixelFormat::RGB8Unorm, {2, 2}, bytes}, - ImageView2D{PixelFormat::RGB8Snorm, {1, 1}, bytes} - })); - CORRADE_COMPARE(out.str(), - "Trade::KtxImageConverter::convertToData(): expected format PixelFormat::RGB8Unorm " - "for level 1 but got PixelFormat::RGB8Snorm\n"); -} - void KtxImageConverterTest::levelWrongSize() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); From 6740ffa7cb96a102676b28218bc82dbbbfd903a8 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Mon, 2 Aug 2021 23:26:48 +0200 Subject: [PATCH 47/95] KtxImageConverter: require default compressed pixel storage Revisit once Magnum supports this through a view on the block data, similar to pixels() --- src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 42a3dee26..cdec6072e 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -690,8 +690,8 @@ void copyPixels(const BasicImageView& image, Containers::ArrayView void copyPixels(const BasicCompressedImageView& image, Containers::ArrayView pixels) { - /** @todo How do we deal with CompressedPixelStorage::skip? - ImageView::pixels handles this for non-compressed images. */ + /** @todo Support CompressedPixelStorage::skip */ + CORRADE_ASSERT(image.storage() == CompressedPixelStorage{}, "Trade::KtxImageConverter::convertToData(): non-default compressed storage is not supported", ); Utility::copy(image.data().prefix(pixels.size()), pixels); } From 04473161018b39b5d3147c5d7cc29ca4f7886a9f Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 13 Aug 2021 00:36:59 +0200 Subject: [PATCH 48/95] KtxImageConverter: write DFD for depth formats --- .../KtxImageConverter/KtxImageConverter.cpp | 386 ++++++++---------- 1 file changed, 159 insertions(+), 227 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index cdec6072e..8b8298366 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -181,8 +181,6 @@ UnsignedByte formatTypeSize(PixelFormat format) { case PixelFormat::Depth24UnormStencil8UI: case PixelFormat::Depth32FStencil8UI: return 4; - default: - break; } CORRADE_ASSERT_UNREACHABLE("componentSize(): unsupported format" << format, {}); @@ -192,9 +190,70 @@ UnsignedByte formatTypeSize(CompressedPixelFormat) { return 1; } -Containers::Pair> samples(CompressedPixelFormat format) { - /* There is no good way to auto-generate these. The KDF spec has a - format.json (https://github.com/KhronosGroup/KTX-Specification/blob/master/formats.json) +struct SampleData { + UnsignedShort bitOffset; + UnsignedShort bitLength; + Implementation::KdfBasicBlockSample::ChannelId channelId; +}; + +Containers::Pair> samples(PixelFormat format) { + constexpr auto ColorModel = Implementation::KdfBasicBlockHeader::ColorModel::Rgbsda; + + /* We later multiply the offset and length by the type size. This works as + long as the channels are all the same size. If PixelFormat ever supports + formats like R10G10B10A2 this needs to be changed. For depth formats + this assumption already doesn't hold, so we have to specialize and + later code needs to make sure to not multiply by the type size. */ + static constexpr SampleData SamplesRgba[]{ + {0, 8, Implementation::KdfBasicBlockSample::ChannelId::Red}, + {8, 8, Implementation::KdfBasicBlockSample::ChannelId::Green}, + {16, 8, Implementation::KdfBasicBlockSample::ChannelId::Blue}, + {24, 8, Implementation::KdfBasicBlockSample::ChannelId::Alpha} + }; + static constexpr SampleData SamplesDepth16Stencil[]{ + {0, 16, Implementation::KdfBasicBlockSample::ChannelId::Depth}, + {16, 8, Implementation::KdfBasicBlockSample::ChannelId::Stencil} + }; + static constexpr SampleData SamplesDepth24Stencil[]{ + {0, 24, Implementation::KdfBasicBlockSample::ChannelId::Depth}, + {24, 8, Implementation::KdfBasicBlockSample::ChannelId::Stencil} + }; + static constexpr SampleData SamplesDepth32Stencil[]{ + {0, 32, Implementation::KdfBasicBlockSample::ChannelId::Depth}, + {32, 8, Implementation::KdfBasicBlockSample::ChannelId::Stencil} + }; + static constexpr SampleData SamplesStencil[]{ + {0, 8, Implementation::KdfBasicBlockSample::ChannelId::Stencil} + }; + + switch(format) { + case PixelFormat::Stencil8UI: + return {ColorModel, SamplesStencil}; + case PixelFormat::Depth16Unorm: + return {ColorModel, Containers::arrayView(SamplesDepth16Stencil).prefix(1)}; + case PixelFormat::Depth16UnormStencil8UI: + return {ColorModel, SamplesDepth16Stencil}; + case PixelFormat::Depth24Unorm: + return {ColorModel, Containers::arrayView(SamplesDepth24Stencil).prefix(1)}; + case PixelFormat::Depth24UnormStencil8UI: + return {ColorModel, SamplesDepth24Stencil}; + case PixelFormat::Depth32F: + return {ColorModel, Containers::arrayView(SamplesDepth32Stencil).prefix(1)}; + case PixelFormat::Depth32FStencil8UI: + return {ColorModel, SamplesDepth32Stencil}; + default: { + const UnsignedInt size = pixelSize(format); + const UnsignedInt typeSize = formatTypeSize(format); + CORRADE_INTERNAL_ASSERT(size%typeSize == 0); + const UnsignedInt numChannels = size/typeSize; + return {ColorModel, Containers::arrayView(SamplesRgba).prefix(numChannels)}; + } + } +} + +Containers::Pair> samples(CompressedPixelFormat format) { + /* There is no good way to auto-generate these from data. The KDF spec has + a format.json (https://github.com/KhronosGroup/KTX-Specification/blob/master/formats.json) but that doesn't contain any information on how to fill the DFD. Then there's Khronos' own dfdutils (https://github.com/KhronosGroup/KTX-Software/tree/master/lib/dfdutils) but that generates headers through Perl scripts, and the headers need @@ -203,117 +262,72 @@ Containers::Pair> 1; - /* BC6h has unsigned floats, but the spec says to use a sampleLower of - -1.0 anyway: - https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#bc6h_channel - So we don't need to distinguish between Ufloat and Sfloat*/ - constexpr UnsignedInt FloatMin = 0xBF800000u; /* -1.0f */ - constexpr UnsignedInt FloatMax = 0x7F800000u; /* 1.0f */ - - /** @todo Remove the upper, lower, ChannelFormat flags and patch it later? - There are a few oddities that need to be treated differently from - the non-compressed DFDs... */ - static constexpr Implementation::KdfBasicBlockSample SamplesBc1[]{ - {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color, - {}, Min, Max} + static constexpr SampleData SamplesBc1[]{ + {0, 64, Implementation::KdfBasicBlockSample::ChannelId::Color} }; - /* The DFD examples in the spec don't set ChannelFormat::Linear in any of - the block-compressed alpha channels */ - static constexpr Implementation::KdfBasicBlockSample SamplesBc1AlphaPunchThrough[]{ - {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Bc1Alpha, - {}, Min, Max} + static constexpr SampleData SamplesBc1AlphaPunchThrough[]{ + {0, 64, Implementation::KdfBasicBlockSample::ChannelId::Bc1Alpha} }; - static constexpr Implementation::KdfBasicBlockSample SamplesBc2And3[]{ - {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Alpha, - {}, Min, Max}, - {64, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color, - {}, Min, Max} + static constexpr SampleData SamplesBc2And3[]{ + {0, 64, Implementation::KdfBasicBlockSample::ChannelId::Alpha}, + {64, 64, Implementation::KdfBasicBlockSample::ChannelId::Color} }; - static constexpr Implementation::KdfBasicBlockSample SamplesBc4[]{ - {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color, - {}, Min, Max} + static constexpr SampleData SamplesBc4[]{ + {0, 64, Implementation::KdfBasicBlockSample::ChannelId::Color} }; - static constexpr Implementation::KdfBasicBlockSample SamplesBc4Signed[]{ - {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color | Implementation::KdfBasicBlockSample::ChannelFormat::Signed, - {}, SignedMin, SignedMax} + static constexpr SampleData SamplesBc4Signed[]{ + {0, 64, Implementation::KdfBasicBlockSample::ChannelId::Color} }; - static constexpr Implementation::KdfBasicBlockSample SamplesBc5[]{ - {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Red, - {}, Min, Max}, - {64, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Green, - {}, Min, Max} + static constexpr SampleData SamplesBc5[]{ + {0, 64, Implementation::KdfBasicBlockSample::ChannelId::Red}, + {64, 64, Implementation::KdfBasicBlockSample::ChannelId::Green} }; - static constexpr Implementation::KdfBasicBlockSample SamplesBc5Signed[]{ - {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Red | Implementation::KdfBasicBlockSample::ChannelFormat::Signed, - {}, SignedMin, SignedMax}, - {64, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Green | Implementation::KdfBasicBlockSample::ChannelFormat::Signed, - {}, SignedMin, SignedMax} + static constexpr SampleData SamplesBc5Signed[]{ + {0, 64, Implementation::KdfBasicBlockSample::ChannelId::Red}, + {64, 64, Implementation::KdfBasicBlockSample::ChannelId::Green} }; - static constexpr Implementation::KdfBasicBlockSample SamplesBc6h[]{ - {0, 128 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color | Implementation::KdfBasicBlockSample::ChannelFormat::Float, - {}, FloatMin, FloatMax} + static constexpr SampleData SamplesBc6h[]{ + {0, 128, Implementation::KdfBasicBlockSample::ChannelId::Color} }; - static constexpr Implementation::KdfBasicBlockSample SamplesBc6hSigned[]{ - {0, 128 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color | Implementation::KdfBasicBlockSample::ChannelFormat::Float | - Implementation::KdfBasicBlockSample::ChannelFormat::Signed, - {}, FloatMin, FloatMax} + static constexpr SampleData SamplesBc6hSigned[]{ + {0, 128, Implementation::KdfBasicBlockSample::ChannelId::Color} }; - static constexpr Implementation::KdfBasicBlockSample SamplesBc7[]{ - {0, 128 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color, - {}, Min, Max} + static constexpr SampleData SamplesBc7[]{ + {0, 128, Implementation::KdfBasicBlockSample::ChannelId::Color} }; - static constexpr Implementation::KdfBasicBlockSample SamplesEacR11[]{ - {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Red, - {}, Min, Max} + static constexpr SampleData SamplesEacR11[]{ + {0, 64, Implementation::KdfBasicBlockSample::ChannelId::Red} }; - static constexpr Implementation::KdfBasicBlockSample SamplesEacR11Signed[]{ - {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Red | Implementation::KdfBasicBlockSample::ChannelFormat::Signed, - {}, SignedMin, SignedMax} + static constexpr SampleData SamplesEacR11Signed[]{ + {0, 64, Implementation::KdfBasicBlockSample::ChannelId::Red} }; - static constexpr Implementation::KdfBasicBlockSample SamplesEacRG11[]{ - {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Red, - {}, Min, Max}, - {64, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Green, - {}, Min, Max} + static constexpr SampleData SamplesEacRG11[]{ + {0, 64, Implementation::KdfBasicBlockSample::ChannelId::Red}, + {64, 64, Implementation::KdfBasicBlockSample::ChannelId::Green} }; - static constexpr Implementation::KdfBasicBlockSample SamplesEacRG11Signed[]{ - {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Red | Implementation::KdfBasicBlockSample::ChannelFormat::Signed, - {}, SignedMin, SignedMax}, - {64, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Green | Implementation::KdfBasicBlockSample::ChannelFormat::Signed, - {}, SignedMin, SignedMax} + static constexpr SampleData SamplesEacRG11Signed[]{ + {0, 64, Implementation::KdfBasicBlockSample::ChannelId::Red}, + {64, 64, Implementation::KdfBasicBlockSample::ChannelId::Green} }; - static constexpr Implementation::KdfBasicBlockSample SamplesEtc2[]{ - {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Etc2Color, - {}, Min, Max} + static constexpr SampleData SamplesEtc2[]{ + {0, 64, Implementation::KdfBasicBlockSample::ChannelId::Etc2Color} }; - static constexpr Implementation::KdfBasicBlockSample SamplesEtc2AlphaPunchThrough[]{ - {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Etc2Color, - {}, Min, Max}, - {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Alpha, - {}, Min, Max} + static constexpr SampleData SamplesEtc2AlphaPunchThrough[]{ + {0, 64, Implementation::KdfBasicBlockSample::ChannelId::Etc2Color}, + {0, 64, Implementation::KdfBasicBlockSample::ChannelId::Alpha} }; - static constexpr Implementation::KdfBasicBlockSample SamplesEtc2Alpha[]{ - {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Alpha, - {}, Min, Max}, - {64, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Etc2Color, - {}, Min, Max} + static constexpr SampleData SamplesEtc2Alpha[]{ + {0, 64, Implementation::KdfBasicBlockSample::ChannelId::Alpha}, + {64, 64, Implementation::KdfBasicBlockSample::ChannelId::Etc2Color} }; - static constexpr Implementation::KdfBasicBlockSample SamplesAstc[]{ - {0, 128 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color, - {}, Min, Max} + static constexpr SampleData SamplesAstc[]{ + {0, 128, Implementation::KdfBasicBlockSample::ChannelId::Color} }; - static constexpr Implementation::KdfBasicBlockSample SamplesAstcHdr[]{ - {0, 128 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color | Implementation::KdfBasicBlockSample::ChannelFormat::Float | - Implementation::KdfBasicBlockSample::ChannelFormat::Signed, - {}, FloatMin, FloatMax} + static constexpr SampleData SamplesAstcHdr[]{ + {0, 128, Implementation::KdfBasicBlockSample::ChannelId::Color} }; - static constexpr Implementation::KdfBasicBlockSample SamplesPvrtc[]{ - {0, 64 - 1, Implementation::KdfBasicBlockSample::ChannelId::Color, - {}, Min, Max} + static constexpr SampleData SamplesPvrtc[]{ + {0, 64, Implementation::KdfBasicBlockSample::ChannelId::Color} }; switch(format) { @@ -454,10 +468,6 @@ Containers::Pair channelMapping(Implementation::VkForm simple version is enough for our needs. Signed integer values are sign-extended. Floats need to be bitcast. */ - - /** @todo If we support custom formats, this might require changes for - typeSize == 8 because these values are only 32-bit. Currently, - Magnum doesn't expose 64-bit formats. */ CORRADE_INTERNAL_ASSERT(typeSize <= 4); const UnsignedInt typeMask = ~0u >> ((4 - typeSize)*8); @@ -483,139 +493,29 @@ Containers::Pair channelMapping(Implementation::VkForm return {Corrade::Utility::bitCast(-1.0f), Corrade::Utility::bitCast(1.0f)}; } - CORRADE_ASSERT_UNREACHABLE("channelLimits(): invalid format suffix" << UnsignedInt(suffix), {}); -} - -Containers::Array fillDataFormatDescriptor(PixelFormat format, Implementation::VkFormatSuffix suffix) { - const UnsignedInt texelSize = pixelSize(format); - const UnsignedInt typeSize = formatTypeSize(format); - const UnsignedInt numChannels = texelSize/typeSize; - - /* Calculate size */ - const std::size_t dfdSamplesSize = numChannels*sizeof(Implementation::KdfBasicBlockSample); - const std::size_t dfdBlockSize = sizeof(Implementation::KdfBasicBlockHeader) + dfdSamplesSize; - const std::size_t dfdSize = sizeof(UnsignedInt) + dfdBlockSize; - CORRADE_INTERNAL_ASSERT(dfdSize % 4 == 0); - - Containers::Array data{ValueInit, dfdSize}; - - std::size_t offset = 0; - - /* Length */ - UnsignedInt& length = *reinterpret_cast(data.suffix(offset).data()); - offset += sizeof(length); - - length = dfdSize; - - /* Block header */ - Implementation::KdfBasicBlockHeader& header = *reinterpret_cast(data.suffix(offset).data()); - offset += sizeof(header); - - header.vendorId = Implementation::KdfBasicBlockHeader::VendorId::Khronos; - header.descriptorType = Implementation::KdfBasicBlockHeader::DescriptorType::Basic; - header.versionNumber = Implementation::KdfBasicBlockHeader::VersionNumber::Kdf1_3; - header.descriptorBlockSize = dfdBlockSize; - - header.colorModel = Implementation::KdfBasicBlockHeader::ColorModel::Rgbsda; - header.colorPrimaries = Implementation::KdfBasicBlockHeader::ColorPrimaries::Srgb; - header.transferFunction = suffix == Implementation::VkFormatSuffix::SRGB - ? Implementation::KdfBasicBlockHeader::TransferFunction::Srgb - : Implementation::KdfBasicBlockHeader::TransferFunction::Linear; - /** @todo Do we ever have premultiplied alpha? */ - header.bytesPlane[0] = texelSize; - - /* Channels */ - const auto samples = Containers::arrayCast(data.suffix(offset)); - offset += dfdSamplesSize; - - const UnsignedByte bitLength = typeSize*8; - - constexpr Implementation::KdfBasicBlockSample::ChannelId UncompressedChannelIds[]{ - Implementation::KdfBasicBlockSample::ChannelId::Red, - Implementation::KdfBasicBlockSample::ChannelId::Green, - Implementation::KdfBasicBlockSample::ChannelId::Blue, - Implementation::KdfBasicBlockSample::ChannelId::Alpha, - Implementation::KdfBasicBlockSample::ChannelId::Depth, - Implementation::KdfBasicBlockSample::ChannelId::Stencil - }; - - /** @todo Special-case depth+stencil formats. Channel count is wrong - for packed formats (only Depth24UnormStencil8UI) and they need - correct stencil offset+length */ - Containers::ArrayView channelIds; - /* - switch(format) { - case PixelFormat::Stencil8UI: - channelIds = {&UncompressedChannelIds[5], 1}; - break; - case PixelFormat::Depth16Unorm: - case PixelFormat::Depth24Unorm: - case PixelFormat::Depth32F: - channelIds = {&UncompressedChannelIds[4], 1}; - break; - case PixelFormat::Depth16UnormStencil8UI: - case PixelFormat::Depth24UnormStencil8UI: - case PixelFormat::Depth32FStencil8UI: - channelIds = {&UncompressedChannelIds[4], 2}; - break; - default: - channelIds = {&UncompressedChannelIds[0], numChannels}; - break; - } - */ - channelIds = {&UncompressedChannelIds[0], numChannels}; - - CORRADE_INTERNAL_ASSERT(channelIds.size() == numChannels); - - const auto mapping = channelMapping(suffix, typeSize); - - UnsignedShort bitOffset = 0; - for(UnsignedInt i = 0; i != numChannels; ++i) { - auto& sample = samples[i]; - sample.bitOffset = bitOffset; - sample.bitLength = bitLength - 1; - const auto channelId = channelIds[i]; - sample.channelType = channelId | channelFormat(suffix); - if(channelId == Implementation::KdfBasicBlockSample::ChannelId::Alpha) - sample.channelType |= Implementation::KdfBasicBlockSample::ChannelFormat::Linear; - sample.lower = mapping.first(); - sample.upper = mapping.second(); - - bitOffset += bitLength; - - Utility::Endianness::littleEndianInPlace(sample.bitOffset, - sample.lower, sample.upper); - } - - Utility::Endianness::littleEndianInPlace(length); - Utility::Endianness::littleEndianInPlace(header.vendorId, header.descriptorType, - header.versionNumber, header.descriptorBlockSize); - - CORRADE_INTERNAL_ASSERT(offset == dfdSize); - - return data; + CORRADE_ASSERT_UNREACHABLE("channelMapping(): invalid format suffix" << UnsignedInt(suffix), {}); } -Containers::Array fillDataFormatDescriptor(CompressedPixelFormat format, Implementation::VkFormatSuffix suffix) { +template +Containers::Array fillDataFormatDescriptor(Format format, Implementation::VkFormatSuffix suffix) { const auto sampleData = samples(format); + CORRADE_INTERNAL_ASSERT(!sampleData.second().empty()); - /* Calculate size */ + /* Calculate total size */ const std::size_t dfdSamplesSize = sampleData.second().size()*sizeof(Implementation::KdfBasicBlockSample); const std::size_t dfdBlockSize = sizeof(Implementation::KdfBasicBlockHeader) + dfdSamplesSize; const std::size_t dfdSize = sizeof(UnsignedInt) + dfdBlockSize; CORRADE_INTERNAL_ASSERT(dfdSize % 4 == 0); Containers::Array data{ValueInit, dfdSize}; - std::size_t offset = 0; - /* Length */ UnsignedInt& length = *reinterpret_cast(data.suffix(offset).data()); offset += sizeof(length); length = dfdSize; - /* Block header */ + /* Basic block header */ Implementation::KdfBasicBlockHeader& header = *reinterpret_cast(data.suffix(offset).data()); offset += sizeof(header); @@ -630,28 +530,60 @@ Containers::Array fillDataFormatDescriptor(CompressedPixelFormat format, I ? Implementation::KdfBasicBlockHeader::TransferFunction::Srgb : Implementation::KdfBasicBlockHeader::TransferFunction::Linear; /** @todo Do we ever have premultiplied alpha? */ - const Vector3i blockSize = compressedBlockSize(format); - for(UnsignedInt i = 0; i != blockSize.Size; ++i) { - if(blockSize[i] > 1) - header.texelBlockDimension[i] = blockSize[i] - 1; + + const Vector3i unitSize = formatUnitSize(format); + const UnsignedInt unitDataSize = formatUnitDataSize(format); + + for(UnsignedInt i = 0; i != unitSize.Size; ++i) { + if(unitSize[i] > 1) + header.texelBlockDimension[i] = unitSize[i] - 1; } - header.bytesPlane[0] = compressedBlockDataSize(format); + header.bytesPlane[0] = unitDataSize; - /* Channels */ + /* Sample blocks, one per channel */ auto samples = Containers::arrayCast(data.suffix(offset)); offset += dfdSamplesSize; - Utility::copy(sampleData.second(), samples); + constexpr bool isCompressedFormat = std::is_same::value; + const bool isDepthStencil = !isCompressedFormat && + sampleData.second().front().channelId != Implementation::KdfBasicBlockSample::ChannelId::Red; + + const UnsignedByte typeSize = formatTypeSize(format); + /* Compressed integer formats must use 32-bit lower/upper */ + /* @todo BC6h has unsigned floats, but the spec says to use a sampleLower + of -1.0. Is this an error? + https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#bc6h_channel + The signed channel format flag is still set, however. */ + const auto lowerUpper = channelMapping(suffix, isCompressedFormat ? sizeof(UnsignedInt) : typeSize); + const UnsignedByte formatFlags = channelFormat(suffix); + const UnsignedByte bitRangeMultiplier = isDepthStencil ? 1 : typeSize; UnsignedShort extent = 0; - for(auto& sample: samples) { + for(UnsignedInt i = 0; i != samples.size(); ++i) { + const auto& sampleContent = sampleData.second()[i]; + auto& sample = samples[i]; + sample.bitOffset = sampleContent.bitOffset*bitRangeMultiplier; + sample.bitLength = sampleContent.bitLength*bitRangeMultiplier - 1; + sample.channelType = sampleContent.channelId | formatFlags; + if(header.transferFunction != Implementation::KdfBasicBlockHeader::TransferFunction::Linear && + sampleContent.channelId == Implementation::KdfBasicBlockSample::ChannelId::Alpha) + { + sample.channelType |= Implementation::KdfBasicBlockSample::ChannelFormat::Linear; + } + + sample.lower = lowerUpper.first(); + sample.upper = lowerUpper.second(); + extent = Math::max(sample.bitOffset + sample.bitLength + 1, extent); + Utility::Endianness::littleEndianInPlace(sample.bitOffset, sample.lower, sample.upper); } - /* Just making sure we didn't make any major mistake in samples() */ - CORRADE_INTERNAL_ASSERT(extent == header.bytesPlane[0]*8); + /* Make sure channel bit ranges returned by samples() are plausible. + Can't use equals because some formats have channels smaller than the + pixel size, e.g. Depth24Unorm. */ + CORRADE_INTERNAL_ASSERT(extent <= unitDataSize*8); Utility::Endianness::littleEndianInPlace(length); Utility::Endianness::littleEndianInPlace(header.vendorId, header.descriptorType, From 6205be4ef87bd863041006493fa801f29a4325df Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Fri, 13 Aug 2021 03:12:15 +0200 Subject: [PATCH 49/95] KtxImporter: test swizzle for depth (ignored), compressed (error) and multi-byte channel formats --- .../KtxImporter/Test/KtxImporterTest.cpp | 65 ++++++++++++++++-- .../Test/bgr-swizzle-bgr-16bit.ktx2 | Bin 0 -> 348 bytes .../KtxImporter/Test/generate.sh | 1 + .../KtxImporter/Test/pattern-16bit.png | Bin 0 -> 136 bytes 4 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 src/MagnumPlugins/KtxImporter/Test/bgr-swizzle-bgr-16bit.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/pattern-16bit.png diff --git a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp index 634bfa494..a33f5fa79 100644 --- a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp +++ b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp @@ -59,8 +59,6 @@ struct KtxImporterTest: TestSuite::Tester { - cube face order to match GL expectations - all formats should be supported - depth/stencil formats - - depth swizzle is ignored - - non-identity swizzle is invalid for compressed formats - orientation flips (+ cubes?) - larger formats */ @@ -103,8 +101,10 @@ struct KtxImporterTest: TestSuite::Tester { void orientationFlipCompressed(); void swizzle(); + void swizzleMultipleBytes(); void swizzleIdentity(); void swizzleUnsupported(); + void swizzleCompressed(); void openTwice(); void importTwice(); @@ -321,7 +321,9 @@ const struct { {"BGRA8 format+header cancel", "swizzle-bgra.ktx2", PixelFormat::RGBA8Srgb, Implementation::VK_FORMAT_B8G8R8A8_SRGB, nullptr, Containers::arrayCast(PatternRgba2DData)}, - /** @todo Check swizzle of larger formats */ + {"depth header ignored", "swizzle-bgra.ktx2", + PixelFormat::Depth32F, VK_FORMAT_D32_SFLOAT, + nullptr, Containers::arrayCast(PatternRgba2DData)} }; void patchKeyValueData(Containers::ArrayView keyValueData, Containers::ArrayView fileData) { @@ -394,8 +396,10 @@ KtxImporterTest::KtxImporterTest() { addInstancedTests({&KtxImporterTest::swizzle}, Containers::arraySize(SwizzleData)); - addTests({&KtxImporterTest::swizzleIdentity, + addTests({&KtxImporterTest::swizzleMultipleBytes, + &KtxImporterTest::swizzleIdentity, &KtxImporterTest::swizzleUnsupported, + &KtxImporterTest::swizzleCompressed, &KtxImporterTest::openTwice, &KtxImporterTest::importTwice}); @@ -1232,7 +1236,8 @@ void KtxImporterTest::swizzle() { CORRADE_VERIFY(importer->openData(fileData)); /** @todo Change origin to top-left for the swizzle test files so we can - check the relevant messages only. Also for swizzleIdentity(). */ + check the relevant messages only. Also for swizzleMultipleBytes() + and swizzleIdentity(). */ std::string expectedMessage = "Trade::KtxImporter::openData(): image will be flipped along y\n"; if(data.message) expectedMessage += Utility::formatString("Trade::KtxImporter::openData(): {}\n", data.message); @@ -1246,6 +1251,41 @@ void KtxImporterTest::swizzle() { CORRADE_COMPARE_AS(image->data(), data.data, TestSuite::Compare::Container); } +void KtxImporterTest::swizzleMultipleBytes() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + importer->addFlags(ImporterFlag::Verbose); + + std::ostringstream outDebug; + Debug redirectDebug{&outDebug}; + + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "bgr-swizzle-bgr-16bit.ktx2"))); + + CORRADE_COMPARE(outDebug.str(), + "Trade::KtxImporter::openData(): image will be flipped along y\n" + "Trade::KtxImporter::openData(): format requires conversion from BGR to RGB\n"); + + /* For some reason a 16-bit PNG sent through toktx ends up with 8-bit + channels duplicated to 16 bits instead of being remapped. Not sure if + this is a bug in GIMP or toktx, although the PNG shows correctly in + several viewers so probably the latter. PVRTexTool does the same thing, + see imageRgb32U(). This is PatternRgbData[0], but each byte extended to + unsigned short by just repeating the byte twice. */ + constexpr UnsignedShort Half = 0x7f7f; + constexpr Math::Color3 content[4*3]{ + {0xffff, 0, 0}, {0xffff, 0xffff, 0xffff}, { 0, 0, 0}, { 0, 0xffff, 0}, + {0xffff, 0xffff, 0xffff}, {0xffff, 0, 0}, { 0, 0, 0}, { 0, 0xffff, 0}, + { 0, 0, 0xffff}, { 0, 0xffff, 0}, {Half, 0, Half}, {Half, 0, Half} + }; + + CORRADE_COMPARE(importer->image2DCount(), 1); + auto image = importer->image2D(0); + CORRADE_VERIFY(image); + CORRADE_COMPARE(image->format(), PixelFormat::RGB16Unorm); + CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); + CORRADE_COMPARE_AS(Containers::arrayCast>(image->data()), + Containers::arrayView(content), TestSuite::Compare::Container); +} + void KtxImporterTest::swizzleIdentity() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); importer->addFlags(ImporterFlag::Verbose); @@ -1274,6 +1314,21 @@ void KtxImporterTest::swizzleUnsupported() { CORRADE_COMPARE(out.str(), "Trade::KtxImporter::openData(): unsupported channel mapping rgb1\n"); } +void KtxImporterTest::swizzleCompressed() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + + auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-compressed-bc1.ktx2")); + + constexpr auto keyValueData = "\x10\x00\x00\x00KTXswizzle\0bgra\0"_s; + patchKeyValueData(keyValueData, fileData); + + std::ostringstream out; + Error redirectError{&out}; + + CORRADE_VERIFY(!importer->openData(fileData)); + CORRADE_COMPARE(out.str(), "Trade::KtxImporter::openData(): unsupported channel mapping bgra\n"); +} + void KtxImporterTest::openTwice() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); diff --git a/src/MagnumPlugins/KtxImporter/Test/bgr-swizzle-bgr-16bit.ktx2 b/src/MagnumPlugins/KtxImporter/Test/bgr-swizzle-bgr-16bit.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..593ee26bd40475688113ec074eb35a4265add3d7 GIT binary patch literal 348 zcmZ9GF$=;l5QSf>I25EsoSd=?wTk*13OYJCxL7P26bms}>0p15tD8Tr-kHXVAH2)m zyBCtjVzm~N=+*TG-U@(AYLi+di$e{IIgpm5Cuu|ar(Xx-dSOq#!?=jyI40CF7FGY- zRTU7xx6grR0iVD1f#)7|^zBT^^sKj9W@o4!d4cgSxh#rPie#_G7#c5EQl|>GYS*@hCRy?C<(6B2fJx9#%Z=T@dm=_LOTEe literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/generate.sh b/src/MagnumPlugins/KtxImporter/Test/generate.sh index 5737dc8ba..99c3a1732 100644 --- a/src/MagnumPlugins/KtxImporter/Test/generate.sh +++ b/src/MagnumPlugins/KtxImporter/Test/generate.sh @@ -18,6 +18,7 @@ toktx --t2 --target_type RGBA --swizzle rgb1 swizzle-unsupported.ktx2 pattern.pn # data should come out normally after the importer swizzles it toktx --t2 --input_swizzle bgra --swizzle bgr1 bgr-swizzle-bgr.ktx2 pattern.png toktx --t2 --target_type RGBA --input_swizzle bgra --swizzle bgra bgra-swizzle-bgra.ktx2 pattern.png +toktx --t2 --input_swizzle bgra --swizzle bgr1 bgr-swizzle-bgr-16bit.ktx2 pattern-16bit.png # swizzled header # with patched swizzled vkFormat data should come out normally because both cancel each other out diff --git a/src/MagnumPlugins/KtxImporter/Test/pattern-16bit.png b/src/MagnumPlugins/KtxImporter/Test/pattern-16bit.png new file mode 100644 index 0000000000000000000000000000000000000000..8d56f89db3f2b72d5596aa9a4e5994826674d626 GIT binary patch literal 136 zcmeAS@N?(olHy`uVBq!ia0vp^EI`aGzyu_-*`8hkQk(@Ik;M!QeEUI|(SkKt1t`c~ z;_2(k{*;49oKLLz=+8qyA$3m|#}JO|zNa=aG8phQ8@zaIcUN|jVw7WpSd_0xVoBka e4-Tbm()LM7!r#t6|7Hi&!r?K(M literal 0 HcmV?d00001 From 4d59a3ae5576e7b7f81fdaea0fe65bd3bf4fd7c4 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Tue, 17 Aug 2021 20:35:23 +0200 Subject: [PATCH 50/95] KtxImageConverter: read orientation from configuration --- .../KtxImageConverter/KtxImageConverter.cpp | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 8b8298366..e235187d7 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -671,7 +671,6 @@ ImageConverterFeatures KtxImageConverter::doFeatures() const { template class View> Containers::Array KtxImageConverter::convertLevels(Containers::ArrayView> imageLevels) { const auto format = imageLevels.front().format(); - if(isFormatImplementationSpecific(format)) { Error{} << "Trade::KtxImageConverter::convertToData(): implementation-specific formats are not supported"; return {}; @@ -690,8 +689,29 @@ Containers::Array KtxImageConverter::convertLevels(Containers::ArrayView KtxImageConverter::convertLevels(Containers::ArrayView keyValueMap[]{ /* Origin left, bottom, back (increasing right, up, out) */ - /** @todo KTX spec says "most other APIs and the majority of texture - compression tools use [r, rd, rdi]". - Should we just always flip image data? Maybe make this - configurable. */ - Containers::pair("KTXorientation"_s, "ruo"_s.prefix(dimensions)), + Containers::pair("KTXorientation"_s, Containers::StringView{orientation}.prefix(Math::min(size_t(dimensions), orientation.size()))), Containers::pair("KTXswizzle"_s, Containers::StringView{swizzle}), Containers::pair("KTXwriter"_s, Containers::StringView{writerName}) }; From 12f5c0965d02c5e3d4856b2fcf0f253e8566b6e1 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Tue, 17 Aug 2021 20:35:49 +0200 Subject: [PATCH 51/95] KtxImageConverter: don't write empty key/value data --- .../KtxImageConverter/KtxImageConverter.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index e235187d7..051c46d28 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -851,11 +851,12 @@ Containers::Array KtxImageConverter::convertLevels(Containers::ArrayView Date: Tue, 17 Aug 2021 20:38:16 +0200 Subject: [PATCH 52/95] KtxImageConverter: test orientation and general key/value data output --- .../Test/KtxImageConverterTest.cpp | 150 ++++++++++++++++-- 1 file changed, 136 insertions(+), 14 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index 34ed95c5f..474ac6ea4 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -67,11 +67,18 @@ struct KtxImageConverterTest: TestSuite::Tester { void pvrtcRgb(); - void configurationWriterName(); - void configurationWriterNameEmpty(); + void configurationOrientation(); + void configurationOrientationLessDimensions(); + void configurationOrientationEmpty(); + void configurationOrientationInvalid(); void configurationSwizzle(); void configurationSwizzleEmpty(); void configurationSwizzleInvalid(); + void configurationWriterName(); + void configurationWriterNameEmpty(); + + void configurationEmpty(); + void configurationSorted(); /* Explicitly forbid system-wide plugin dependencies */ PluginManager::Manager _converterManager{"nonexistent"}; @@ -89,6 +96,16 @@ const struct { {"4bppSrgb", CompressedPixelFormat::PvrtcRGB4bppSrgb, CompressedPixelFormat::PvrtcRGBA4bppSrgb}, }; +const struct { + const char* name; + const char* value; + const char* message; +} InvalidOrientationData[]{ + {"too short", "r", "invalid orientation length, expected at least 3 but got 1"}, + {"invalid character", "xxx", "invalid character in orientation, expected r or l but got x"}, + {"invalid order", "rid", "invalid character in orientation, expected d or u but got i"}, +}; + const struct { const char* name; const char* value; @@ -110,14 +127,24 @@ KtxImageConverterTest::KtxImageConverterTest() { addInstancedTests({&KtxImageConverterTest::pvrtcRgb}, Containers::arraySize(PvrtcRgbData)); - addTests({&KtxImageConverterTest::configurationWriterName, - &KtxImageConverterTest::configurationWriterNameEmpty, - &KtxImageConverterTest::configurationSwizzle, + addTests({&KtxImageConverterTest::configurationOrientation, + &KtxImageConverterTest::configurationOrientationLessDimensions, + &KtxImageConverterTest::configurationOrientationEmpty}); + + addInstancedTests({&KtxImageConverterTest::configurationOrientationInvalid}, + Containers::arraySize(InvalidOrientationData)); + + addTests({&KtxImageConverterTest::configurationSwizzle, &KtxImageConverterTest::configurationSwizzleEmpty}); addInstancedTests({&KtxImageConverterTest::configurationSwizzleInvalid}, Containers::arraySize(InvalidSwizzleData)); + addTests({&KtxImageConverterTest::configurationWriterName, + &KtxImageConverterTest::configurationWriterNameEmpty, + &KtxImageConverterTest::configurationEmpty, + &KtxImageConverterTest::configurationSorted}); + /* Load the plugin directly from the build tree. Otherwise it's static and already loaded. */ #ifdef KTXIMAGECONVERTER_PLUGIN_FILENAME @@ -310,31 +337,59 @@ Containers::String readKeyValueData(Containers::ArrayView fileData) return data; } -void KtxImageConverterTest::configurationWriterName() { +void KtxImageConverterTest::configurationOrientation() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); /* Default value */ - CORRADE_COMPARE(converter->configuration().value("writerName"), "Magnum::KtxImageConverter"); - CORRADE_VERIFY(converter->configuration().setValue("writerName", "KtxImageConverterTest&$%1234@\x02\n\r\t\x15!")); + CORRADE_COMPARE(converter->configuration().value("orientation"), "ruo"); + CORRADE_VERIFY(converter->configuration().setValue("orientation", "ldo")); + + const UnsignedByte bytes[4]{}; + const auto data = converter->convertToData(ImageView3D{PixelFormat::RGBA8Unorm, {1, 1, 1}, bytes}); + CORRADE_VERIFY(data); + + const auto keyValueData = readKeyValueData(data); + CORRADE_VERIFY(keyValueData.contains("KTXorientation\0ldo\0"_s)); +} + +void KtxImageConverterTest::configurationOrientationLessDimensions() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + /* Orientation string is shortened to the number of dimensions, extra characters are ignored */ + CORRADE_VERIFY(converter->configuration().setValue("orientation", "rdxxx")); const UnsignedByte bytes[4]{}; const auto data = converter->convertToData(ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, bytes}); CORRADE_VERIFY(data); const auto keyValueData = readKeyValueData(data); - CORRADE_VERIFY(keyValueData.contains("KTXwriter\0KtxImageConverterTest&$%1234@\x02\n\r\t\x15!"_s)); + CORRADE_VERIFY(keyValueData.contains("KTXorientation\0rd\0"_s)); } -void KtxImageConverterTest::configurationWriterNameEmpty() { +void KtxImageConverterTest::configurationOrientationEmpty() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); - CORRADE_VERIFY(converter->configuration().setValue("writerName", "")); + CORRADE_VERIFY(converter->configuration().setValue("orientation", "")); const UnsignedByte bytes[4]{}; const auto data = converter->convertToData(ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, bytes}); CORRADE_VERIFY(data); - /* Empty writer name doesn't write the key to the key/value data at all */ + /* Empty orientation doesn't write the key to the key/value data at all */ const auto keyValueData = readKeyValueData(data); - CORRADE_VERIFY(!keyValueData.contains("KTXwriter"_s)); + CORRADE_VERIFY(!keyValueData.contains("KTXorientation"_s)); +} + +void KtxImageConverterTest::configurationOrientationInvalid() { + auto&& data = InvalidOrientationData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + CORRADE_VERIFY(converter->configuration().setValue("orientation", data.value)); + + std::ostringstream out; + Error redirectError{&out}; + + const UnsignedByte bytes[4]{}; + CORRADE_VERIFY(!converter->convertToData(ImageView3D{PixelFormat::RGBA8Unorm, {1, 1, 1}, bytes})); + CORRADE_COMPARE(out.str(), Utility::formatString("Trade::KtxImageConverter::convertToData(): {}\n", data.message)); } void KtxImageConverterTest::configurationSwizzle() { @@ -348,7 +403,7 @@ void KtxImageConverterTest::configurationSwizzle() { CORRADE_VERIFY(data); const auto keyValueData = readKeyValueData(data); - CORRADE_VERIFY(keyValueData.contains("KTXswizzle\0rgba"_s)); + CORRADE_VERIFY(keyValueData.contains("KTXswizzle\0rgba\0"_s)); } void KtxImageConverterTest::configurationSwizzleEmpty() { @@ -379,6 +434,73 @@ void KtxImageConverterTest::configurationSwizzleInvalid() { CORRADE_COMPARE(out.str(), Utility::formatString("Trade::KtxImageConverter::convertToData(): {}\n", data.message)); } +void KtxImageConverterTest::configurationWriterName() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + /* Default value */ + CORRADE_COMPARE(converter->configuration().value("writerName"), "Magnum::KtxImageConverter"); + CORRADE_VERIFY(converter->configuration().setValue("writerName", "KtxImageConverterTest&$%1234@\x02\n\r\t\x15!")); + + const UnsignedByte bytes[4]{}; + const auto data = converter->convertToData(ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, bytes}); + CORRADE_VERIFY(data); + + /* Writer doesn't have to be null-terminated, don't test for \0 */ + const auto keyValueData = readKeyValueData(data); + CORRADE_VERIFY(keyValueData.contains("KTXwriter\0KtxImageConverterTest&$%1234@\x02\n\r\t\x15!"_s)); +} + +void KtxImageConverterTest::configurationWriterNameEmpty() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + CORRADE_VERIFY(converter->configuration().setValue("writerName", "")); + + const UnsignedByte bytes[4]{}; + const auto data = converter->convertToData(ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, bytes}); + CORRADE_VERIFY(data); + + /* Empty writer name doesn't write the key to the key/value data at all */ + const auto keyValueData = readKeyValueData(data); + CORRADE_VERIFY(!keyValueData.contains("KTXwriter"_s)); +} + +void KtxImageConverterTest::configurationEmpty() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + CORRADE_VERIFY(converter->configuration().removeValue("writerName")); + CORRADE_VERIFY(converter->configuration().removeValue("swizzle")); + CORRADE_VERIFY(converter->configuration().removeValue("orientation")); + + const UnsignedByte bytes[4]{}; + const auto data = converter->convertToData(ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, bytes}); + CORRADE_VERIFY(data); + + const Implementation::KtxHeader& header = *reinterpret_cast(data.data()); + CORRADE_COMPARE(header.kvdByteOffset, 0); + CORRADE_COMPARE(header.kvdByteLength, 0); +} + +void KtxImageConverterTest::configurationSorted() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + CORRADE_VERIFY(converter->configuration().setValue("writerName", "x")); + CORRADE_VERIFY(converter->configuration().setValue("swizzle", "barg")); + CORRADE_VERIFY(converter->configuration().setValue("orientation", "rd")); + + const UnsignedByte bytes[4]{}; + const auto data = converter->convertToData(ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, bytes}); + CORRADE_VERIFY(data); + + const auto keyValueData = readKeyValueData(data); + const auto writerOffset = keyValueData.find("KTXwriter"_s); + const auto swizzleOffset = keyValueData.find("KTXswizzle"_s); + const auto orientationOffset = keyValueData.find("KTXorientation"_s); + + CORRADE_VERIFY(!writerOffset.isEmpty()); + CORRADE_VERIFY(!swizzleOffset.isEmpty()); + CORRADE_VERIFY(!orientationOffset.isEmpty()); + + /* Entries are sorted alphabetically */ + CORRADE_VERIFY(orientationOffset.begin() < swizzleOffset.begin()); + CORRADE_VERIFY(swizzleOffset.begin() < writerOffset.begin()); +} + }}}} CORRADE_TEST_MAIN(Magnum::Trade::Test::KtxImageConverterTest) From 5e09782e34e4eae1a990451e3fab20b9a82a297e Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Tue, 17 Aug 2021 20:40:30 +0200 Subject: [PATCH 53/95] KtxImageConverter: test handling of pixel storage --- .../Test/KtxImageConverterTest.cpp | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index 474ac6ea4..9d5f13fee 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -62,6 +62,10 @@ struct KtxImageConverterTest: TestSuite::Tester { void unsupportedCompressedFormat(); void implementationSpecificFormat(); + /* Non-default compressed pixel storage is currently not supported. + It's firing an internal assert, so we're not testing that. */ + void pixelStorage(); + void tooManyLevels(); void levelWrongSize(); @@ -121,6 +125,8 @@ KtxImageConverterTest::KtxImageConverterTest() { &KtxImageConverterTest::unsupportedCompressedFormat, &KtxImageConverterTest::implementationSpecificFormat, + &KtxImageConverterTest::pixelStorage, + &KtxImageConverterTest::tooManyLevels, &KtxImageConverterTest::levelWrongSize}); @@ -263,6 +269,35 @@ void KtxImageConverterTest::implementationSpecificFormat() { "Trade::KtxImageConverter::convertToData(): implementation-specific formats are not supported\n"); } + +void KtxImageConverterTest::pixelStorage() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + constexpr UnsignedByte bytes[4*3]{ + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11 + }; + + PixelStorage storage; + storage.setAlignment(4); + storage.setSkip({1, 1, 0}); + + const ImageView2D inputImage{storage, PixelFormat::R8UI, {2, 2}, Containers::arrayView(bytes)}; + const auto output = converter->convertToData(inputImage); + CORRADE_VERIFY(output); + + if(_importerManager.loadState("KtxImporter") == PluginManager::LoadState::NotFound) + CORRADE_SKIP("KtxImporter plugin not found, cannot test"); + + Containers::Pointer importer = _importerManager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openData(output)); + + const auto image = importer->image2D(0); + CORRADE_VERIFY(image); + CORRADE_COMPARE_AS(image->data(), Containers::arrayView({5, 6, 9, 10}), TestSuite::Compare::Container); +} + void KtxImageConverterTest::tooManyLevels() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); From 35401d4ec96f655aebf4adea0b4e9722de7cfd29 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Tue, 17 Aug 2021 20:41:27 +0200 Subject: [PATCH 54/95] KtxImageConverter: test handling of implementation-specific compressed format --- .../Test/KtxImageConverterTest.cpp | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index 9d5f13fee..cba9c0bbf 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -61,6 +61,7 @@ struct KtxImageConverterTest: TestSuite::Tester { void supportedCompressedFormat(); void unsupportedCompressedFormat(); void implementationSpecificFormat(); + void implementationSpecificCompressedFormat(); /* Non-default compressed pixel storage is currently not supported. It's firing an internal assert, so we're not testing that. */ @@ -124,6 +125,7 @@ KtxImageConverterTest::KtxImageConverterTest() { &KtxImageConverterTest::supportedCompressedFormat, &KtxImageConverterTest::unsupportedCompressedFormat, &KtxImageConverterTest::implementationSpecificFormat, + &KtxImageConverterTest::implementationSpecificCompressedFormat, &KtxImageConverterTest::pixelStorage, @@ -213,7 +215,7 @@ const CompressedPixelFormat UnsupportedCompressedFormats[]{ CompressedPixelFormat::Astc6x6x5RGBAF, CompressedPixelFormat::Astc6x6x6RGBAUnorm, CompressedPixelFormat::Astc6x6x6RGBASrgb, - CompressedPixelFormat::Astc6x6x6RGBAF, + CompressedPixelFormat::Astc6x6x6RGBAF }; void KtxImageConverterTest::supportedCompressedFormat() { @@ -238,12 +240,15 @@ void KtxImageConverterTest::supportedCompressedFormat() { void KtxImageConverterTest::unsupportedCompressedFormat() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + const UnsignedByte bytes[32]{}; + for(CompressedPixelFormat format: UnsupportedCompressedFormats) { CORRADE_ITERATION(format); + CORRADE_INTERNAL_ASSERT(Containers::arraySize(bytes) >= compressedBlockDataSize(CompressedPixelFormat(format))); std::ostringstream out; Error redirectError{&out}; - CORRADE_VERIFY(!converter->convertToData(CompressedImageView2D{format, {1, 1}, {}})); + CORRADE_VERIFY(!converter->convertToData(CompressedImageView2D{format, {1, 1}, bytes})); /** @todo Is there no better way to do this? */ std::ostringstream formattedOut; @@ -269,6 +274,19 @@ void KtxImageConverterTest::implementationSpecificFormat() { "Trade::KtxImageConverter::convertToData(): implementation-specific formats are not supported\n"); } +void KtxImageConverterTest::implementationSpecificCompressedFormat() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + const UnsignedByte bytes[]{1}; + + std::ostringstream out; + Error redirectError{&out}; + + CompressedPixelStorage storage; + CORRADE_VERIFY(!converter->convertToData(CompressedImageView2D{storage, 0, {1, 1}, bytes})); + CORRADE_COMPARE(out.str(), + "Trade::KtxImageConverter::convertToData(): implementation-specific formats are not supported\n"); +} void KtxImageConverterTest::pixelStorage() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); @@ -336,11 +354,9 @@ void KtxImageConverterTest::pvrtcRgb() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); const UnsignedByte bytes[16]{}; - const UnsignedInt dataSize = compressedBlockDataSize(data.inputFormat); + const Vector2i imageSize = { 2, 2 }; CORRADE_INTERNAL_ASSERT(Containers::arraySize(bytes) >= dataSize); - - const Vector2i imageSize = {2, 2}; CORRADE_INTERNAL_ASSERT((Vector3i{imageSize, 1}) <= compressedBlockSize(data.inputFormat)); const CompressedImageView2D inputImage{data.inputFormat, imageSize, Containers::arrayView(bytes).prefix(dataSize)}; From f7bf338db1128dc549e26678daf5bf39de9d68c9 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Tue, 17 Aug 2021 20:44:31 +0200 Subject: [PATCH 55/95] KtxImageConverter: forgot the orientation entry in .conf --- .../KtxImageConverter/KtxImageConverter.conf | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.conf b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.conf index f4c7b1354..b27d564fb 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.conf +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.conf @@ -1,9 +1,15 @@ # [configuration_] [configuration] -# Name of the tool writing the image file, saved in the file header -writerName=Magnum::KtxImageConverter +# Orientation string to save in the file header. This doesn't flip the input +# pixels, it only tells readers the orientation during import. Must be empty +# or a string of the form [rl][du][oi] (r/l: right/left, d/u: down/up, +# o/i: out of/into the screen). Only subsets of rdi and ruo are recommended, other +# values may not be supported by all readers. +orientation=ruo # Format swizzle string to save in the file header. This doesn't save swizzled # data, it only tells readers the desired channel mapping during import. Must # be empty or 4 characters long, valid characters are r,g,b,a,0,1. swizzle= +# Name of the tool writing the image file, saved in the file header +writerName=Magnum::KtxImageConverter # [configuration_] From d14cf102e589c873b71ee42093d34f49ff3728a0 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 18 Aug 2021 03:44:10 +0200 Subject: [PATCH 56/95] KtxImporter: test compressed ground truth We just extract the image block data generated by PVRTexTool to .bin files as the golden source. It duplicates some data, but we can now use the .bin files to feed into the image converter tests. --- .../KtxImporter/Test/KtxImporterTest.cpp | 22 ++++++++++++++----- src/MagnumPlugins/KtxImporter/Test/README.md | 4 +++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp index a33f5fa79..ae6afee11 100644 --- a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp +++ b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -228,7 +229,7 @@ const struct { const CompressedPixelFormat format; const Math::Vector<1, Int> size; } CompressedImage1DData[]{ - {"PVRTC", "1d-compressed-bc1.ktx2", CompressedPixelFormat::Bc1RGBASrgb, {4}}, + {"BC1", "1d-compressed-bc1.ktx2", CompressedPixelFormat::Bc1RGBASrgb, {4}}, {"ETC2", "1d-compressed-etc2.ktx2", CompressedPixelFormat::Etc2RGB8Srgb, {7}} }; @@ -674,8 +675,15 @@ void KtxImporterTest::image1DCompressed() { CORRADE_COMPARE(storage.imageHeight(), 0); CORRADE_COMPARE(storage.skip(), Vector3i{}); - /** @todo Can we test ground truth data here? Probably have to generate it - using KtxImageConverter. */ + /* The compressed data is the output of PVRTexTool, nothing hand-crafted. + Use --save-diagnostic to extract them if they're missing or wrong. The + same files are re-used in the tests for KtxImageConverter as input data. */ + const Vector3i blockSize = compressedBlockSize(data.format); + const Vector3i blockCount = (Vector3i::pad(data.size, 1) + (blockSize - Vector3i{1}))/blockSize; + CORRADE_COMPARE(image->data().size(), blockCount.product()*compressedBlockDataSize(data.format)); + CORRADE_COMPARE_AS(std::string(image->data().data(), image->data().size()), + Utility::Directory::join(KTXIMPORTER_TEST_DIR, Utility::Directory::splitExtension(data.file).first + ".bin"), + TestSuite::Compare::StringToFile); } void KtxImporterTest::image2D() { @@ -833,8 +841,12 @@ void KtxImporterTest::image2DCompressed() { CORRADE_COMPARE(storage.imageHeight(), 0); CORRADE_COMPARE(storage.skip(), Vector3i{}); - /** @todo Can we test ground truth data here? Probably have to generate it - using KtxImageConverter. */ + const Vector3i blockSize = compressedBlockSize(data.format); + const Vector3i blockCount = (Vector3i::pad(data.size, 1) + (blockSize - Vector3i{1}))/blockSize; + CORRADE_COMPARE(image->data().size(), blockCount.product()*compressedBlockDataSize(data.format)); + CORRADE_COMPARE_AS(std::string(image->data().data(), image->data().size()), + Utility::Directory::join(KTXIMPORTER_TEST_DIR, Utility::Directory::splitExtension(data.file).first + ".bin"), + TestSuite::Compare::StringToFile); } /* Origin bottom-left. There's some weird color shift happening in the test diff --git a/src/MagnumPlugins/KtxImporter/Test/README.md b/src/MagnumPlugins/KtxImporter/Test/README.md index 4db3ba8ab..60e07fdc3 100644 --- a/src/MagnumPlugins/KtxImporter/Test/README.md +++ b/src/MagnumPlugins/KtxImporter/Test/README.md @@ -1,6 +1,8 @@ Updating test files =================== -The `*.ktx2?` files are created using [Khronos Texture Tools](https://github.com/KhronosGroup/KTX-Software) as well as [PVRTexTool](https://developer.imaginationtech.com/pvrtextool/). +Most of the `*.ktx2?` files are created using [Khronos Texture Tools](https://github.com/KhronosGroup/KTX-Software) as well as [PVRTexTool](https://developer.imaginationtech.com/pvrtextool/). Install both of those and then execute the `generate.sh` script to create the test files. + +The remaining files (various .ktx2 files, .bin files for compressed block data) are generated by running both KtxImageConverterTest and KtxImporterTest (in that order!) with `--save-diagnostic [path/to/this/folder]` until they stop failing. Use a third-party viewer to make sure the .ktx2 files load correctly and look plausible. From 7fb1a671c6566e0410243a6bdce74c5f624a09f1 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 18 Aug 2021 03:46:39 +0200 Subject: [PATCH 57/95] KtxImporter: test more image types A few interesting formats, including depth/stencil, combined mips + layers, combined compressed + mips/layers, 3D compressed --- .../KtxImporter/Test/KtxImporterTest.cpp | 380 ++++++++++++++++-- 1 file changed, 342 insertions(+), 38 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp index ae6afee11..6b78e539d 100644 --- a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp +++ b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp @@ -56,14 +56,6 @@ namespace Magnum { namespace Trade { namespace Test { namespace { struct KtxImporterTest: TestSuite::Tester { explicit KtxImporterTest(); - /** @todo - combined mip + array textures - - cube face order to match GL expectations - - all formats should be supported - - depth/stencil formats - - orientation flips (+ cubes?) - - larger formats - */ - void openShort(); void invalid(); @@ -72,17 +64,25 @@ struct KtxImporterTest: TestSuite::Tester { void texture(); + void imageRgba(); + void imageRgb32U(); + void imageRgb32F(); + void imageDepthStencil(); + void image1D(); void image1DMipmaps(); void image1DLayers(); void image1DCompressed(); + void image1DCompressedMipmaps(); void image2D(); - void image2DRgba(); void image2DMipmaps(); void image2DMipmapsIncomplete(); void image2DLayers(); + void image2DMipmapsAndLayers(); void image2DCompressed(); + void image2DCompressedMipmaps(); + void image2DCompressedLayers(); void imageCubeMapIncomplete(); void imageCubeMap(); @@ -92,13 +92,15 @@ struct KtxImporterTest: TestSuite::Tester { void image3D(); void image3DMipmaps(); void image3DLayers(); + void image3DCompressed(); + /** @todo Compressed 3D image with mipmaps */ void keyValueDataEmpty(); void keyValueDataInvalid(); void keyValueDataInvalidIgnored(); void orientationInvalid(); - void orientationFlip(); + void orientationFlip(); /** @todo */ void orientationFlipCompressed(); void swizzle(); @@ -138,6 +140,32 @@ const Color4ub PatternRgba2DData[3][4]{ {PatternRgbData[0][2][0], PatternRgbData[0][2][1], PatternRgbData[0][2][2], PatternRgbData[0][2][3]} }; +constexpr UnsignedByte PatternStencil8UIData[4*3]{ + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12 +}; + +constexpr UnsignedShort PatternDepth16UnormData[4*3]{ + 0xff01, 0xff02, 0xff03, 0xff04, + 0xff05, 0xff06, 0xff07, 0xff08, + 0xff09, 0xff10, 0xff11, 0xff12 +}; + +constexpr UnsignedInt PatternDepth24UnormStencil8UIData[4*3]{ + 0xffffff01, 0xffffff02, 0xffffff03, 0xffffff04, + 0xffffff05, 0xffffff06, 0xffffff07, 0xffffff08, + 0xffffff09, 0xffffff10, 0xffffff11, 0xffffff12 +}; + +constexpr UnsignedLong HalfL = 0x7f7f7f7f7f7f7f7f; +constexpr UnsignedLong FullL = 0xffffffffffffffff; +constexpr UnsignedLong PatternDepth32FStencil8UIData[4*3]{ + 0, 0, 0, HalfL, + 0, FullL, FullL, HalfL, + 0, FullL, 0, FullL +}; + const struct { const char* name; const std::size_t length; @@ -223,6 +251,18 @@ const struct { {"3D array", "3d-layers.ktx2", Trade::TextureType::Texture3D} }; +const struct { + const char* name; + const char* file; + const PixelFormat format; + const Containers::ArrayView data; +} DepthStencilImageData[]{ + {"Stencil8UI", "2d-s8.ktx2", PixelFormat::Stencil8UI, Containers::arrayCast(PatternStencil8UIData)}, + {"Depth16Unorm", "2d-d16.ktx2", PixelFormat::Depth16Unorm, Containers::arrayCast(PatternDepth16UnormData)}, + {"Depth24UnormStencil8UI", "2d-d24s8.ktx2", PixelFormat::Depth24UnormStencil8UI, Containers::arrayCast(PatternDepth24UnormStencil8UIData)}, + {"Depth32FStencil8UI", "2d-d32fs8.ktx2", PixelFormat::Depth32FStencil8UI, Containers::arrayCast(PatternDepth32FStencil8UIData)} +}; + const struct { const char* name; const char* file; @@ -245,7 +285,8 @@ const struct { {"BC3", "2d-compressed-bc3.ktx2", CompressedPixelFormat::Bc3RGBASrgb, {8, 8}}, {"BC4", "2d-compressed-bc4.ktx2", CompressedPixelFormat::Bc4RUnorm, {8, 8}}, {"BC5", "2d-compressed-bc5.ktx2", CompressedPixelFormat::Bc5RGUnorm, {8, 8}}, - {"ETC2", "2d-compressed-etc2.ktx2", CompressedPixelFormat::Etc2RGB8Srgb, {9, 10}} + {"ETC2", "2d-compressed-etc2.ktx2", CompressedPixelFormat::Etc2RGB8Srgb, {9, 10}}, + {"ASTC", "2d-compressed-astc.ktx2", CompressedPixelFormat::Astc12x10RGBASrgb, {9, 10}} }; using namespace Containers::Literals; @@ -353,6 +394,13 @@ KtxImporterTest::KtxImporterTest() { addInstancedTests({&KtxImporterTest::texture}, Containers::arraySize(TextureData)); + addTests({&KtxImporterTest::imageRgba, + &KtxImporterTest::imageRgb32U, + &KtxImporterTest::imageRgb32F}); + + addInstancedTests({&KtxImporterTest::imageDepthStencil}, + Containers::arraySize(DepthStencilImageData)); + addTests({&KtxImporterTest::image1D, &KtxImporterTest::image1DMipmaps, &KtxImporterTest::image1DLayers}); @@ -360,16 +408,21 @@ KtxImporterTest::KtxImporterTest() { addInstancedTests({&KtxImporterTest::image1DCompressed}, Containers::arraySize(CompressedImage1DData)); - addTests({&KtxImporterTest::image2D, - &KtxImporterTest::image2DRgba, + addTests({&KtxImporterTest::image1DCompressedMipmaps, + + &KtxImporterTest::image2D, &KtxImporterTest::image2DMipmaps, &KtxImporterTest::image2DMipmapsIncomplete, - &KtxImporterTest::image2DLayers}); + &KtxImporterTest::image2DLayers, + &KtxImporterTest::image2DMipmapsAndLayers}); addInstancedTests({&KtxImporterTest::image2DCompressed}, Containers::arraySize(CompressedImage2DData)); - addTests({&KtxImporterTest::imageCubeMapIncomplete, + addTests({&KtxImporterTest::image2DCompressedMipmaps, + &KtxImporterTest::image2DCompressedLayers, + + &KtxImporterTest::imageCubeMapIncomplete, &KtxImporterTest::imageCubeMap, &KtxImporterTest::imageCubeMapLayers, &KtxImporterTest::imageCubeMapMipmaps, @@ -377,6 +430,7 @@ KtxImporterTest::KtxImporterTest() { &KtxImporterTest::image3D, &KtxImporterTest::image3DMipmaps, &KtxImporterTest::image3DLayers, + &KtxImporterTest::image3DCompressed, &KtxImporterTest::keyValueDataEmpty}); @@ -560,6 +614,121 @@ void KtxImporterTest::texture() { CORRADE_COMPARE(counts[dimensions - 1], total); } +void KtxImporterTest::imageRgba() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgba.ktx2"))); + + CORRADE_COMPARE(importer->image2DCount(), 1); + CORRADE_COMPARE(importer->image2DLevelCount(0), 1); + + auto image = importer->image2D(0); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGBA8Srgb); + CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); + + const PixelStorage storage = image->storage(); + CORRADE_COMPARE(storage.alignment(), 4); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(PatternRgba2DData), TestSuite::Compare::Container); +} + +void KtxImporterTest::imageRgb32U() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb32.ktx2"))); + + CORRADE_COMPARE(importer->image2DCount(), 1); + CORRADE_COMPARE(importer->image2DLevelCount(0), 1); + + auto image = importer->image2D(0); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGB32UI); + CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); + + const PixelStorage storage = image->storage(); + CORRADE_COMPARE(storage.alignment(), 4); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + /* Output of PVRTexTool with format conversion. This is PatternRgbData[0], + but each byte extended to uint by just repeating the byte 4 times. */ + constexpr UnsignedInt Half = 0x7f7f7f7f; + constexpr Math::Color3 content[4*3]{ + {~0u, 0, 0}, {~0u, ~0u, ~0u}, { 0, 0, 0}, { 0, ~0u, 0}, + {~0u, ~0u, ~0u}, {~0u, 0, 0}, { 0, 0, 0}, { 0, ~0u, 0}, + { 0, 0, ~0u}, { 0, ~0u, 0}, {Half, 0, Half}, {Half, 0, Half} + }; + + CORRADE_COMPARE_AS(Containers::arrayCast>(image->data()), + Containers::arrayView(content), TestSuite::Compare::Container); +} + +void KtxImporterTest::imageRgb32F() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgbf32.ktx2"))); + + CORRADE_COMPARE(importer->image2DCount(), 1); + CORRADE_COMPARE(importer->image2DLevelCount(0), 1); + + auto image = importer->image2D(0); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGB32F); + CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); + + const PixelStorage storage = image->storage(); + CORRADE_COMPARE(storage.alignment(), 4); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + /* Output of PVRTexTool with format conversion. This is PatternRgbData[0], + but each byte mapped to the range 0.0 - 1.0. */ + constexpr Float Half = 127.0f/255.0f; + constexpr Math::Color3 content[4*3]{ + {1.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, + {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f, 0.0f}, {Half, 0.0f, Half}, {Half, 0.0f, Half} + }; + + CORRADE_COMPARE_AS(Containers::arrayCast>(image->data()), + Containers::arrayView(content), TestSuite::Compare::Container); +} + +void KtxImporterTest::imageDepthStencil() { + auto&& data = DepthStencilImageData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, data.file))); + + CORRADE_COMPARE(importer->image2DCount(), 1); + CORRADE_COMPARE(importer->image2DLevelCount(0), 1); + + auto image = importer->image2D(0); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), data.format); + CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); + + const PixelStorage storage = image->storage(); + CORRADE_COMPARE(storage.alignment(), 4); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + CORRADE_COMPARE_AS(image->data(), data.data, TestSuite::Compare::Container); +} + void KtxImporterTest::image1D() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d.ktx2"))); @@ -686,32 +855,41 @@ void KtxImporterTest::image1DCompressed() { TestSuite::Compare::StringToFile); } -void KtxImporterTest::image2D() { +void KtxImporterTest::image1DCompressedMipmaps() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2"))); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d-compressed-mipmaps.ktx2"))); - CORRADE_COMPARE(importer->image2DCount(), 1); - CORRADE_COMPARE(importer->image2DLevelCount(0), 1); + CORRADE_COMPARE(importer->image1DCount(), 1); + CORRADE_COMPARE(importer->image1DLevelCount(0), 3); - auto image = importer->image2D(0); - CORRADE_VERIFY(image); + Math::Vector<1, Int> mipSize{7}; + for(UnsignedInt i = 0; i != importer->image1DLevelCount(0); ++i) { + CORRADE_ITERATION(i); - CORRADE_VERIFY(!image->isCompressed()); - CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); - CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); + auto image = importer->image1D(0, i); + CORRADE_VERIFY(image); - const PixelStorage storage = image->storage(); - CORRADE_COMPARE(storage.alignment(), 4); - CORRADE_COMPARE(storage.rowLength(), 0); - CORRADE_COMPARE(storage.imageHeight(), 0); - CORRADE_COMPARE(storage.skip(), Vector3i{}); + CORRADE_VERIFY(image->isCompressed()); + CORRADE_COMPARE(image->compressedFormat(), CompressedPixelFormat::Etc2RGB8Srgb); + CORRADE_COMPARE(image->size(), mipSize); - CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(PatternRgbData[0]), TestSuite::Compare::Container); + const Vector3i blockSize = compressedBlockSize(image->compressedFormat()); + const Vector3i blockCount = (Vector3i::pad(mipSize, 1) + (blockSize - Vector3i{1}))/blockSize; + CORRADE_COMPARE(image->data().size(), blockCount.product()*compressedBlockDataSize(image->compressedFormat())); + /* This is suboptimal because when generating ground-truth data with + --save-diagnostic the test needs to be run 4 times to save all mips. + But hopefully this won't really be necessary. */ + CORRADE_COMPARE_AS(std::string(image->data().data(), image->data().size()), + Utility::Directory::join(KTXIMPORTER_TEST_DIR, Utility::formatString("1d-compressed-mipmaps-mip{}.bin", i)), + TestSuite::Compare::StringToFile); + + mipSize = Math::max(mipSize >> 1, 1); + } } -void KtxImporterTest::image2DRgba() { +void KtxImporterTest::image2D() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgba.ktx2"))); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2"))); CORRADE_COMPARE(importer->image2DCount(), 1); CORRADE_COMPARE(importer->image2DLevelCount(0), 1); @@ -720,7 +898,7 @@ void KtxImporterTest::image2DRgba() { CORRADE_VERIFY(image); CORRADE_VERIFY(!image->isCompressed()); - CORRADE_COMPARE(image->format(), PixelFormat::RGBA8Srgb); + CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); const PixelStorage storage = image->storage(); @@ -729,7 +907,7 @@ void KtxImporterTest::image2DRgba() { CORRADE_COMPARE(storage.imageHeight(), 0); CORRADE_COMPARE(storage.skip(), Vector3i{}); - CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(PatternRgba2DData), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(PatternRgbData[0]), TestSuite::Compare::Container); } void KtxImporterTest::image2DMipmaps() { @@ -819,6 +997,54 @@ void KtxImporterTest::image2DLayers() { CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(PatternRgbData), TestSuite::Compare::Container); } +void KtxImporterTest::image2DMipmapsAndLayers() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-mipmaps-and-layers.ktx2"))); + + const auto mip0 = Containers::arrayCast(Containers::arrayView(PatternRgbData)); + /* Mip data generated by PVRTexTool since it doesn't allow specifying our + own mip data. toktx doesn't seem to support array textures at all, so + this is our best option. Colors were extracted with an external viewer. */ + const Color3ub mip1[2*1*3]{ + 0x0000ff_rgb, 0x7f007f_rgb, + 0x0000ff_rgb, 0x7f007f_rgb, + 0x000000_rgb, 0x000000_rgb + }; + const Color3ub mip2[1*1*3]{ + 0x0000ff_rgb, + 0x0000ff_rgb, + 0x000000_rgb + }; + const Containers::ArrayView mipViews[3]{mip0, mip1, mip2}; + + CORRADE_COMPARE(importer->image3DCount(), 1); + CORRADE_COMPARE(importer->image3DLevelCount(0), Containers::arraySize(mipViews)); + + Vector2i mipSize{4, 3}; + for(UnsignedInt i = 0; i != importer->image3DLevelCount(0); ++i) { + CORRADE_ITERATION(i); + + auto image = importer->image3D(0, i); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(!image->isCompressed()); + CORRADE_COMPARE(image->format(), PixelFormat::RGB8Srgb); + CORRADE_COMPARE(image->size(), (Vector3i{mipSize, 3})); + + const PixelStorage storage = image->storage(); + /* Alignment is 4 when row length is a multiple of 4 */ + const Int alignment = ((mipSize.x()*image->pixelSize())%4 == 0) ? 4 : 1; + CORRADE_COMPARE(storage.alignment(), alignment); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(mipViews[i]), TestSuite::Compare::Container); + + mipSize = Math::max(mipSize >> 1, 1); + } +} + void KtxImporterTest::image2DCompressed() { auto&& data = CompressedImage2DData[testCaseInstanceId()]; setTestCaseDescription(data.name); @@ -849,6 +1075,57 @@ void KtxImporterTest::image2DCompressed() { TestSuite::Compare::StringToFile); } +void KtxImporterTest::image2DCompressedMipmaps() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-compressed-mipmaps.ktx2"))); + + CORRADE_COMPARE(importer->image2DCount(), 1); + CORRADE_COMPARE(importer->image2DLevelCount(0), 4); + + Vector2i mipSize{9, 10}; + for(UnsignedInt i = 0; i != importer->image2DLevelCount(0); ++i) { + CORRADE_ITERATION(i); + + auto image = importer->image2D(0, i); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(image->isCompressed()); + CORRADE_COMPARE(image->compressedFormat(), CompressedPixelFormat::Etc2RGB8Srgb); + CORRADE_COMPARE(image->size(), mipSize); + + const Vector3i blockSize = compressedBlockSize(image->compressedFormat()); + const Vector3i blockCount = (Vector3i::pad(mipSize, 1) + (blockSize - Vector3i{1}))/blockSize; + CORRADE_COMPARE(image->data().size(), blockCount.product()*compressedBlockDataSize(image->compressedFormat())); + CORRADE_COMPARE_AS(std::string(image->data().data(), image->data().size()), + Utility::Directory::join(KTXIMPORTER_TEST_DIR, Utility::formatString("2d-compressed-mipmaps-mip{}.bin", i)), + TestSuite::Compare::StringToFile); + + mipSize = Math::max(mipSize >> 1, 1); + } +} + +void KtxImporterTest::image2DCompressedLayers() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-compressed-layers.ktx2"))); + + CORRADE_COMPARE(importer->image3DCount(), 1); + CORRADE_COMPARE(importer->image3DLevelCount(0), 1); + + auto image = importer->image3D(0); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(image->isCompressed()); + CORRADE_COMPARE(image->compressedFormat(), CompressedPixelFormat::Etc2RGB8Srgb); + CORRADE_COMPARE(image->size(), (Vector3i{9, 10, 2})); + + const Vector3i blockSize = compressedBlockSize(image->compressedFormat()); + const Vector3i blockCount = (Vector3i::pad(image->size(), 1) + (blockSize - Vector3i{1}))/blockSize; + CORRADE_COMPARE(image->data().size(), blockCount.product()*compressedBlockDataSize(image->compressedFormat())); + CORRADE_COMPARE_AS(std::string(image->data().data(), image->data().size()), + Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-compressed-layers.bin"), + TestSuite::Compare::StringToFile); +} + /* Origin bottom-left. There's some weird color shift happening in the test files, probably the sampling in PVRTexTool. Non-white pixels in the original files are multiples of 0x101010. */ @@ -1036,14 +1313,11 @@ void KtxImporterTest::image3D() { } void KtxImporterTest::image3DMipmaps() { - /** @todo */ - CORRADE_SKIP("Need a valid test file"); - Containers::Pointer importer = _manager.instantiate("KtxImporter"); CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d-mipmaps.ktx2"))); const auto mip0 = Containers::arrayCast(Containers::arrayView(PatternRgbData)); - const Color3ub mip1[2]{0x000000_rgb, 0x000000_rgb}; + const Color3ub mip1[2]{0xffffff_rgb, 0x007f7f_rgb}; const Color3ub mip2[1]{0x000000_rgb}; const Containers::ArrayView mipViews[3]{mip0, mip1, mip2}; @@ -1110,6 +1384,36 @@ void KtxImporterTest::image3DLayers() { } } +void KtxImporterTest::image3DCompressed() { + Containers::Pointer importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d-compressed.ktx2"))); + + CORRADE_COMPARE(importer->image3DCount(), 1); + CORRADE_COMPARE(importer->image3DLevelCount(0), 1); + + auto image = importer->image3D(0); + CORRADE_VERIFY(image); + + constexpr CompressedPixelFormat format = CompressedPixelFormat::Etc2RGB8Srgb; + constexpr Vector3i size{9, 10, 3}; + + CORRADE_VERIFY(image->isCompressed()); + CORRADE_COMPARE(image->compressedFormat(), format); + CORRADE_COMPARE(image->size(), size); + + const CompressedPixelStorage storage = image->compressedStorage(); + CORRADE_COMPARE(storage.rowLength(), 0); + CORRADE_COMPARE(storage.imageHeight(), 0); + CORRADE_COMPARE(storage.skip(), Vector3i{}); + + const Vector3i blockSize = compressedBlockSize(format); + const Vector3i blockCount = (size + (blockSize - Vector3i{1}))/blockSize; + CORRADE_COMPARE(image->data().size(), blockCount.product()*compressedBlockDataSize(format)); + CORRADE_COMPARE_AS(std::string(image->data().data(), image->data().size()), + Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d-compressed.bin"), + TestSuite::Compare::StringToFile); +} + void KtxImporterTest::keyValueDataEmpty() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2")); From ac22a89ef35620e657bb691f280903c3016623d1 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 18 Aug 2021 03:51:36 +0200 Subject: [PATCH 58/95] KtxImageConverter: test output Expecting a perfect binary match against output generated by toktx/PVRTexTool where possible. 3D mipmaps and depth/stencil format are generated and saved with --save-diagnostic since no tool can generate these. Not ideal, but there isn't any option that's much better. At least it checks for regressions, and we make sure that the output is correct in an external image viewer. --- .../KtxImageConverter/Test/CMakeLists.txt | 6 + .../Test/KtxImageConverterTest.cpp | 454 +++++++++++++++++- .../KtxImageConverter/Test/configure.h.cmake | 1 + 3 files changed, 457 insertions(+), 4 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt b/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt index 14f6bb2fd..1f6c528a0 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt @@ -24,6 +24,12 @@ # DEALINGS IN THE SOFTWARE. # +if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) + set(KTXIMPORTER_TEST_DIR ".") +else() + set(KTXIMPORTER_TEST_DIR ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test) +endif() + # CMake before 3.8 has broken $ expressions for iOS (see # https://gitlab.kitware.com/cmake/cmake/merge_requests/404) and since Corrade # doesn't support dynamic plugins on iOS, this sorta works around that. Should diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index cba9c0bbf..4da353bee 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -54,9 +56,6 @@ namespace Magnum { namespace Trade { namespace Test { namespace { struct KtxImageConverterTest: TestSuite::Tester { explicit KtxImageConverterTest(); - /** @todo - depth/stencil - */ - void supportedFormat(); void supportedCompressedFormat(); void unsupportedCompressedFormat(); @@ -70,6 +69,31 @@ struct KtxImageConverterTest: TestSuite::Tester { void tooManyLevels(); void levelWrongSize(); + void convert1D(); + void convert1DMipmaps(); + void convert1DCompressed(); + void convert1DCompressedMipmaps(); + + void convert2D(); + void convert2DMipmaps(); + /* Should be enough to only test this for one type */ + void convert2DMipmapsIncomplete(); + void convert2DCompressed(); + void convert2DCompressedMipmaps(); + + void convert3D(); + void convert3DMipmaps(); + void convert3DCompressed(); + /** @todo 3D compressed + mipmaps. We could generate it here, but we can't + verify the output is correct using an external viewer. Revisit + once toktx or PVRTexTool support generating these? + Then again, we're doing it for depth-stencil formats, too... */ + + /** @todo Add tests for cube and layered (and combined) images once the + converter supports those */ + + void convertFormats(); + void pvrtcRgb(); void configurationOrientation(); @@ -90,6 +114,122 @@ struct KtxImageConverterTest: TestSuite::Tester { PluginManager::Manager _importerManager{"nonexistent"}; }; +using namespace Math::Literals; + +/* Origin top-left-back */ +const Color3ub PatternRgbData[3][3][4]{ + /* black.png */ + {{0x000000_rgb, 0x000000_rgb, 0x000000_rgb, 0x000000_rgb}, + {0x000000_rgb, 0x000000_rgb, 0x000000_rgb, 0x000000_rgb}, + {0x000000_rgb, 0x000000_rgb, 0x000000_rgb, 0x000000_rgb}}, + /* pattern.png */ + {{0x0000ff_rgb, 0x00ff00_rgb, 0x7f007f_rgb, 0x7f007f_rgb}, + {0xffffff_rgb, 0xff0000_rgb, 0x000000_rgb, 0x00ff00_rgb}, + {0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x00ff00_rgb}}, + /* pattern.png */ + {{0x0000ff_rgb, 0x00ff00_rgb, 0x7f007f_rgb, 0x7f007f_rgb}, + {0xffffff_rgb, 0xff0000_rgb, 0x000000_rgb, 0x00ff00_rgb}, + {0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x00ff00_rgb}} +}; + +/* Output of PVRTexTool with format conversion. This is PatternRgbData[2], + but each byte extended to uint by just repeating the byte 4 times. */ +constexpr UnsignedInt HalfU = 0x7f7f7f7f; +constexpr UnsignedInt FullU = 0xffffffff; +constexpr Math::Color3 PatternRgb32UIData[4*3]{ + { 0, 0, FullU}, { 0, FullU, 0}, {HalfU, 0, HalfU}, {HalfU, 0, HalfU}, + {FullU, FullU, FullU}, {FullU, 0, 0}, { 0, 0, 0}, { 0, FullU, 0}, + {FullU, 0, 0}, {FullU, FullU, FullU}, { 0, 0, 0}, { 0, FullU, 0} +}; + +/* Output of PVRTexTool with format conversion. This is PatternRgbData[2], + but each byte mapped to the range 0.0 - 1.0. */ +constexpr Float HalfF = 127.0f / 255.0f; +constexpr Math::Color3 PatternRgb32FData[4*3]{ + {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f, 0.0f}, {HalfF, 0.0f, HalfF}, {HalfF, 0.0f, HalfF}, + {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f} +}; + +constexpr UnsignedByte PatternStencil8UIData[4*3]{ + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12 +}; + +constexpr UnsignedShort PatternDepth16UnormData[4*3]{ + 0xff01, 0xff02, 0xff03, 0xff04, + 0xff05, 0xff06, 0xff07, 0xff08, + 0xff09, 0xff10, 0xff11, 0xff12 +}; + +constexpr UnsignedInt PatternDepth24UnormStencil8UIData[4*3]{ + 0xffffff01, 0xffffff02, 0xffffff03, 0xffffff04, + 0xffffff05, 0xffffff06, 0xffffff07, 0xffffff08, + 0xffffff09, 0xffffff10, 0xffffff11, 0xffffff12 +}; + +constexpr UnsignedLong HalfL = 0x7f7f7f7f7f7f7f7f; +constexpr UnsignedLong FullL = 0xffffffffffffffff; +constexpr UnsignedLong PatternDepth32FStencil8UIData[4*3]{ + 0, 0, 0, HalfL, + 0, FullL, FullL, HalfL, + 0, FullL, 0, FullL +}; + +const char* WriterToktx = "toktx v4.0.0~6 / libktx v4.0.0~5"; +const char* WriterPVRTexTool = "PVRTexLib v5.1.0"; + +const struct { + const char* name; + const char* file; + const CompressedPixelFormat format; + const Math::Vector<1, Int> size; +} Convert1DCompressedData[]{ + {"BC1", "1d-compressed-bc1.ktx2", CompressedPixelFormat::Bc1RGBASrgb, {4}}, + {"ETC2", "1d-compressed-etc2.ktx2", CompressedPixelFormat::Etc2RGB8Srgb, {7}} +}; + +const struct { + const char* name; + const char* file; + const CompressedPixelFormat format; + const Vector2i size; +} Convert2DCompressedData[]{ + {"PVRTC", "2d-compressed-pvrtc.ktx2", CompressedPixelFormat::PvrtcRGBA4bppSrgb, {8, 8}}, + {"BC1", "2d-compressed-bc1.ktx2", CompressedPixelFormat::Bc1RGBASrgb, {8, 8}}, + {"BC2", "2d-compressed-bc2.ktx2", CompressedPixelFormat::Bc2RGBASrgb, {8, 8}}, + {"BC3", "2d-compressed-bc3.ktx2", CompressedPixelFormat::Bc3RGBASrgb, {8, 8}}, + {"BC4", "2d-compressed-bc4.ktx2", CompressedPixelFormat::Bc4RUnorm, {8, 8}}, + {"BC5", "2d-compressed-bc5.ktx2", CompressedPixelFormat::Bc5RGUnorm, {8, 8}}, + {"ETC2", "2d-compressed-etc2.ktx2", CompressedPixelFormat::Etc2RGB8Srgb, {9, 10}}, + {"ASTC", "2d-compressed-astc.ktx2", CompressedPixelFormat::Astc12x10RGBASrgb, {9, 10}} +}; + +const struct { + const char* name; + const char* file; + const char* orientation; + const char* writer; + const PixelFormat format; + const Containers::ArrayView data; + bool save; +} ConvertFormatsData[]{ + {"RGB32UI", "2d-rgb32.ktx2", "rd", WriterPVRTexTool, PixelFormat::RGB32UI, + Containers::arrayCast(PatternRgb32UIData), false}, + {"RGB32F", "2d-rgbf32.ktx2", "rd", WriterPVRTexTool, PixelFormat::RGB32F, + Containers::arrayCast(PatternRgb32FData), false}, + /* These are saved as test files for KtxImporterTest */ + {"Stencil8UI", "2d-s8.ktx2", nullptr, nullptr, PixelFormat::Stencil8UI, + Containers::arrayCast(PatternStencil8UIData), true}, + {"Depth16Unorm", "2d-d16.ktx2", nullptr, nullptr, PixelFormat::Depth16Unorm, + Containers::arrayCast(PatternDepth16UnormData), true}, + {"Depth24UnormStencil8UI", "2d-d24s8.ktx2", nullptr, nullptr, PixelFormat::Depth24UnormStencil8UI, + Containers::arrayCast(PatternDepth24UnormStencil8UIData), true}, + {"Depth32FStencil8UI", "2d-d32fs8.ktx2", nullptr, nullptr, PixelFormat::Depth32FStencil8UI, + Containers::arrayCast(PatternDepth32FStencil8UIData), true} +}; + const struct { const char* name; const CompressedPixelFormat inputFormat; @@ -130,7 +270,29 @@ KtxImageConverterTest::KtxImageConverterTest() { &KtxImageConverterTest::pixelStorage, &KtxImageConverterTest::tooManyLevels, - &KtxImageConverterTest::levelWrongSize}); + &KtxImageConverterTest::levelWrongSize, + + &KtxImageConverterTest::convert1D, + &KtxImageConverterTest::convert1DMipmaps}); + + addInstancedTests({&KtxImageConverterTest::convert1DCompressed}, + Containers::arraySize(Convert1DCompressedData)); + + addTests({&KtxImageConverterTest::convert1DCompressedMipmaps, + &KtxImageConverterTest::convert2D, + &KtxImageConverterTest::convert2DMipmaps, + &KtxImageConverterTest::convert2DMipmapsIncomplete}); + + addInstancedTests({&KtxImageConverterTest::convert2DCompressed}, + Containers::arraySize(Convert2DCompressedData)); + + addTests({&KtxImageConverterTest::convert2DCompressedMipmaps, + &KtxImageConverterTest::convert3D, + &KtxImageConverterTest::convert3DMipmaps, + &KtxImageConverterTest::convert3DCompressed}); + + addInstancedTests({&KtxImageConverterTest::convertFormats}, + Containers::arraySize(ConvertFormatsData)); addInstancedTests({&KtxImageConverterTest::pvrtcRgb}, Containers::arraySize(PvrtcRgbData)); @@ -347,6 +509,290 @@ void KtxImageConverterTest::levelWrongSize() { "Trade::KtxImageConverter::convertToData(): expected size Vector(1, 1) for level 1 but got Vector(2, 1)\n"); } +void KtxImageConverterTest::convert1D() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + /* toktx writes no orientation for 1D files */ + converter->configuration().removeValue("orientation"); + converter->configuration().setValue("writerName", WriterToktx); + + const Color3ub data[4]{ + 0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x007f7f_rgb + }; + PixelStorage storage; + storage.setAlignment(1); + const ImageView1D inputImage{storage, PixelFormat::RGB8Srgb, {4}, data}; + const auto output = converter->convertToData(inputImage); + CORRADE_VERIFY(output); + + /* Compare against 'ground truth' output generated by toktx/PVRTexTool */ + const auto expected = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d.ktx2")); + CORRADE_COMPARE_AS(output, expected, TestSuite::Compare::Container); +} + +void KtxImageConverterTest::convert1DMipmaps() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + converter->configuration().removeValue("orientation"); + converter->configuration().setValue("writerName", WriterToktx); + + const Math::Vector<1, Int> size{4}; + const Color3ub mip0[4]{0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x007f7f_rgb}; + const Color3ub mip1[2]{0xffffff_rgb, 0x007f7f_rgb}; + const Color3ub mip2[1]{0x000000_rgb}; + + PixelStorage storage; + storage.setAlignment(1); + const ImageView1D inputImages[3]{ + ImageView1D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 0, 1), mip0}, + ImageView1D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 1, 1), mip1}, + ImageView1D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 2, 1), mip2}, + }; + + const auto output = converter->convertToData(inputImages); + CORRADE_VERIFY(output); + + const auto expected = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d-mipmaps.ktx2")); + CORRADE_COMPARE_AS(output, expected, TestSuite::Compare::Container); +} + +void KtxImageConverterTest::convert1DCompressed() { + auto&& data = Convert1DCompressedData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + converter->configuration().setValue("orientation", "r"); + converter->configuration().setValue("writerName", WriterPVRTexTool); + + const auto blockData = Utility::Directory::read( + Utility::Directory::join(KTXIMPORTER_TEST_DIR, Utility::Directory::splitExtension(data.file).first + ".bin")); + const CompressedImageView1D inputImage{data.format, data.size, blockData}; + + const auto output = converter->convertToData(inputImage); + CORRADE_VERIFY(output); + + const auto expected = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, data.file)); + CORRADE_COMPARE_AS(output, expected, TestSuite::Compare::Container); +} + +void KtxImageConverterTest::convert1DCompressedMipmaps() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + converter->configuration().setValue("orientation", "r"); + converter->configuration().setValue("writerName", WriterPVRTexTool); + + const Math::Vector<1, Int> size{7}; + const auto mip0 = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d-compressed-mipmaps-mip0.bin")); + const auto mip1 = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d-compressed-mipmaps-mip1.bin")); + const auto mip2 = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d-compressed-mipmaps-mip2.bin")); + + const CompressedImageView1D inputImages[3]{ + CompressedImageView1D{CompressedPixelFormat::Etc2RGB8Srgb, Math::max(size >> 0, 1), mip0}, + CompressedImageView1D{CompressedPixelFormat::Etc2RGB8Srgb, Math::max(size >> 1, 1), mip1}, + CompressedImageView1D{CompressedPixelFormat::Etc2RGB8Srgb, Math::max(size >> 2, 1), mip2}, + }; + + const auto output = converter->convertToData(inputImages); + CORRADE_VERIFY(output); + + const auto expected = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d-compressed-mipmaps.ktx2")); + CORRADE_COMPARE_AS(output, expected, TestSuite::Compare::Container); +} + +void KtxImageConverterTest::convert2D() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + converter->configuration().setValue("orientation", "rd"); + converter->configuration().setValue("writerName", WriterToktx); + + PixelStorage storage; + storage.setAlignment(1); + const ImageView2D inputImage{storage, PixelFormat::RGB8Srgb, {4, 3}, PatternRgbData[Containers::arraySize(PatternRgbData) - 1]}; + const auto output = converter->convertToData(inputImage); + CORRADE_VERIFY(output); + + const auto expected = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2")); + CORRADE_COMPARE_AS(output, expected, TestSuite::Compare::Container); +} + +void KtxImageConverterTest::convert2DMipmaps() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + converter->configuration().setValue("orientation", "rd"); + converter->configuration().setValue("writerName", WriterToktx); + + const Vector2i size{4, 3}; + const auto mip0 = Containers::arrayCast(Containers::arrayView( + PatternRgbData[Containers::arraySize(PatternRgbData) - 1])); + const Color3ub mip1[2]{0xffffff_rgb, 0x007f7f_rgb}; + const Color3ub mip2[1]{0x000000_rgb}; + + PixelStorage storage; + storage.setAlignment(1); + const ImageView2D inputImages[3]{ + ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 0, 1), mip0}, + ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 1, 1), mip1}, + ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 2, 1), mip2}, + }; + + const auto output = converter->convertToData(inputImages); + CORRADE_VERIFY(output); + + const auto expected = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-mipmaps.ktx2")); + CORRADE_COMPARE_AS(output, expected, TestSuite::Compare::Container); +} + +void KtxImageConverterTest::convert2DMipmapsIncomplete() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + converter->configuration().setValue("orientation", "rd"); + converter->configuration().setValue("writerName", WriterToktx); + + const Vector2i size{4, 3}; + const auto mip0 = Containers::arrayCast(Containers::arrayView( + PatternRgbData[Containers::arraySize(PatternRgbData) - 1])); + const Color3ub mip1[2]{0xffffff_rgb, 0x007f7f_rgb}; + + PixelStorage storage; + storage.setAlignment(1); + const ImageView2D inputImages[2]{ + ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 0, 1), mip0}, + ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 1, 1), mip1} + }; + + const auto output = converter->convertToData(inputImages); + CORRADE_VERIFY(output); + + const auto expected = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-mipmaps-incomplete.ktx2")); + CORRADE_COMPARE_AS(output, expected, TestSuite::Compare::Container); +} + +void KtxImageConverterTest::convert2DCompressed() { + auto&& data = Convert2DCompressedData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + converter->configuration().setValue("orientation", "rd"); + converter->configuration().setValue("writerName", WriterPVRTexTool); + + const auto blockData = Utility::Directory::read( + Utility::Directory::join(KTXIMPORTER_TEST_DIR, Utility::Directory::splitExtension(data.file).first + ".bin")); + const CompressedImageView2D inputImage{data.format, data.size, blockData}; + + const auto output = converter->convertToData(inputImage); + CORRADE_VERIFY(output); + + const auto expected = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, data.file)); + CORRADE_COMPARE_AS(output, expected, TestSuite::Compare::Container); +} + +void KtxImageConverterTest::convert2DCompressedMipmaps() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + converter->configuration().setValue("orientation", "rd"); + converter->configuration().setValue("writerName", WriterToktx); + + const Vector2i size{4, 3}; + const auto mip0 = Containers::arrayCast(Containers::arrayView( + PatternRgbData[Containers::arraySize(PatternRgbData) - 1])); + const Color3ub mip1[2]{0xffffff_rgb, 0x007f7f_rgb}; + const Color3ub mip2[1]{0x000000_rgb}; + + PixelStorage storage; + storage.setAlignment(1); + const ImageView2D inputImages[3]{ + ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 0, 1), mip0}, + ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 1, 1), mip1}, + ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 2, 1), mip2}, + }; + + const auto output = converter->convertToData(inputImages); + CORRADE_VERIFY(output); + + const auto expected = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-mipmaps.ktx2")); + CORRADE_COMPARE_AS(output, expected, TestSuite::Compare::Container); +} + +void KtxImageConverterTest::convert3D() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + converter->configuration().setValue("orientation", "rdi"); + converter->configuration().setValue("writerName", WriterPVRTexTool); + + PixelStorage storage; + storage.setAlignment(1); + const ImageView3D inputImage{storage, PixelFormat::RGB8Srgb, {4, 3, 3}, PatternRgbData}; + const auto output = converter->convertToData(inputImage); + CORRADE_VERIFY(output); + + const auto expected = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d.ktx2")); + CORRADE_COMPARE_AS(output, expected, TestSuite::Compare::Container); +} + +void KtxImageConverterTest::convert3DMipmaps() { + /* Neither toktx nor PVRTexTool can create mipmapped 3D textures. We use + the converter to create our own test file for the importer and the + converter ground truth. At the very least it catches unexpected changes. + Save it by running the test with: + --save-diagnostic [path/to/KtxImporter/Test] */ + + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + converter->configuration().setValue("orientation", "rdi"); + + const Vector3i size{4, 3, 3}; + const auto mip0 = Containers::arrayCast(Containers::arrayView(PatternRgbData)); + const Color3ub mip1[2]{0xffffff_rgb, 0x007f7f_rgb}; + const Color3ub mip2[1]{0x000000_rgb}; + + PixelStorage storage; + storage.setAlignment(1); + const ImageView3D inputImages[3]{ + ImageView3D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 0, 1), mip0}, + ImageView3D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 1, 1), mip1}, + ImageView3D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 2, 1), mip2}, + }; + + const auto output = converter->convertToData(inputImages); + CORRADE_VERIFY(output); + + CORRADE_COMPARE_AS(std::string(output.data(), output.size()), + Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d-mipmaps.ktx2"), + TestSuite::Compare::StringToFile); +} + +void KtxImageConverterTest::convert3DCompressed() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + converter->configuration().setValue("orientation", "rdi"); + converter->configuration().setValue("writerName", WriterPVRTexTool); + + const auto blockData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d-compressed.bin")); + const CompressedImageView3D inputImage{CompressedPixelFormat::Etc2RGB8Srgb, {9, 10, 3}, blockData}; + + const auto output = converter->convertToData(inputImage); + CORRADE_VERIFY(output); + + const auto expected = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d-compressed.ktx2")); + CORRADE_COMPARE_AS(output, expected, TestSuite::Compare::Container); +} + +void KtxImageConverterTest::convertFormats() { + auto&& data = ConvertFormatsData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + if(data.orientation) + converter->configuration().setValue("orientation", data.orientation); + if(data.writer) + converter->configuration().setValue("writerName", data.writer); + + PixelStorage storage; + storage.setAlignment(1); + const ImageView2D inputImage{storage, data.format, {4, 3}, data.data}; + const auto output = converter->convertToData(inputImage); + CORRADE_VERIFY(output); + + if(data.save) { + CORRADE_COMPARE_AS(std::string(output.data(), output.size()), + Utility::Directory::join(KTXIMPORTER_TEST_DIR, data.file), + TestSuite::Compare::StringToFile); + } else { + const auto expected = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, data.file)); + CORRADE_COMPARE_AS(output, expected, TestSuite::Compare::Container); + } +} + void KtxImageConverterTest::pvrtcRgb() { auto&& data = PvrtcRgbData[testCaseInstanceId()]; setTestCaseDescription(data.name); diff --git a/src/MagnumPlugins/KtxImageConverter/Test/configure.h.cmake b/src/MagnumPlugins/KtxImageConverter/Test/configure.h.cmake index 857e9bd47..03c42a37c 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/configure.h.cmake +++ b/src/MagnumPlugins/KtxImageConverter/Test/configure.h.cmake @@ -26,3 +26,4 @@ #cmakedefine KTXIMAGECONVERTER_PLUGIN_FILENAME "${KTXIMAGECONVERTER_PLUGIN_FILENAME}" #cmakedefine KTXIMPORTER_PLUGIN_FILENAME "${KTXIMPORTER_PLUGIN_FILENAME}" +#define KTXIMPORTER_TEST_DIR "${KTXIMPORTER_TEST_DIR}" From 73bae54ffeb72a12c2aab7bebbf1ed1a310c796a Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 18 Aug 2021 17:48:42 +0200 Subject: [PATCH 59/95] KtxImageConverter: fix combined depth+stencil DFD --- .../KtxImageConverter/KtxImageConverter.cpp | 61 +++++++++++++------ 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 051c46d28..839d4bf4d 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -193,7 +193,11 @@ UnsignedByte formatTypeSize(CompressedPixelFormat) { struct SampleData { UnsignedShort bitOffset; UnsignedShort bitLength; - Implementation::KdfBasicBlockSample::ChannelId channelId; + Implementation::KdfBasicBlockSample::ChannelId id; + /* For pixel formats where not all channels share the same suffix (only + combined depth + stencil for now) we have to specify it manually. + Is there a good way to automate this in formatMapping.hpp? */ + Implementation::VkFormatSuffix suffix = {}; }; Containers::Pair> samples(PixelFormat format) { @@ -211,15 +215,18 @@ Containers::Pair fillDataFormatDescriptor(Format format, Implementation:: const auto sampleData = samples(format); CORRADE_INTERNAL_ASSERT(!sampleData.second().empty()); - /* Calculate total size */ + /* Calculate total size. Header + one sample block per channel. */ const std::size_t dfdSamplesSize = sampleData.second().size()*sizeof(Implementation::KdfBasicBlockSample); const std::size_t dfdBlockSize = sizeof(Implementation::KdfBasicBlockHeader) + dfdSamplesSize; const std::size_t dfdSize = sizeof(UnsignedInt) + dfdBlockSize; @@ -534,6 +541,8 @@ Containers::Array fillDataFormatDescriptor(Format format, Implementation:: const Vector3i unitSize = formatUnitSize(format); const UnsignedInt unitDataSize = formatUnitDataSize(format); + /* Value of texelBlockDimension is saved as one less than the actual size. + The intent is to allow 256 but it's a wonderful bug source. */ for(UnsignedInt i = 0; i != unitSize.Size; ++i) { if(unitSize[i] > 1) header.texelBlockDimension[i] = unitSize[i] - 1; @@ -546,34 +555,52 @@ Containers::Array fillDataFormatDescriptor(Format format, Implementation:: constexpr bool isCompressedFormat = std::is_same::value; const bool isDepthStencil = !isCompressedFormat && - sampleData.second().front().channelId != Implementation::KdfBasicBlockSample::ChannelId::Red; + sampleData.second().front().id != Implementation::KdfBasicBlockSample::ChannelId::Red; const UnsignedByte typeSize = formatTypeSize(format); /* Compressed integer formats must use 32-bit lower/upper */ + const UnsignedByte mappingTypeSize = isCompressedFormat ? sizeof(UnsignedInt) : typeSize; /* @todo BC6h has unsigned floats, but the spec says to use a sampleLower of -1.0. Is this an error? - https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#bc6h_channel - The signed channel format flag is still set, however. */ - const auto lowerUpper = channelMapping(suffix, isCompressedFormat ? sizeof(UnsignedInt) : typeSize); + https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#bc6h_channel + The signed channel format flag is still set, however. */ + const auto lowerUpper = channelMapping(suffix, mappingTypeSize); const UnsignedByte formatFlags = channelFormat(suffix); + /* For non-compressed RGBA channels, we get */ const UnsignedByte bitRangeMultiplier = isDepthStencil ? 1 : typeSize; UnsignedShort extent = 0; for(UnsignedInt i = 0; i != samples.size(); ++i) { const auto& sampleContent = sampleData.second()[i]; auto& sample = samples[i]; + /* Value of bitLength is saved as one less than the actual size */ sample.bitOffset = sampleContent.bitOffset*bitRangeMultiplier; sample.bitLength = sampleContent.bitLength*bitRangeMultiplier - 1; - sample.channelType = sampleContent.channelId | formatFlags; + + /* Some channels have custom suffixes, can't use data calculated + from the main suffix */ + UnsignedByte sampleFormatFlags; + Containers::Pair sampleLowerUpper; + if(sampleContent.suffix != Implementation::VkFormatSuffix{}) { + sampleFormatFlags = channelFormat(sampleContent.suffix); + sampleLowerUpper = channelMapping(sampleContent.suffix, mappingTypeSize); + } else { + sampleFormatFlags = formatFlags; + sampleLowerUpper = lowerUpper; + } + + sample.channelType = sampleContent.id | sampleFormatFlags; + sample.lower = sampleLowerUpper.first(); + sample.upper = sampleLowerUpper.second(); + + /* The linear format flag should only be set when the transfer function + is non-linear */ if(header.transferFunction != Implementation::KdfBasicBlockHeader::TransferFunction::Linear && - sampleContent.channelId == Implementation::KdfBasicBlockSample::ChannelId::Alpha) + sampleContent.id == Implementation::KdfBasicBlockSample::ChannelId::Alpha) { sample.channelType |= Implementation::KdfBasicBlockSample::ChannelFormat::Linear; } - sample.lower = lowerUpper.first(); - sample.upper = lowerUpper.second(); - extent = Math::max(sample.bitOffset + sample.bitLength + 1, extent); Utility::Endianness::littleEndianInPlace(sample.bitOffset, From 5687961a8774096053b9a8a5b558a0eabcbcb5f9 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 18 Aug 2021 17:50:03 +0200 Subject: [PATCH 60/95] KtxImageConverter: test multiple conversions --- .../Test/KtxImageConverterTest.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index 4da353bee..44200790b 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -109,6 +109,8 @@ struct KtxImageConverterTest: TestSuite::Tester { void configurationEmpty(); void configurationSorted(); + void convertTwice(); + /* Explicitly forbid system-wide plugin dependencies */ PluginManager::Manager _converterManager{"nonexistent"}; PluginManager::Manager _importerManager{"nonexistent"}; @@ -313,7 +315,9 @@ KtxImageConverterTest::KtxImageConverterTest() { addTests({&KtxImageConverterTest::configurationWriterName, &KtxImageConverterTest::configurationWriterNameEmpty, &KtxImageConverterTest::configurationEmpty, - &KtxImageConverterTest::configurationSorted}); + &KtxImageConverterTest::configurationSorted, + + &KtxImageConverterTest::convertTwice}); /* Load the plugin directly from the build tree. Otherwise it's static and already loaded. */ @@ -998,6 +1002,18 @@ void KtxImageConverterTest::configurationSorted() { CORRADE_VERIFY(swizzleOffset.begin() < writerOffset.begin()); } +void KtxImageConverterTest::convertTwice() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + const UnsignedByte bytes[4]{}; + const auto data1 = converter->convertToData(ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, bytes}); + CORRADE_VERIFY(data1); + const auto data2 = converter->convertToData(ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, bytes}); + + /* Shouldn't crash, output should be identical */ + CORRADE_COMPARE_AS(data1, data2, TestSuite::Compare::Container); +} + }}}} CORRADE_TEST_MAIN(Magnum::Trade::Test::KtxImageConverterTest) From 0c9d53332bfd655c69cb93a860da7d13085188a2 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 18 Aug 2021 17:50:25 +0200 Subject: [PATCH 61/95] KtxImageConverter: test cleanup --- .../Test/KtxImageConverterTest.cpp | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index 44200790b..d3e694bb2 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -259,7 +259,8 @@ const struct { const char* message; } InvalidSwizzleData[]{ {"too short", "r", "invalid swizzle length, expected 4 but got 1"}, - {"invalid characters", "rxba", "invalid characters in swizzle rxba"} + {"invalid characters", "rxba", "invalid characters in swizzle rxba"}, + {"invalid characters", "1012", "invalid characters in swizzle 1012"} }; KtxImageConverterTest::KtxImageConverterTest() { @@ -416,7 +417,7 @@ void KtxImageConverterTest::unsupportedCompressedFormat() { Error redirectError{&out}; CORRADE_VERIFY(!converter->convertToData(CompressedImageView2D{format, {1, 1}, bytes})); - /** @todo Is there no better way to do this? */ + /** @todo Is there a better way to do this? */ std::ostringstream formattedOut; Debug redirectDebug{&formattedOut}; Debug{} << "Trade::KtxImageConverter::convertToData(): unsupported format" << format; @@ -428,7 +429,7 @@ void KtxImageConverterTest::unsupportedCompressedFormat() { void KtxImageConverterTest::implementationSpecificFormat() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); - const UnsignedByte bytes[]{1}; + const UnsignedByte bytes[1]{}; std::ostringstream out; Error redirectError{&out}; @@ -443,7 +444,7 @@ void KtxImageConverterTest::implementationSpecificFormat() { void KtxImageConverterTest::implementationSpecificCompressedFormat() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); - const UnsignedByte bytes[]{1}; + const UnsignedByte bytes[1]{}; std::ostringstream out; Error redirectError{&out}; @@ -548,7 +549,7 @@ void KtxImageConverterTest::convert1DMipmaps() { const ImageView1D inputImages[3]{ ImageView1D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 0, 1), mip0}, ImageView1D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 1, 1), mip1}, - ImageView1D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 2, 1), mip2}, + ImageView1D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 2, 1), mip2} }; const auto output = converter->convertToData(inputImages); @@ -590,7 +591,7 @@ void KtxImageConverterTest::convert1DCompressedMipmaps() { const CompressedImageView1D inputImages[3]{ CompressedImageView1D{CompressedPixelFormat::Etc2RGB8Srgb, Math::max(size >> 0, 1), mip0}, CompressedImageView1D{CompressedPixelFormat::Etc2RGB8Srgb, Math::max(size >> 1, 1), mip1}, - CompressedImageView1D{CompressedPixelFormat::Etc2RGB8Srgb, Math::max(size >> 2, 1), mip2}, + CompressedImageView1D{CompressedPixelFormat::Etc2RGB8Srgb, Math::max(size >> 2, 1), mip2} }; const auto output = converter->convertToData(inputImages); @@ -631,7 +632,7 @@ void KtxImageConverterTest::convert2DMipmaps() { const ImageView2D inputImages[3]{ ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 0, 1), mip0}, ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 1, 1), mip1}, - ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 2, 1), mip2}, + ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 2, 1), mip2} }; const auto output = converter->convertToData(inputImages); @@ -700,7 +701,7 @@ void KtxImageConverterTest::convert2DCompressedMipmaps() { const ImageView2D inputImages[3]{ ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 0, 1), mip0}, ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 1, 1), mip1}, - ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 2, 1), mip2}, + ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 2, 1), mip2} }; const auto output = converter->convertToData(inputImages); @@ -745,7 +746,7 @@ void KtxImageConverterTest::convert3DMipmaps() { const ImageView3D inputImages[3]{ ImageView3D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 0, 1), mip0}, ImageView3D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 1, 1), mip1}, - ImageView3D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 2, 1), mip2}, + ImageView3D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 2, 1), mip2} }; const auto output = converter->convertToData(inputImages); @@ -805,7 +806,7 @@ void KtxImageConverterTest::pvrtcRgb() { const UnsignedByte bytes[16]{}; const UnsignedInt dataSize = compressedBlockDataSize(data.inputFormat); - const Vector2i imageSize = { 2, 2 }; + const Vector2i imageSize = {2, 2}; CORRADE_INTERNAL_ASSERT(Containers::arraySize(bytes) >= dataSize); CORRADE_INTERNAL_ASSERT((Vector3i{imageSize, 1}) <= compressedBlockSize(data.inputFormat)); @@ -873,7 +874,7 @@ void KtxImageConverterTest::configurationOrientationEmpty() { const auto data = converter->convertToData(ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, bytes}); CORRADE_VERIFY(data); - /* Empty orientation doesn't write the key to the key/value data at all */ + /* Empty orientation isn't written to key/value data at all */ const auto keyValueData = readKeyValueData(data); CORRADE_VERIFY(!keyValueData.contains("KTXorientation"_s)); } @@ -915,7 +916,7 @@ void KtxImageConverterTest::configurationSwizzleEmpty() { const auto data = converter->convertToData(ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, bytes}); CORRADE_VERIFY(data); - /* Empty swizzle doesn't write the key to the key/value data at all */ + /* Empty swizzle isn't written to key/value data at all */ const auto keyValueData = readKeyValueData(data); CORRADE_VERIFY(!keyValueData.contains("KTXswizzle"_s)); } @@ -958,7 +959,7 @@ void KtxImageConverterTest::configurationWriterNameEmpty() { const auto data = converter->convertToData(ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, bytes}); CORRADE_VERIFY(data); - /* Empty writer name doesn't write the key to the key/value data at all */ + /* Empty writer name isn't written to key/value data at all */ const auto keyValueData = readKeyValueData(data); CORRADE_VERIFY(!keyValueData.contains("KTXwriter"_s)); } @@ -973,6 +974,8 @@ void KtxImageConverterTest::configurationEmpty() { const auto data = converter->convertToData(ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, bytes}); CORRADE_VERIFY(data); + /* Key/value data should not be written if it only contains empty values */ + const Implementation::KtxHeader& header = *reinterpret_cast(data.data()); CORRADE_COMPARE(header.kvdByteOffset, 0); CORRADE_COMPARE(header.kvdByteLength, 0); From 7a89b005c4d730aafc52c9975d3a7e9478a555f2 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 18 Aug 2021 17:53:18 +0200 Subject: [PATCH 62/95] KtxImporter: test image flip --- .../KtxImporter/Test/KtxImporterTest.cpp | 137 ++++++++++++++---- 1 file changed, 110 insertions(+), 27 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp index 6b78e539d..1c1733094 100644 --- a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp +++ b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp @@ -100,7 +100,7 @@ struct KtxImporterTest: TestSuite::Tester { void keyValueDataInvalidIgnored(); void orientationInvalid(); - void orientationFlip(); /** @todo */ + void orientationFlip(); void orientationFlipCompressed(); void swizzle(); @@ -118,6 +118,15 @@ struct KtxImporterTest: TestSuite::Tester { using namespace Math::Literals; +const Color3ub PatternRgb1DData[3][4]{ + /* pattern-1d.png */ + 0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x007f7f_rgb, + /* pattern-1d.png */ + 0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x007f7f_rgb, + /* black-1d.png */ + 0x000000_rgb, 0x000000_rgb, 0x000000_rgb, 0x000000_rgb +}; + /* Origin bottom-left */ const Color3ub PatternRgbData[3][3][4]{ /* pattern.png */ @@ -257,10 +266,14 @@ const struct { const PixelFormat format; const Containers::ArrayView data; } DepthStencilImageData[]{ - {"Stencil8UI", "2d-s8.ktx2", PixelFormat::Stencil8UI, Containers::arrayCast(PatternStencil8UIData)}, - {"Depth16Unorm", "2d-d16.ktx2", PixelFormat::Depth16Unorm, Containers::arrayCast(PatternDepth16UnormData)}, - {"Depth24UnormStencil8UI", "2d-d24s8.ktx2", PixelFormat::Depth24UnormStencil8UI, Containers::arrayCast(PatternDepth24UnormStencil8UIData)}, - {"Depth32FStencil8UI", "2d-d32fs8.ktx2", PixelFormat::Depth32FStencil8UI, Containers::arrayCast(PatternDepth32FStencil8UIData)} + {"Stencil8UI", "2d-s8.ktx2", PixelFormat::Stencil8UI, + Containers::arrayCast(PatternStencil8UIData)}, + {"Depth16Unorm", "2d-d16.ktx2", PixelFormat::Depth16Unorm, + Containers::arrayCast(PatternDepth16UnormData)}, + {"Depth24UnormStencil8UI", "2d-d24s8.ktx2", PixelFormat::Depth24UnormStencil8UI, + Containers::arrayCast(PatternDepth24UnormStencil8UIData)}, + {"Depth32FStencil8UI", "2d-d32fs8.ktx2", PixelFormat::Depth32FStencil8UI, + Containers::arrayCast(PatternDepth32FStencil8UIData)} }; const struct { @@ -330,11 +343,28 @@ const struct { const struct { const char* name; const char* file; - const UnsignedByte dimensions; + const Vector3i size; + const PixelFormat format; + const Containers::ArrayView data; + const Vector3ub flipped; } FlipData[]{ - {"1D", "1D.ktx2", 1}, - {"2D", "2d-rgb.ktx2", 2}, - {"3D", "3d.ktx2", 3} + /* Don't test everything, just a few common and interesting orientations */ + {"l", "1d.ktx2", {4, 0, 0}, PixelFormat::RGB8Srgb, + Containers::arrayCast(PatternRgb1DData[0]), {true, false, false}}, + {"r", "1d.ktx2", {4, 0, 0}, PixelFormat::RGB8Srgb, + Containers::arrayCast(PatternRgb1DData[0]), {false, false, false}}, + /* Value of flipped is relative to the orientation on disk. Files are rd[i], + the ground truth data expects a flip to ru[o]. */ + {"lu", "2d-rgb.ktx2", {4, 3, 0}, PixelFormat::RGB8Srgb, + Containers::arrayCast(PatternRgbData[0]), {true, true, false}}, + {"rd", "2d-rgb.ktx2", {4, 3, 0}, PixelFormat::RGB8Srgb, + Containers::arrayCast(PatternRgbData[0]), {false, false, false}}, + {"luo", "3d.ktx2", {4, 3, 3}, PixelFormat::RGB8Srgb, + Containers::arrayCast(PatternRgbData), {true, true, true}}, + {"rdo", "3d.ktx2", {4, 3, 3}, PixelFormat::RGB8Srgb, + Containers::arrayCast(PatternRgbData), {false, false, true}}, + {"rdi", "3d.ktx2", {4, 3, 3}, PixelFormat::RGB8Srgb, + Containers::arrayCast(PatternRgbData), {false, false, false}} }; const struct { @@ -368,6 +398,25 @@ const struct { nullptr, Containers::arrayCast(PatternRgba2DData)} }; +Containers::Array createKeyValueData(Containers::StringView key, Containers::ArrayView value, bool terminatingZero = false) { + UnsignedInt size = key.size() + 1 + value.size() + UnsignedInt(terminatingZero); + size = (size + 3)/4*4; + Containers::Array keyValueData{ValueInit, sizeof(UnsignedInt) + size}; + + std::size_t offset = 0; + *reinterpret_cast(keyValueData.data()) = Utility::Endianness::littleEndian(size); + offset += sizeof(size); + Utility::copy(key, keyValueData.suffix(offset).prefix(key.size())); + offset += key.size() + 1; + Utility::copy(value, keyValueData.suffix(offset).prefix(value.size())); + + return keyValueData; +} + +Containers::Array createKeyValueData(Containers::StringView key, Containers::StringView value) { + return createKeyValueData(key, value, true); +} + void patchKeyValueData(Containers::ArrayView keyValueData, Containers::ArrayView fileData) { CORRADE_INTERNAL_ASSERT(fileData.size() >= sizeof(Implementation::KtxHeader)); Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); @@ -749,18 +798,14 @@ void KtxImporterTest::image1D() { CORRADE_COMPARE(storage.imageHeight(), 0); CORRADE_COMPARE(storage.skip(), Vector3i{}); - const Color3ub data[4]{ - 0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x007f7f_rgb - }; - - CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(data), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(PatternRgb1DData[0]), TestSuite::Compare::Container); } void KtxImporterTest::image1DMipmaps() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d-mipmaps.ktx2"))); - const Color3ub mip0[4]{0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x007f7f_rgb}; + const auto mip0 = Containers::arrayView(PatternRgb1DData[0]); const Color3ub mip1[2]{0xffffff_rgb, 0x007f7f_rgb}; const Color3ub mip2[1]{0x000000_rgb}; const Containers::ArrayView mipViews[3]{mip0, mip1, mip2}; @@ -797,12 +842,6 @@ void KtxImporterTest::image1DLayers() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d-layers.ktx2"))); - const Color3ub data[4*3]{ - 0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x007f7f_rgb, - 0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x007f7f_rgb, - 0x000000_rgb, 0x000000_rgb, 0x000000_rgb, 0x000000_rgb - }; - CORRADE_COMPARE(importer->image2DCount(), 1); CORRADE_COMPARE(importer->image2DLevelCount(0), 1); @@ -819,7 +858,7 @@ void KtxImporterTest::image1DLayers() { CORRADE_COMPARE(storage.imageHeight(), 0); CORRADE_COMPARE(storage.skip(), Vector3i{}); - CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(data), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(PatternRgb1DData), TestSuite::Compare::Container); } void KtxImporterTest::image1DCompressed() { @@ -915,7 +954,7 @@ void KtxImporterTest::image2DMipmaps() { CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-mipmaps.ktx2"))); /* Is there a nicer way to get a flat view for a multi-dimensional array? */ - const auto mip0 = Containers::arrayCast(Containers::arrayView(PatternRgbData[0])); + const auto mip0 = Containers::arrayCast(PatternRgbData[0]); const Color3ub mip1[2]{0xffffff_rgb, 0x007f7f_rgb}; const Color3ub mip2[1]{0x000000_rgb}; const Containers::ArrayView mipViews[3]{mip0, mip1, mip2}; @@ -952,7 +991,7 @@ void KtxImporterTest::image2DMipmapsIncomplete() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-mipmaps-incomplete.ktx2"))); - const auto mip0 = Containers::arrayCast(Containers::arrayView(PatternRgbData[0])); + const auto mip0 = Containers::arrayCast(PatternRgbData[0]); const Color3ub mip1[2]{0xffffff_rgb, 0x007f7f_rgb}; const Containers::ArrayView mipViews[2]{mip0, mip1}; @@ -1001,7 +1040,7 @@ void KtxImporterTest::image2DMipmapsAndLayers() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-mipmaps-and-layers.ktx2"))); - const auto mip0 = Containers::arrayCast(Containers::arrayView(PatternRgbData)); + const auto mip0 = Containers::arrayCast(PatternRgbData); /* Mip data generated by PVRTexTool since it doesn't allow specifying our own mip data. toktx doesn't seem to support array textures at all, so this is our best option. Colors were extracted with an external viewer. */ @@ -1511,9 +1550,53 @@ void KtxImporterTest::orientationFlip() { setTestCaseDescription(data.name); Containers::Pointer importer = _manager.instantiate("KtxImporter"); - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2"))); + auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, data.file)); + patchKeyValueData(createKeyValueData("KTXorientation"_s, data.name), fileData); + + CORRADE_VERIFY(importer->openData(fileData)); + + const Vector3i size = Math::max(data.size, 1); + const Int dimensions = Math::min(data.size, 1).sum(); + Containers::Array imageData; + switch(dimensions) { + case 1: { + const auto image = importer->image1D(0); + imageData = Containers::Array{image->data().size()}; + Utility::copy(image->data(), imageData); + break; + } + case 2: { + const auto image = importer->image2D(0); + imageData = Containers::Array{image->data().size()}; + Utility::copy(image->data(), imageData); + break; + } + case 3: { + const auto image = importer->image3D(0); + imageData = Containers::Array{image->data().size()}; + Utility::copy(image->data(), imageData); + break; + } + default: CORRADE_INTERNAL_ASSERT_UNREACHABLE(); + } + + Containers::StridedArrayView4D src{imageData, { + std::size_t(size.z()), + std::size_t(size.y()), + std::size_t(size.x()), + pixelSize(data.format) + }}; + + Containers::Array flippedData{imageData.size()}; + Containers::StridedArrayView4D dst{flippedData, src.size()}; + + if(data.flipped[2]) src = src.flipped<0>(); + if(data.flipped[1]) src = src.flipped<1>(); + if(data.flipped[0]) src = src.flipped<2>(); + + Utility::copy(src, dst); - /** @todo */ + CORRADE_COMPARE_AS(data.data, flippedData, TestSuite::Compare::Container); } void KtxImporterTest::orientationFlipCompressed() { From 0dd268ebf528bca87ed52b81c08918f322a3a4b0 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 18 Aug 2021 17:55:07 +0200 Subject: [PATCH 63/95] KtxImporter: test cleanup --- .../KtxImporter/Test/CMakeLists.txt | 1 + .../KtxImporter/Test/KtxImporterTest.cpp | 135 +++++++++--------- 2 files changed, 68 insertions(+), 68 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt b/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt index 3100f5322..ec03cc699 100644 --- a/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt @@ -4,6 +4,7 @@ # Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, # 2020, 2021 Vladimír Vondruš # Copyright © 2021 Pablo Escobar +# # 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 diff --git a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp index 1c1733094..798428434 100644 --- a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp +++ b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp @@ -1168,25 +1168,44 @@ void KtxImporterTest::image2DCompressedLayers() { /* Origin bottom-left. There's some weird color shift happening in the test files, probably the sampling in PVRTexTool. Non-white pixels in the original files are multiples of 0x101010. */ -const Color3ub FacesRgbData[6][2][2]{ +const Color3ub FacesRgbData[2][6][2][2]{ /* cube+x.png */ - {{0xffffff_rgb, 0x0d0d0d_rgb}, - {0x0d0d0d_rgb, 0x0d0d0d_rgb}}, + {{{0xffffff_rgb, 0x0d0d0d_rgb}, + {0x0d0d0d_rgb, 0x0d0d0d_rgb}}, /* cube-x.png */ - {{0xffffff_rgb, 0x222222_rgb}, - {0x222222_rgb, 0x222222_rgb}}, + {{0xffffff_rgb, 0x222222_rgb}, + {0x222222_rgb, 0x222222_rgb}}, /* cube+y.png */ - {{0xffffff_rgb, 0x323232_rgb}, - {0x323232_rgb, 0x323232_rgb}}, + {{0xffffff_rgb, 0x323232_rgb}, + {0x323232_rgb, 0x323232_rgb}}, /* cube-y.png */ - {{0xffffff_rgb, 0x404040_rgb}, - {0x404040_rgb, 0x404040_rgb}}, + {{0xffffff_rgb, 0x404040_rgb}, + {0x404040_rgb, 0x404040_rgb}}, /* cube+z.png */ - {{0xffffff_rgb, 0x4f4f4f_rgb}, - {0x4f4f4f_rgb, 0x4f4f4f_rgb}}, + {{0xffffff_rgb, 0x4f4f4f_rgb}, + {0x4f4f4f_rgb, 0x4f4f4f_rgb}}, /* cube-z.png */ - {{0xffffff_rgb, 0x606060_rgb}, - {0x606060_rgb, 0x606060_rgb}} + {{0xffffff_rgb, 0x606060_rgb}, + {0x606060_rgb, 0x606060_rgb}}}, + + /* cube+z.png */ + {{{0xffffff_rgb, 0x4f4f4f_rgb}, + {0x4f4f4f_rgb, 0x4f4f4f_rgb}}, + /* cube-z.png */ + {{0xffffff_rgb, 0x606060_rgb}, + {0x606060_rgb, 0x606060_rgb}}, + /* cube+x.png */ + {{0xffffff_rgb, 0x0d0d0d_rgb}, + {0x0d0d0d_rgb, 0x0d0d0d_rgb}}, + /* cube-x.png */ + {{0xffffff_rgb, 0x222222_rgb}, + {0x222222_rgb, 0x222222_rgb}}, + /* cube+y.png */ + {{0xffffff_rgb, 0x323232_rgb}, + {0x323232_rgb, 0x323232_rgb}}, + /* cube-y.png */ + {{0xffffff_rgb, 0x404040_rgb}, + {0x404040_rgb, 0x404040_rgb}}} }; void KtxImporterTest::imageCubeMapIncomplete() { @@ -1194,26 +1213,16 @@ void KtxImporterTest::imageCubeMapIncomplete() { auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "cubemap.ktx2")); CORRADE_VERIFY(fileData.size() >= sizeof(Implementation::KtxHeader)); - constexpr auto key = "KTXcubemapIncomplete"_s; - /* Value is a single byte */ - UnsignedInt size = key.size() + 1 + 1; - size = (size + 3)/4*4; - Containers::Array keyValueData{ValueInit, sizeof(UnsignedInt) + size}; - - std::size_t offset = 0; - *reinterpret_cast(keyValueData.data()) = Utility::Endianness::littleEndian(size); - offset += sizeof(size); - Utility::copy(key, keyValueData.suffix(offset).prefix(key.size())); - offset += key.size() + 1; - keyValueData[offset] = 0x3f; - - { - Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); - header.layerCount = Utility::Endianness::littleEndian(6u); - header.faceCount = Utility::Endianness::littleEndian(1u); + /* All 6 bits set, should still emit a warning because the check only happens + when face count is not 6 */ + const char data[1]{0x3f}; + /* Not a string, so no terminating 0 */ + const auto keyValueData = createKeyValueData("KTXcubemapIncomplete"_s, Containers::arrayView(data)); + patchKeyValueData(keyValueData, fileData); - patchKeyValueData(keyValueData, fileData); - } + Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); + header.layerCount = Utility::Endianness::littleEndian(6u); + header.faceCount = Utility::Endianness::littleEndian(1u); std::ostringstream outWarning; Warning redirectWarning{&outWarning}; @@ -1226,6 +1235,10 @@ void KtxImporterTest::imageCubeMapIncomplete() { CORRADE_COMPARE(importer->image3DCount(), 1); CORRADE_COMPARE(importer->image3DLevelCount(0), 1); + const auto texture = importer->texture(0); + CORRADE_VERIFY(texture); + CORRADE_COMPARE(texture->type(), TextureType::Texture2DArray); + auto image = importer->image3D(0); CORRADE_VERIFY(image); @@ -1239,7 +1252,7 @@ void KtxImporterTest::imageCubeMapIncomplete() { CORRADE_COMPARE(storage.imageHeight(), 0); CORRADE_COMPARE(storage.skip(), Vector3i{}); - CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(FacesRgbData), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(FacesRgbData[0]), TestSuite::Compare::Container); } void KtxImporterTest::imageCubeMap() { @@ -1262,21 +1275,21 @@ void KtxImporterTest::imageCubeMap() { CORRADE_COMPARE(storage.imageHeight(), 0); CORRADE_COMPARE(storage.skip(), Vector3i{}); - CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(FacesRgbData), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(FacesRgbData[0]), TestSuite::Compare::Container); } void KtxImporterTest::imageCubeMapMipmaps() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "cubemap-mipmaps.ktx2"))); - const auto mip0 = Containers::arrayCast(Containers::arrayView(FacesRgbData)); + const auto mip0 = Containers::arrayCast(FacesRgbData[0]); const Color3ub mip1[1*1*6]{ - FacesRgbData[0][1][0], - FacesRgbData[1][1][0], - FacesRgbData[2][1][0], - FacesRgbData[3][1][0], - FacesRgbData[4][1][0], - FacesRgbData[5][1][0] + FacesRgbData[0][0][1][0], + FacesRgbData[0][1][1][0], + FacesRgbData[0][2][1][0], + FacesRgbData[0][3][1][0], + FacesRgbData[0][4][1][0], + FacesRgbData[0][5][1][0] }; const Containers::ArrayView mipViews[2]{mip0, mip1}; @@ -1324,7 +1337,7 @@ void KtxImporterTest::imageCubeMapLayers() { for(UnsignedInt i = 0; i != NumLayers; ++i) { CORRADE_ITERATION(i); - CORRADE_COMPARE_AS(image->data().suffix(i*faceSize).prefix(faceSize), Containers::arrayCast(FacesRgbData), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(image->data().suffix(i*faceSize).prefix(faceSize), Containers::arrayCast(FacesRgbData[i]), TestSuite::Compare::Container); } } @@ -1348,6 +1361,9 @@ void KtxImporterTest::image3D() { CORRADE_COMPARE(storage.imageHeight(), 0); CORRADE_COMPARE(storage.skip(), Vector3i{}); + /* Same expected data as image2DLayers but the input images were created + with reversed slice order to account for the z-flip on import from rdi + to ruo */ CORRADE_COMPARE_AS(image->data(), Containers::arrayCast(PatternRgbData), TestSuite::Compare::Container); } @@ -1355,7 +1371,7 @@ void KtxImporterTest::image3DMipmaps() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d-mipmaps.ktx2"))); - const auto mip0 = Containers::arrayCast(Containers::arrayView(PatternRgbData)); + const auto mip0 = Containers::arrayCast(PatternRgbData); const Color3ub mip1[2]{0xffffff_rgb, 0x007f7f_rgb}; const Color3ub mip2[1]{0x000000_rgb}; const Containers::ArrayView mipViews[3]{mip0, mip1, mip2}; @@ -1393,7 +1409,7 @@ void KtxImporterTest::image3DLayers() { CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d-layers.ktx2"))); const auto layer0 = Containers::arrayCast(PatternRgbData); - /* Pattern, black, black*/ + /* Pattern, black, black */ Color3ub layer1Data[3][3][4]{}; Utility::copy(Containers::arrayView(PatternRgbData[0]), layer1Data[0]); const auto layer1 = Containers::arrayCast(layer1Data); @@ -1497,7 +1513,7 @@ void KtxImporterTest::keyValueDataInvalidIgnored() { auto&& data = IgnoredInvalidKeyValueData[testCaseInstanceId()]; setTestCaseDescription(data.name); - /* "Invalid" (according to the spec) key/value data that can just be + /* Invalid (according to the spec) key/value data that can just be ignored without warning because it doesn't affect the import */ Containers::Pointer importer = _manager.instantiate("KtxImporter"); @@ -1519,29 +1535,14 @@ void KtxImporterTest::orientationInvalid() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, data.file)); - CORRADE_VERIFY(fileData.size() >= sizeof(Implementation::KtxHeader)); - - constexpr auto key = "KTXorientation"_s; - UnsignedInt size = key.size() + 1 + data.orientation.size() + 1; - size = (size + 3)/4*4; - Containers::Array keyValueData{ValueInit, sizeof(UnsignedInt) + size}; - - std::size_t offset = 0; - *reinterpret_cast(keyValueData.data()) = Utility::Endianness::littleEndian(size); - offset += sizeof(size); - Utility::copy(key, keyValueData.suffix(offset).prefix(key.size())); - offset += key.size() + 1; - Utility::copy(data.orientation, keyValueData.suffix(offset).prefix(data.orientation.size())); - - patchKeyValueData(keyValueData, fileData); + patchKeyValueData(createKeyValueData("KTXorientation"_s, data.orientation), fileData); std::ostringstream outWarning; Warning redirectWarning{&outWarning}; + CORRADE_VERIFY(importer->openData(fileData)); constexpr Containers::StringView orientations[]{"right"_s, "down"_s, "forward"_s}; const Containers::String orientationString = ", "_s.join(Containers::arrayView(orientations).prefix(data.dimensions)); - - CORRADE_VERIFY(importer->openData(fileData)); CORRADE_COMPARE(outWarning.str(), Utility::formatString("Trade::KtxImporter::openData(): missing or invalid orientation, assuming {}\n", orientationString)); } @@ -1602,6 +1603,9 @@ void KtxImporterTest::orientationFlip() { void KtxImporterTest::orientationFlipCompressed() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); + /* Just check for the warning, image2DCompressed checks that the output is + as expected */ + std::ostringstream outWarning; Warning redirectWarning{&outWarning}; @@ -1634,9 +1638,6 @@ void KtxImporterTest::swizzle() { CORRADE_VERIFY(importer->openData(fileData)); - /** @todo Change origin to top-left for the swizzle test files so we can - check the relevant messages only. Also for swizzleMultipleBytes() - and swizzleIdentity(). */ std::string expectedMessage = "Trade::KtxImporter::openData(): image will be flipped along y\n"; if(data.message) expectedMessage += Utility::formatString("Trade::KtxImporter::openData(): {}\n", data.message); @@ -1717,9 +1718,7 @@ void KtxImporterTest::swizzleCompressed() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-compressed-bc1.ktx2")); - - constexpr auto keyValueData = "\x10\x00\x00\x00KTXswizzle\0bgra\0"_s; - patchKeyValueData(keyValueData, fileData); + patchKeyValueData(createKeyValueData("KTXswizzle"_s, "bgra"_s), fileData); std::ostringstream out; Error redirectError{&out}; From e5bf3f4e7d4f6e8b717eb4597f718b5d38a0f4c7 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 18 Aug 2021 17:55:20 +0200 Subject: [PATCH 64/95] KtxImporter: clarify comment --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 17b94672b..a5af10c2b 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -382,7 +382,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { CORRADE_INTERNAL_ASSERT(f->numDataDimensions >= f->numDimensions); CORRADE_INTERNAL_ASSERT(f->numDataDimensions - f->numDimensions <= 1); - /* Make sure we don't choke on size calculations using product() */ + /* Make size 1 instead of 0 in missing dimensions for simpler calculations */ const Vector3i size = Math::max(Vector3i{header.imageSize}, 1); if(numFaces != 1) { From 68021537b43c20008a8ead32bc5d5217af761d25 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 18 Aug 2021 17:59:56 +0200 Subject: [PATCH 65/95] KtxImporter: update test files --- .../KtxImporter/Test/1d-compressed-bc1.bin | Bin 0 -> 8 bytes .../KtxImporter/Test/1d-compressed-etc2.bin | Bin 0 -> 16 bytes .../Test/1d-compressed-mipmaps-mip0.bin | Bin 0 -> 16 bytes .../Test/1d-compressed-mipmaps-mip1.bin | Bin 0 -> 8 bytes .../Test/1d-compressed-mipmaps-mip2.bin | Bin 0 -> 8 bytes .../Test/1d-compressed-mipmaps.ktx2 | Bin 0 -> 288 bytes .../KtxImporter/Test/2d-compressed-astc.bin | 1 + .../KtxImporter/Test/2d-compressed-astc.ktx2 | Bin 0 -> 224 bytes .../KtxImporter/Test/2d-compressed-bc1.bin | Bin 0 -> 32 bytes .../KtxImporter/Test/2d-compressed-bc2.bin | Bin 0 -> 64 bytes .../KtxImporter/Test/2d-compressed-bc3.bin | Bin 0 -> 64 bytes .../KtxImporter/Test/2d-compressed-bc4.bin | Bin 0 -> 32 bytes .../KtxImporter/Test/2d-compressed-bc5.bin | Bin 0 -> 64 bytes .../KtxImporter/Test/2d-compressed-etc2.bin | Bin 0 -> 72 bytes .../KtxImporter/Test/2d-compressed-layers.bin | Bin 0 -> 144 bytes .../Test/2d-compressed-layers.ktx2 | Bin 0 -> 352 bytes .../Test/2d-compressed-mipmaps-mip0.bin | Bin 0 -> 72 bytes .../Test/2d-compressed-mipmaps-mip1.bin | Bin 0 -> 16 bytes .../Test/2d-compressed-mipmaps-mip2.bin | Bin 0 -> 8 bytes .../Test/2d-compressed-mipmaps-mip3.bin | Bin 0 -> 8 bytes .../Test/2d-compressed-mipmaps.ktx2 | Bin 0 -> 384 bytes .../KtxImporter/Test/2d-compressed-pvrtc.bin | Bin 0 -> 32 bytes .../KtxImporter/Test/2d-d16.ktx2 | Bin 0 -> 236 bytes .../KtxImporter/Test/2d-d24s8.ktx2 | Bin 0 -> 276 bytes .../KtxImporter/Test/2d-d32fs8.ktx2 | Bin 0 -> 328 bytes .../Test/2d-mipmaps-and-layers.ktx2 | Bin 0 -> 432 bytes .../KtxImporter/Test/2d-rgb32.ktx2 | Bin 0 -> 384 bytes .../KtxImporter/Test/2d-rgbf32.ktx2 | Bin 0 -> 384 bytes src/MagnumPlugins/KtxImporter/Test/2d-s8.ktx2 | Bin 0 -> 224 bytes .../KtxImporter/Test/3d-compressed.bin | Bin 0 -> 216 bytes .../KtxImporter/Test/3d-compressed.ktx2 | Bin 0 -> 424 bytes .../KtxImporter/Test/3d-mipmaps.ktx2 | Bin 0 -> 432 bytes src/MagnumPlugins/KtxImporter/Test/3d.ktx2 | Bin 348 -> 348 bytes .../KtxImporter/Test/CMakeLists.txt | 79 ++++++++++++++---- .../Test/{black1d.png => black-1d.png} | Bin .../KtxImporter/Test/cubemap-layers.ktx2 | Bin 384 -> 384 bytes .../KtxImporter/Test/generate.sh | 64 ++++++++------ 37 files changed, 105 insertions(+), 39 deletions(-) create mode 100644 src/MagnumPlugins/KtxImporter/Test/1d-compressed-bc1.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/1d-compressed-etc2.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/1d-compressed-mipmaps-mip0.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/1d-compressed-mipmaps-mip1.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/1d-compressed-mipmaps-mip2.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/1d-compressed-mipmaps.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-astc.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-astc.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc1.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc2.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc3.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc4.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc5.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-etc2.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-layers.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-layers.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-mipmaps-mip0.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-mipmaps-mip1.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-mipmaps-mip2.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-mipmaps-mip3.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-mipmaps.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-pvrtc.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-d16.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-d24s8.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-d32fs8.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-mipmaps-and-layers.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-rgb32.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-rgbf32.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-s8.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/3d-compressed.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/3d-compressed.ktx2 create mode 100644 src/MagnumPlugins/KtxImporter/Test/3d-mipmaps.ktx2 rename src/MagnumPlugins/KtxImporter/Test/{black1d.png => black-1d.png} (100%) diff --git a/src/MagnumPlugins/KtxImporter/Test/1d-compressed-bc1.bin b/src/MagnumPlugins/KtxImporter/Test/1d-compressed-bc1.bin new file mode 100644 index 0000000000000000000000000000000000000000..fd9c936b64429e41732905210c0d411d38b20cea GIT binary patch literal 8 OcmezWpF!a=5C8xd*9B_; literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/1d-compressed-etc2.bin b/src/MagnumPlugins/KtxImporter/Test/1d-compressed-etc2.bin new file mode 100644 index 0000000000000000000000000000000000000000..81368ffd92902a4919c1ec2fac0a4849f26daac7 GIT binary patch literal 16 Xcmey+(sGBNpZ`A#!-r4&{}~tnLCFU7 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/1d-compressed-mipmaps-mip0.bin b/src/MagnumPlugins/KtxImporter/Test/1d-compressed-mipmaps-mip0.bin new file mode 100644 index 0000000000000000000000000000000000000000..3cb42ca76046d2d0a6d0b8452005c819bbe27fc1 GIT binary patch literal 16 Xcmey+T6%+@pZ`A#!-r4&{}~tnK+*=| literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/1d-compressed-mipmaps-mip1.bin b/src/MagnumPlugins/KtxImporter/Test/1d-compressed-mipmaps-mip1.bin new file mode 100644 index 0000000000000000000000000000000000000000..289ffb850062e66187e1485e7d3a1ef8a9daf001 GIT binary patch literal 8 Pcmey(P*BJ4fu8{Y599*r literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/1d-compressed-mipmaps-mip2.bin b/src/MagnumPlugins/KtxImporter/Test/1d-compressed-mipmaps-mip2.bin new file mode 100644 index 0000000000000000000000000000000000000000..459e5ba67d523b44b7c44919c91e9265ac5ede63 GIT binary patch literal 8 Pcmb0|Nsi5VHd@G60D&Lj`64X&oRw0>lrq!k<%}`Ls Z@PVJLO>emto)+P)Vz|!lFa-(hN2Xpm^4JZyeP9I rwTK}gEGQ(k!Y4CHq0CgzP|pA;2?Qb=ELoWvEhH2L1Q_dotSANmpbZ~< literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc1.bin b/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc1.bin new file mode 100644 index 0000000000000000000000000000000000000000..0500e401be3a064ef53248b62a8e81ff2bbfc023 GIT binary patch literal 32 TcmZSh!2kyQ6%d*M%7@VaKd1w6 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc2.bin b/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc2.bin new file mode 100644 index 0000000000000000000000000000000000000000..ac0494d369fcc31ecb1f4e6428e90d1458312de5 GIT binary patch literal 64 acmezW9|9PDFn|G+`=7r8m5(kD;{yQlhR3kzrf literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc4.bin b/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc4.bin new file mode 100644 index 0000000000000000000000000000000000000000..4c8f88463b4bc5c43e794170fac37ecedcf1e402 GIT binary patch literal 32 UcmezWp8*W&>ml@iC?7@x0E7ev`2YX_ literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc5.bin b/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc5.bin new file mode 100644 index 0000000000000000000000000000000000000000..fbe2304e7c98014c672414ed5087488127134549 GIT binary patch literal 64 VcmezWp8*Bb*Q0P5{-epG^8pK!2KfL0 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-compressed-etc2.bin b/src/MagnumPlugins/KtxImporter/Test/2d-compressed-etc2.bin new file mode 100644 index 0000000000000000000000000000000000000000..f1db6931dd8591186dca680b09216b4b5e794652 GIT binary patch literal 72 xcmb`8#taO|Gy^jO0{|st3jqKC literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-compressed-layers.bin b/src/MagnumPlugins/KtxImporter/Test/2d-compressed-layers.bin new file mode 100644 index 0000000000000000000000000000000000000000..74f73bbc95b1804f3b43557602a6c8d9d0293b39 GIT binary patch literal 144 zcmYj}+YJB^5CkVkeCk32`a`KYAsaSvKa<;6Ck^sqGV9)uD1id~>85qDwVN}E=z{t` Owe$b>t*bBJ)Xo8eH4`cT literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-compressed-layers.ktx2 b/src/MagnumPlugins/KtxImporter/Test/2d-compressed-layers.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..a939a87e04726e55750690b4b9342dc541a4a473 GIT binary patch literal 352 zcmZ4O9TK5nWU!l;ONwg>0|Nsi5OV@C7Z8I06A**MKo|rvfLI5JL25047#&=I3QRzw zL3%)di9v&5AtMt5GcyAN2Sf-++JmeCga1Gv1Y`l7m0y&Znpcuol9`{!P?Q1`lZJ?w z7iE^D7BK{b1%;$m_+%z2l$q)o>KTBwGt@D#)HAR@U}#`yQ2YO%fq{jApNWB=0WJ=c nhlzt|V`GRq7|j558xSyo^dr+CbCCHkd6+mzKS&-%gUkT{{JJiO literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-compressed-mipmaps-mip0.bin b/src/MagnumPlugins/KtxImporter/Test/2d-compressed-mipmaps-mip0.bin new file mode 100644 index 0000000000000000000000000000000000000000..58425382727c8ee62058438f81f5e62d492c1b0f GIT binary patch literal 72 xcmb0|Nsi5OV@C7ZAe$NQ?!_*#M+#++W-Hc@Zo1-;75qVgqQ}h3j+B0V66ZD85o3s9H6uEi!xL5N)k&l^Ya*rN`Yc35b^S&%#ze1 z2H(W=ywY4NEANsD&)mfHROkG>veY7w4C8;M|IGhc{)_7 yAmZgknI)-348Do!d8N5lR^BBQp1FzXsm}R%WvN9V8GN7~3SesfqXAs<=;{F(B1TF8 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-mipmaps-and-layers.ktx2 b/src/MagnumPlugins/KtxImporter/Test/2d-mipmaps-and-layers.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..c5dbd3211af5aa15ac72335e91a6c7847b6ecee3 GIT binary patch literal 432 zcmZ4O9TK5nWU!l;ONvXDfq{V$h*^M`8HhmuM1#a&{24&D4-h{AVhbQf2QDDRK#&8W z;Is}@TnH)-qZOdyoKSHX4YC6Sm>4`57#W$sY?vDu7}&vl5b+;~IT+ZXra<@t4D1jM zP4`57#Sg^@j!%tr2GL0vtd62Lxa5ngZx2cz5|2&A!NP~kOOpfeoUP)p}W_}(+ zQ3_B@8X{g^lv$Em#1Ie`6p~uulbNJYW~yhXX8b zAi%_+!NAA}F%4!W0|PrRL>6L?5Re0OR(?@tYFE-$8^fCq{av(!nx#?W2b?!{! V-rxQKf;>*j=e*rJmxp>&K0h{H9b5na literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/3d-compressed.ktx2 b/src/MagnumPlugins/KtxImporter/Test/3d-compressed.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..0af0287aade54d3b33b7ebe2d6bfa11bd733ddd8 GIT binary patch literal 424 zcmZ4O9TK5nWU!l;ONwg>0|Nsi5OV@C7Z5W8F$jRfKo|rvfLI5JL25047#&=I3fw@W zL3%)di9v&5AtMt5Gth7jh!BvpXM!>R1A#D*19Vn?QD$mhNn%N6ejY>C?vJQCo@T*%v8@%&j4aV9Ro`}1N#Gp28IT;|Nj{nSQz-382A|&VB#$t7 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImporter/Test/3d-mipmaps.ktx2 b/src/MagnumPlugins/KtxImporter/Test/3d-mipmaps.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..2877fd11788ce423c502f639375a8004ce845514 GIT binary patch literal 432 zcmZ4O9TK5nWU!l;ONvXDfq{V$h*^M`8H#}vSPaAmff+#T1H?~&*a3*qfeXk0AjpAG zaM}VY&IT2S(K=9ZnE5aoWCsW^F?cXAGBSbLFgXSWb}%1A{0CwV26m_^5WWBdJ46Fi zUKmINy_sK>nVMIUSdy8a$551#$-tlj5ic*wEJ-b5@J&q5E6ugC@-C_H%uP&Bb_5=0^&ma/configure.h corrade_add_test(KtxImporterTest KtxImporterTest.cpp LIBRARIES Magnum::Trade FILES - 1d.ktx2 - 1d-mipmaps.ktx2 - bgr.ktx2 - bgra.ktx2 - bgra-swizzle-bgra.ktx2 - bgr-swizzle-bgr.ktx2 - orientation-empty.ktx2 - rgb.ktx2 - rgba.ktx2 - rgb-mipmaps.ktx2 - swizzle-bgr.ktx2 - swizzle-bgra.ktx2 - swizzle-identity.ktx2 - swizzle-unsupported.ktx2 - version1.ktx) + 1d-compressed-bc1.bin + 1d-compressed-bc1.ktx2 + 1d-compressed-etc2.bin + 1d-compressed-etc2.ktx2 + 1d-compressed-mipmaps-mip0.bin + 1d-compressed-mipmaps-mip1.bin + 1d-compressed-mipmaps-mip2.bin + 1d-compressed-mipmaps.ktx2 + 1d-layers.ktx2 + 1d-mipmaps.ktx2 + 1d.ktx2 + 2d-compressed-astc.bin + 2d-compressed-astc.ktx2 + 2d-compressed-bc1.bin + 2d-compressed-bc1.ktx2 + 2d-compressed-bc2.bin + 2d-compressed-bc2.ktx2 + 2d-compressed-bc3.bin + 2d-compressed-bc3.ktx2 + 2d-compressed-bc4.bin + 2d-compressed-bc4.ktx2 + 2d-compressed-bc5.bin + 2d-compressed-bc5.ktx2 + 2d-compressed-etc2.bin + 2d-compressed-etc2.ktx2 + 2d-compressed-layers.bin + 2d-compressed-layers.ktx2 + 2d-compressed-mipmaps-mip0.bin + 2d-compressed-mipmaps-mip1.bin + 2d-compressed-mipmaps-mip2.bin + 2d-compressed-mipmaps-mip3.bin + 2d-compressed-mipmaps.ktx2 + 2d-compressed-pvrtc.bin + 2d-compressed-pvrtc.ktx2 + 2d-d16.ktx2 + 2d-d24s8.ktx2 + 2d-d32fs8.ktx2 + 2d-layers.ktx2 + 2d-mipmaps-and-layers.ktx2 + 2d-mipmaps-incomplete.ktx2 + 2d-mipmaps.ktx2 + 2d-rgb.ktx2 + 2d-rgb32.ktx2 + 2d-rgba.ktx2 + 2d-rgbf32.ktx2 + 2d-s8.ktx2 + 3d-compressed.bin + 3d-compressed.ktx2 + 3d-layers.ktx2 + 3d-mipmaps.ktx2 + 3d.ktx2 + bgr-swizzle-bgr-16bit.ktx2 + bgr-swizzle-bgr.ktx2 + bgr.ktx2 + bgra-swizzle-bgra.ktx2 + bgra.ktx2 + cubemap-layers.ktx2 + cubemap-mipmaps.ktx2 + cubemap.ktx2 + swizzle-bgr.ktx2 + swizzle-bgra.ktx2 + swizzle-identity.ktx2 + swizzle-unsupported.ktx2 + version1.ktx) target_include_directories(KtxImporterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$ ${PROJECT_SOURCE_DIR}/src) diff --git a/src/MagnumPlugins/KtxImporter/Test/black1d.png b/src/MagnumPlugins/KtxImporter/Test/black-1d.png similarity index 100% rename from src/MagnumPlugins/KtxImporter/Test/black1d.png rename to src/MagnumPlugins/KtxImporter/Test/black-1d.png diff --git a/src/MagnumPlugins/KtxImporter/Test/cubemap-layers.ktx2 b/src/MagnumPlugins/KtxImporter/Test/cubemap-layers.ktx2 index df4d0a0172fbc1307568bafabdc4708f29aca29d..8aafbc6ab86f622c3b7e453ccc9c4151eae90a74 100644 GIT binary patch delta 11 ScmZo*ZeZSE!8log(E|VzTmtL> delta 15 WcmZo*ZeZSE!8kd9(O|LyqXYmVssqpf diff --git a/src/MagnumPlugins/KtxImporter/Test/generate.sh b/src/MagnumPlugins/KtxImporter/Test/generate.sh index 99c3a1732..e1027a5a0 100644 --- a/src/MagnumPlugins/KtxImporter/Test/generate.sh +++ b/src/MagnumPlugins/KtxImporter/Test/generate.sh @@ -34,52 +34,61 @@ toktx --t2 --target_type RGBA --input_swizzle bgra bgra.ktx2 pattern.png toktx --t2 2d-rgb.ktx2 pattern.png toktx --t2 --target_type RGBA 2d-rgba.ktx2 pattern.png +# A few interesting formats +# PVRTexTool lets us export to different formats although the documentation +# isn't very clear on how it converts +PVRTexToolCLI -i pattern.png -o 2d-rgb32.ktx2 -f r32g32b32,UI,sRGB +PVRTexToolCLI -i pattern.png -o 2d-rgbf32.ktx2 -f r32g32b32,SF,sRGB + # manual mipmaps, full pyramid and one without the last toktx --t2 --mipmap 2d-mipmaps.ktx2 pattern.png pattern-mip1.png pattern-mip2.png toktx --t2 --mipmap --levels 2 2d-mipmaps-incomplete.ktx2 pattern.png pattern-mip1.png -# 2D layers -# output is an image with numLayers=0, wtf -#toktx --t2 --layers 3 2d-layers.ktx2 pattern.png pattern.png black.png +# layers PVRTexToolCLI -i pattern.png,pattern.png,black.png -o 2d-layers.ktx2 -array -f r8g8b8,UBN,sRGB +# mipmaps and layers +# PVRTexTool doesn't let us specify mip images manually +PVRTexToolCLI -i pattern.png,pattern.png,black.png -o 2d-mipmaps-and-layers.ktx2 -array -m -mfilter nearest -f r8g8b8,UBN,sRGB + # cube map -# toktx failed to create ktxTexture; KTX error: Operation not allowed in the current state. -#toktx --t2 --cubemap cube.ktx2 pattern.png pattern.png pattern.png pattern.png pattern.png pattern.png PVRTexToolCLI -i cube+x.png,cube-x.png,cube+y.png,cube-y.png,cube+z.png,cube-z.png -o cubemap.ktx2 -cube -f r8g8b8,UBN,sRGB -PVRTexToolCLI -i cube+x.png,cube-x.png,cube+y.png,cube-y.png,cube+z.png,cube-z.png -o cubemap-mipmaps.ktx2 -cube -m -mfilter linear -f r8g8b8,UBN,sRGB -# faces for layer 0, then layer 1 -PVRTexToolCLI -i cube+x.png,cube-x.png,cube+y.png,cube-y.png,cube+z.png,cube-z.png,cube+x.png,cube-x.png,cube+y.png,cube-y.png,cube+z.png,cube-z.png -o cubemap-layers.ktx2 -cube -array -f r8g8b8,UBN,sRGB +PVRTexToolCLI -i cube+x.png,cube-x.png,cube+y.png,cube-y.png,cube+z.png,cube-z.png -o cubemap-mipmaps.ktx2 -cube -m -mfilter nearest -f r8g8b8,UBN,sRGB +# layered cube map: faces for layer 0, then layer 1 +PVRTexToolCLI -i cube+x.png,cube-x.png,cube+y.png,cube-y.png,cube+z.png,cube-z.png,cube+z.png,cube-z.png,cube+x.png,cube-x.png,cube+y.png,cube-y.png -o cubemap-layers.ktx2 -cube -array -f r8g8b8,UBN,sRGB # 1D toktx --t2 1d.ktx2 pattern-1d.png toktx --t2 --mipmap 1d-mipmaps.ktx2 pattern-1d.png pattern-mip1.png pattern-mip2.png -# output is an image with numLayers=0, wtf -#toktx --t2 --layers 3 1d-layers.ktx2 pattern-1d.png pattern-1d.png black1d.png -PVRTexToolCLI -i pattern-1d.png,pattern-1d.png,black1d.png -o 1d-layers.ktx2 -array -f r8g8b8,UBN,sRGB +PVRTexToolCLI -i pattern-1d.png,pattern-1d.png,black-1d.png -o 1d-layers.ktx2 -array -f r8g8b8,UBN,sRGB # 3D -# toktx failed to create ktxTexture; KTX error: Operation not allowed in the current state. -#toktx --t2 --depth 3 3d-rgb.ktx2 black.png pattern.png pattern.png # PVRTexTool doesn't support 3D images, sigh # Create a layered image and patch the header to make it 3D. The level layout # is identical to a 3D image, but the orientation metadata will become invalid. -# This won't pass validation but KtxImporter should ignore invalid metadata. -# However, because the z axis will be flipped, we need to pass the images -# in reverse order here compared to 2d-layers.ktx2. +# The importer will ignore invalid orientation but for correct ground-truth +# data for converter tests we need to patch it. +# Because the z axis will be flipped, we need to pass the images in reverse +# order compared to 2d-layers.ktx2. PVRTexToolCLI -i black.png,pattern.png,pattern.png -o 3d.ktx2 -array -f r8g8b8,UBN,sRGB # depth = 3, numLayers = 0 printf '\x03\x00\x00\x00\x00\x00\x00\x00' | dd conv=notrunc of=3d.ktx2 bs=1 seek=28 +# KTXorientation length +printf '\x13' | dd conv=notrunc of=3d.ktx2 bs=1 seek=180 +# KTXorientation, replace first padding byte +printf 'i' | dd conv=notrunc of=3d.ktx2 bs=1 seek=201 + # We can't patch a 2D array texture with mipmaps into a 3D texture because the # number of layers stays the same, unlike shrinking z in mipmap levels -# TODO find another way to generate a test file -#PVRTexToolCLI -i black.png,pattern.png,pattern.png -o 3d-mipmaps.ktx2 -array -m -mfilter nearest -f r8g8b8,UBN,sRGB -#printf '\x03\x00\x00\x00\x00\x00\x00\x00' | dd conv=notrunc of=3d-mipmaps.ktx2 bs=1 seek=28 +# 3d-mipmaps.ktx2 is generated by running KtxImageConverterTest --save-diagnostic PVRTexToolCLI -i black.png,pattern.png,pattern.png,black.png,black.png,pattern.png -o 3d-layers.ktx2 -array -f r8g8b8,UBN,sRGB printf '\x03\x00\x00\x00\x02\x00\x00\x00' | dd conv=notrunc of=3d-layers.ktx2 bs=1 seek=28 +# TODO: patch up KTXorientation for 3d-layers.ktx2 if we need it for the converter tests # Compressed # PVRTC and BC* don't support non-power-of-2 +# Generating a whole slew of formats here mainly for testing the DFD output of +# KtxImageConverter, best way to get known good data from an external tool PVRTexToolCLI -i pattern-pot.png -o 2d-compressed-pvrtc.ktx2 -f PVRTC1_4,UBN,sRGB PVRTexToolCLI -i pattern-pot.png -o 2d-compressed-bc1.ktx2 -f BC1,UBN,sRGB PVRTexToolCLI -i pattern-pot.png -o 2d-compressed-bc2.ktx2 -f BC2,UBN,sRGB @@ -87,14 +96,21 @@ PVRTexToolCLI -i pattern-pot.png -o 2d-compressed-bc3.ktx2 -f BC3,UBN,sRGB PVRTexToolCLI -i pattern-pot.png -o 2d-compressed-bc4.ktx2 -f BC4,UBN,sRGB PVRTexToolCLI -i pattern-pot.png -o 2d-compressed-bc5.ktx2 -f BC5,UBN,sRGB PVRTexToolCLI -i pattern-uneven.png -o 2d-compressed-etc2.ktx2 -f ETC2_RGB,UBN,sRGB +PVRTexToolCLI -i pattern-uneven.png -o 2d-compressed-astc.ktx2 -f ASTC_12x10,UBN,sRGB + +PVRTexToolCLI -i pattern-uneven.png -o 2d-compressed-mipmaps.ktx2 -m -mfilter nearest -f ETC2_RGB,UBN,sRGB +PVRTexToolCLI -i pattern-uneven.png,black.png -o 2d-compressed-layers.ktx2 -array -f ETC2_RGB,UBN,sRGB PVRTexToolCLI -i pattern-1d.png -o 1d-compressed-bc1.ktx2 -f BC1,UBN,sRGB PVRTexToolCLI -i pattern-1d-uneven.png -o 1d-compressed-etc2.ktx2 -f ETC2_RGB,UBN,sRGB -# TODO ASTC +PVRTexToolCLI -i pattern-1d-uneven.png -o 1d-compressed-mipmaps.ktx2 -m -mfilter nearest -f ETC2_RGB,UBN,sRGB + +PVRTexToolCLI -i pattern-uneven.png,pattern-uneven.png,black.png -o 3d-compressed.ktx2 -array -f ETC2_RGB,UBN,sRGB +printf '\x03\x00\x00\x00\x00\x00\x00\x00' | dd conv=notrunc of=3d-compressed.ktx2 bs=1 seek=28 +printf '\x13' | dd conv=notrunc of=3d-compressed.ktx2 bs=1 seek=148 +printf 'i' | dd conv=notrunc of=3d-compressed.ktx2 bs=1 seek=169 # TODO: -# 3D mips -# combined arrays + mips -# orientation: 1D l, 2D l, cubemap always rd, 3D -# compressed +# 3D mips so we don't have to generate our own in the converter tests +# Should be possible once https://github.com/KhronosGroup/KTX-Software/pull/468 made it into a release From 503518dd8b59f12bb32b0feec231595de61386f6 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 18 Aug 2021 20:21:08 +0200 Subject: [PATCH 66/95] KtxImageConverter: fix DFD channel upper for D24[S8] --- .../KtxImageConverter/KtxImageConverter.cpp | 18 ++++++++++-------- .../KtxImporter/Test/2d-d24s8.ktx2 | Bin 276 -> 276 bytes 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 839d4bf4d..450193762 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -196,8 +196,9 @@ struct SampleData { Implementation::KdfBasicBlockSample::ChannelId id; /* For pixel formats where not all channels share the same suffix (only combined depth + stencil for now) we have to specify it manually. - Is there a good way to automate this in formatMapping.hpp? */ - Implementation::VkFormatSuffix suffix = {}; + Note that the (invalid) default value is 0. */ + /** @todo Is there a good way to automate this in formatMapping.hpp? */ + Implementation::VkFormatSuffix suffix; }; Containers::Pair> samples(PixelFormat format) { @@ -465,7 +466,7 @@ UnsignedByte channelFormat(Implementation::VkFormatSuffix suffix) { CORRADE_ASSERT_UNREACHABLE("channelFormat(): invalid format suffix" << UnsignedInt(suffix), {}); } -Containers::Pair channelMapping(Implementation::VkFormatSuffix suffix, UnsignedByte typeSize) { +Containers::Pair channelMapping(Implementation::VkFormatSuffix suffix, UnsignedInt bitLength) { /* sampleLower and sampleUpper define how to interpret the range of values found in a channel. samplerLower = black value or -1 for signed values @@ -475,9 +476,9 @@ Containers::Pair channelMapping(Implementation::VkForm simple version is enough for our needs. Signed integer values are sign-extended. Floats need to be bitcast. */ - CORRADE_INTERNAL_ASSERT(typeSize <= 4); + CORRADE_INTERNAL_ASSERT(bitLength <= 32); - const UnsignedInt typeMask = ~0u >> ((4 - typeSize)*8); + const UnsignedInt typeMask = ~0u >> (32 - bitLength); switch(suffix) { case Implementation::VkFormatSuffix::UNORM: @@ -559,12 +560,12 @@ Containers::Array fillDataFormatDescriptor(Format format, Implementation:: const UnsignedByte typeSize = formatTypeSize(format); /* Compressed integer formats must use 32-bit lower/upper */ - const UnsignedByte mappingTypeSize = isCompressedFormat ? sizeof(UnsignedInt) : typeSize; + const UnsignedByte mappingBitLength = (isCompressedFormat ? sizeof(UnsignedInt) : typeSize)*8; /* @todo BC6h has unsigned floats, but the spec says to use a sampleLower of -1.0. Is this an error? https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#bc6h_channel The signed channel format flag is still set, however. */ - const auto lowerUpper = channelMapping(suffix, mappingTypeSize); + const auto lowerUpper = channelMapping(suffix, mappingBitLength); const UnsignedByte formatFlags = channelFormat(suffix); /* For non-compressed RGBA channels, we get */ const UnsignedByte bitRangeMultiplier = isDepthStencil ? 1 : typeSize; @@ -582,8 +583,9 @@ Containers::Array fillDataFormatDescriptor(Format format, Implementation:: UnsignedByte sampleFormatFlags; Containers::Pair sampleLowerUpper; if(sampleContent.suffix != Implementation::VkFormatSuffix{}) { + CORRADE_INTERNAL_ASSERT(!isCompressedFormat); sampleFormatFlags = channelFormat(sampleContent.suffix); - sampleLowerUpper = channelMapping(sampleContent.suffix, mappingTypeSize); + sampleLowerUpper = channelMapping(sampleContent.suffix, sample.bitLength + 1); } else { sampleFormatFlags = formatFlags; sampleLowerUpper = lowerUpper; diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-d24s8.ktx2 b/src/MagnumPlugins/KtxImporter/Test/2d-d24s8.ktx2 index 7665ed2f6c4407e16988f14c2a5a7d4c4f08bef0..a76185663f6667696968f27f5f0ea8a793cd732d 100644 GIT binary patch delta 11 ScmbQjG=*uxWJZRGQyKskfdlpc delta 11 TcmbQjG=*uxWXAs!r!)Wn8hr%y From 98f3dbcd7f40c8a121b73e1a7ac4ead0159565f1 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 18 Aug 2021 20:56:54 +0200 Subject: [PATCH 67/95] Ktx{Importer, ImageConverter}: fix warnings and errors --- .../KtxImageConverter/KtxImageConverter.cpp | 134 +++++++++--------- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 52 +++---- .../KtxImporter/Test/KtxImporterTest.cpp | 28 ++-- 3 files changed, 107 insertions(+), 107 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 450193762..f0413b2fc 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -195,10 +196,9 @@ struct SampleData { UnsignedShort bitLength; Implementation::KdfBasicBlockSample::ChannelId id; /* For pixel formats where not all channels share the same suffix (only - combined depth + stencil for now) we have to specify it manually. - Note that the (invalid) default value is 0. */ + combined depth + stencil for now) we have to specify it manually */ /** @todo Is there a good way to automate this in formatMapping.hpp? */ - Implementation::VkFormatSuffix suffix; + Containers::Optional suffix; }; Containers::Pair> samples(PixelFormat format) { @@ -209,29 +209,29 @@ Containers::Pair fillDataFormatDescriptor(Format format, Implementation:: from the main suffix */ UnsignedByte sampleFormatFlags; Containers::Pair sampleLowerUpper; - if(sampleContent.suffix != Implementation::VkFormatSuffix{}) { + if(sampleContent.suffix) { CORRADE_INTERNAL_ASSERT(!isCompressedFormat); - sampleFormatFlags = channelFormat(sampleContent.suffix); - sampleLowerUpper = channelMapping(sampleContent.suffix, sample.bitLength + 1); + sampleFormatFlags = channelFormat(*sampleContent.suffix); + sampleLowerUpper = channelMapping(*sampleContent.suffix, sample.bitLength + 1); } else { sampleFormatFlags = formatFlags; sampleLowerUpper = lowerUpper; @@ -729,7 +729,7 @@ Containers::Array KtxImageConverter::convertLevels(Containers::ArrayView data; - }; - - Containers::Array in; - - /* Dimensions of the source image (1-3) */ - UnsignedByte numDimensions; - /* Dimensions of the imported image data, including extra dimensions for - array layers or cube map faces */ - UnsignedByte numDataDimensions; - TextureType type; - BoolVector3 flip; - - Format pixelFormat; - - /* Usually only one image with n or n+1 dimensions, multiple images for - 3D array layers */ - Containers::Array> imageData; -}; - Containers::Optional decodeFormat(Implementation::VkFormat vkFormat) { Format f{}; @@ -265,6 +240,31 @@ Containers::Optional decodeFormat(Implementation::VkFormat vkFormat) { return {}; } +} + +struct KtxImporter::File { + struct LevelData { + Vector3i size; + Containers::ArrayView data; + }; + + Containers::Array in; + + /* Dimensions of the source image (1-3) */ + UnsignedByte numDimensions; + /* Dimensions of the imported image data, including extra dimensions for + array layers or cube map faces */ + UnsignedByte numDataDimensions; + TextureType type; + BoolVector3 flip; + + Format pixelFormat; + + /* Usually only one image with n or n+1 dimensions, multiple images for + 3D array layers */ + Containers::Array> imageData; +}; + KtxImporter::KtxImporter(PluginManager::AbstractManager& manager, const std::string& plugin): AbstractImporter{manager, plugin} {} KtxImporter::~KtxImporter() = default; @@ -360,7 +360,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { if(header.imageSize.y() > 0) { if(header.imageSize.z() > 0) { f->numDimensions = 3; - f->type = TextureData::Type::Texture3D; + f->type = TextureType::Texture3D; } else { f->numDimensions = 2; if(isCubeMap) diff --git a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp index 798428434..55f3287dc 100644 --- a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp +++ b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp @@ -120,11 +120,11 @@ using namespace Math::Literals; const Color3ub PatternRgb1DData[3][4]{ /* pattern-1d.png */ - 0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x007f7f_rgb, + {0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x007f7f_rgb}, /* pattern-1d.png */ - 0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x007f7f_rgb, + {0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x007f7f_rgb}, /* black-1d.png */ - 0x000000_rgb, 0x000000_rgb, 0x000000_rgb, 0x000000_rgb + {0x000000_rgb, 0x000000_rgb, 0x000000_rgb, 0x000000_rgb} }; /* Origin bottom-left */ @@ -248,16 +248,16 @@ const struct { const struct { const char* name; const char* file; - const Trade::TextureType type; + const TextureType type; } TextureData[]{ - {"1D", "1d.ktx2", Trade::TextureType::Texture1D}, - {"1D array", "1d-layers.ktx2", Trade::TextureType::Texture1DArray}, - {"2D", "2d-rgb.ktx2", Trade::TextureType::Texture2D}, - {"2D array", "2d-layers.ktx2", Trade::TextureType::Texture2DArray}, - {"cube map", "cubemap.ktx2", Trade::TextureType::CubeMap}, - {"cube map array", "cubemap-layers.ktx2", Trade::TextureType::CubeMapArray}, - {"3D", "3d.ktx2", Trade::TextureType::Texture3D}, - {"3D array", "3d-layers.ktx2", Trade::TextureType::Texture3D} + {"1D", "1d.ktx2", TextureType::Texture1D}, + {"1D array", "1d-layers.ktx2", TextureType::Texture1DArray}, + {"2D", "2d-rgb.ktx2", TextureType::Texture2D}, + {"2D array", "2d-layers.ktx2", TextureType::Texture2DArray}, + {"cube map", "cubemap.ktx2", TextureType::CubeMap}, + {"cube map array", "cubemap-layers.ktx2", TextureType::CubeMapArray}, + {"3D", "3d.ktx2", TextureType::Texture3D}, + {"3D array", "3d-layers.ktx2", TextureType::Texture3D} }; const struct { @@ -1747,11 +1747,11 @@ void KtxImporterTest::importTwice() { /* Verify that everything is working the same way on second use */ { - Containers::Optional image = importer->image2D(0); + Containers::Optional image = importer->image2D(0); CORRADE_VERIFY(image); CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); } { - Containers::Optional image = importer->image2D(0); + Containers::Optional image = importer->image2D(0); CORRADE_VERIFY(image); CORRADE_COMPARE(image->size(), (Vector2i{4, 3})); } From b9d0a5dca8f2c7302bafe85802187230680208a0 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 18 Aug 2021 22:29:45 +0200 Subject: [PATCH 68/95] KtxImageConverter: disable tests on Emscripten CI We don't have access to the KtxImporter test dir --- package/ci/emscripten.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/ci/emscripten.sh b/package/ci/emscripten.sh index e0e6fd5d4..c34964f3c 100755 --- a/package/ci/emscripten.sh +++ b/package/ci/emscripten.sh @@ -97,7 +97,7 @@ cmake .. \ -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=OFF \ -DWITH_JPEGIMPORTER=OFF \ - -DWITH_KTXIMAGECONVERTER=ON \ + -DWITH_KTXIMAGECONVERTER=OFF \ -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ From 89b9b24c443e50c34119aaa3cc221978a293c199 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 18 Aug 2021 22:40:30 +0200 Subject: [PATCH 69/95] KtxImporter: don't use deprecated TextureData::Type --- .../KtxImporter/Test/KtxImporterTest.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp index 55f3287dc..4ff76af45 100644 --- a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp +++ b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp @@ -645,17 +645,17 @@ void KtxImporterTest::texture() { UnsignedInt dimensions; switch(data.type) { - case TextureData::Type::Texture1D: + case TextureType::Texture1D: dimensions = 1; break; - case TextureData::Type::Texture1DArray: - case TextureData::Type::Texture2D: + case TextureType::Texture1DArray: + case TextureType::Texture2D: dimensions = 2; break; - case TextureData::Type::Texture2DArray: - case TextureData::Type::Texture3D: - case TextureData::Type::CubeMap: - case TextureData::Type::CubeMapArray: + case TextureType::Texture2DArray: + case TextureType::Texture3D: + case TextureType::CubeMap: + case TextureType::CubeMapArray: dimensions = 3; break; default: CORRADE_INTERNAL_ASSERT_UNREACHABLE(); From 357d20014073c48eae8fffb02a498a35938450b4 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 18 Aug 2021 23:57:19 +0200 Subject: [PATCH 70/95] KtxImporter: fix whitespace --- .../KtxImporter/Test/CMakeLists.txt | 128 +++++++++--------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt b/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt index 6ffebb20e..09827361f 100644 --- a/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt @@ -47,70 +47,70 @@ file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h corrade_add_test(KtxImporterTest KtxImporterTest.cpp LIBRARIES Magnum::Trade FILES - 1d-compressed-bc1.bin - 1d-compressed-bc1.ktx2 - 1d-compressed-etc2.bin - 1d-compressed-etc2.ktx2 - 1d-compressed-mipmaps-mip0.bin - 1d-compressed-mipmaps-mip1.bin - 1d-compressed-mipmaps-mip2.bin - 1d-compressed-mipmaps.ktx2 - 1d-layers.ktx2 - 1d-mipmaps.ktx2 - 1d.ktx2 - 2d-compressed-astc.bin - 2d-compressed-astc.ktx2 - 2d-compressed-bc1.bin - 2d-compressed-bc1.ktx2 - 2d-compressed-bc2.bin - 2d-compressed-bc2.ktx2 - 2d-compressed-bc3.bin - 2d-compressed-bc3.ktx2 - 2d-compressed-bc4.bin - 2d-compressed-bc4.ktx2 - 2d-compressed-bc5.bin - 2d-compressed-bc5.ktx2 - 2d-compressed-etc2.bin - 2d-compressed-etc2.ktx2 - 2d-compressed-layers.bin - 2d-compressed-layers.ktx2 - 2d-compressed-mipmaps-mip0.bin - 2d-compressed-mipmaps-mip1.bin - 2d-compressed-mipmaps-mip2.bin - 2d-compressed-mipmaps-mip3.bin - 2d-compressed-mipmaps.ktx2 - 2d-compressed-pvrtc.bin - 2d-compressed-pvrtc.ktx2 - 2d-d16.ktx2 - 2d-d24s8.ktx2 - 2d-d32fs8.ktx2 - 2d-layers.ktx2 - 2d-mipmaps-and-layers.ktx2 - 2d-mipmaps-incomplete.ktx2 - 2d-mipmaps.ktx2 - 2d-rgb.ktx2 - 2d-rgb32.ktx2 - 2d-rgba.ktx2 - 2d-rgbf32.ktx2 - 2d-s8.ktx2 - 3d-compressed.bin - 3d-compressed.ktx2 - 3d-layers.ktx2 - 3d-mipmaps.ktx2 - 3d.ktx2 - bgr-swizzle-bgr-16bit.ktx2 - bgr-swizzle-bgr.ktx2 - bgr.ktx2 - bgra-swizzle-bgra.ktx2 - bgra.ktx2 - cubemap-layers.ktx2 - cubemap-mipmaps.ktx2 - cubemap.ktx2 - swizzle-bgr.ktx2 - swizzle-bgra.ktx2 - swizzle-identity.ktx2 - swizzle-unsupported.ktx2 - version1.ktx) + 1d-compressed-bc1.bin + 1d-compressed-bc1.ktx2 + 1d-compressed-etc2.bin + 1d-compressed-etc2.ktx2 + 1d-compressed-mipmaps-mip0.bin + 1d-compressed-mipmaps-mip1.bin + 1d-compressed-mipmaps-mip2.bin + 1d-compressed-mipmaps.ktx2 + 1d-layers.ktx2 + 1d-mipmaps.ktx2 + 1d.ktx2 + 2d-compressed-astc.bin + 2d-compressed-astc.ktx2 + 2d-compressed-bc1.bin + 2d-compressed-bc1.ktx2 + 2d-compressed-bc2.bin + 2d-compressed-bc2.ktx2 + 2d-compressed-bc3.bin + 2d-compressed-bc3.ktx2 + 2d-compressed-bc4.bin + 2d-compressed-bc4.ktx2 + 2d-compressed-bc5.bin + 2d-compressed-bc5.ktx2 + 2d-compressed-etc2.bin + 2d-compressed-etc2.ktx2 + 2d-compressed-layers.bin + 2d-compressed-layers.ktx2 + 2d-compressed-mipmaps-mip0.bin + 2d-compressed-mipmaps-mip1.bin + 2d-compressed-mipmaps-mip2.bin + 2d-compressed-mipmaps-mip3.bin + 2d-compressed-mipmaps.ktx2 + 2d-compressed-pvrtc.bin + 2d-compressed-pvrtc.ktx2 + 2d-d16.ktx2 + 2d-d24s8.ktx2 + 2d-d32fs8.ktx2 + 2d-layers.ktx2 + 2d-mipmaps-and-layers.ktx2 + 2d-mipmaps-incomplete.ktx2 + 2d-mipmaps.ktx2 + 2d-rgb.ktx2 + 2d-rgb32.ktx2 + 2d-rgba.ktx2 + 2d-rgbf32.ktx2 + 2d-s8.ktx2 + 3d-compressed.bin + 3d-compressed.ktx2 + 3d-layers.ktx2 + 3d-mipmaps.ktx2 + 3d.ktx2 + bgr-swizzle-bgr-16bit.ktx2 + bgr-swizzle-bgr.ktx2 + bgr.ktx2 + bgra-swizzle-bgra.ktx2 + bgra.ktx2 + cubemap-layers.ktx2 + cubemap-mipmaps.ktx2 + cubemap.ktx2 + swizzle-bgr.ktx2 + swizzle-bgra.ktx2 + swizzle-identity.ktx2 + swizzle-unsupported.ktx2 + version1.ktx) target_include_directories(KtxImporterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$ ${PROJECT_SOURCE_DIR}/src) From 31d14844db5afd7a3f6317d536adfd5a13338245 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 18 Aug 2021 23:57:56 +0200 Subject: [PATCH 71/95] KtxImporter: docs++ --- src/MagnumPlugins/KtxImporter/KtxImporter.h | 67 +++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.h b/src/MagnumPlugins/KtxImporter/KtxImporter.h index 716d99dba..e2df69149 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.h +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.h @@ -26,6 +26,11 @@ DEALINGS IN THE SOFTWARE. */ +/** @file + * @brief Class @ref Magnum::Trade::KtxImporter + * @m_since_latest_{plugins} + */ + #include #include "MagnumPlugins/KtxImporter/configure.h" @@ -48,6 +53,68 @@ namespace Magnum { namespace Trade { +/** +@brief KTX2 image importer plugin +@m_since_latest_{plugins} + +Supports Khronos Texture 2.0 images (`*.ktx2`). + +@section Trade-KtxImporter-usage Usage + +This plugin depends on the @ref Trade library and is built if +`WITH_KTXIMPORTER` is enabled when building Magnum Plugins. To use as a dynamic +plugin, load @cpp "KtxImporter" @ce via @ref Corrade::PluginManager::Manager. + +Additionally, if you're using Magnum as a CMake subproject, bundle the +[magnum-plugins repository](https://github.com/mosra/magnum-plugins) and do the +following: + +@code{.cmake} +set(WITH_KTXIMPORTER ON CACHE BOOL "" FORCE) +add_subdirectory(magnum-plugins EXCLUDE_FROM_ALL) + +# So the dynamically loaded plugin gets built implicitly +add_dependencies(your-app MagnumPlugins::KtxImporter) +@endcode + +To use as a static plugin or as a dependency of another plugin with CMake, put +[FindMagnumPlugins.cmake](https://github.com/mosra/magnum-plugins/blob/master/modules/FindMagnumPlugins.cmake) +into your `modules/` directory, request the `KtxImporter` component of the +`MagnumPlugins` package in CMake and link to the `MagnumPlugins::KtxImporter` +target: + +@code{.cmake} +find_package(MagnumPlugins REQUIRED KtxImporter) + +# ... +target_link_libraries(your-app PRIVATE MagnumPlugins::KtxImporter) +@endcode + +See @ref building-plugins, @ref cmake-plugins, @ref plugins and +@ref file-formats for more information. + +@section Trade-KtxImporter-behavior Behavior and limitations + +Imports images in the following formats: + +- KTX2 with all uncompressed Vulkan formats that have an equivalent in + @ref PixelFormat, with component swizzling as necessary +- KTX2 with most compressed Vulkan formats that have an equivalent in + @ref CompressedPixelFormat. None of the 3D ASTC formats are supported. + +With compressed pixel formats, the image will not be flipped if the Y- or Z-axis +orientation doesn't match the output orientation. The nontrivial amount of work +involved with flipping block-compressed data makes this unfeasible. The import +will succeed but a warning will be emitted. + +The importer recognizes @ref ImporterFlag::Verbose, printing additional info +when the flag is enabled. + +@subsection Trade-KtxImporter-behavior-swizzle Swizzle support + +Explicit swizzling via the KTXswizzle header entry supports BGR and BGRA. Any +other non-identity channel remapping is unsupported and results in an error. +*/ class MAGNUM_KTXIMPORTER_EXPORT KtxImporter: public AbstractImporter { public: /** @brief Plugin manager constructor */ From 5d584587864d6c670b94e6a7b1a6436d67e85fa2 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 18 Aug 2021 23:58:19 +0200 Subject: [PATCH 72/95] KtxImageConverter: docs++ --- .../KtxImageConverter/KtxImageConverter.h | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h index 29d65d1ae..f458c0c34 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h @@ -26,6 +26,11 @@ DEALINGS IN THE SOFTWARE. */ +/** @file + * @brief Class @ref Magnum::Trade::KtxImageConverter + * @m_since_latest_{plugins} + */ + #include #include "MagnumPlugins/KtxImageConverter/configure.h" @@ -48,6 +53,76 @@ namespace Magnum { namespace Trade { +/** +@brief KTX2 image converter plugin +@m_since_latest_{plugins} + +Creates Khronos Texture 2.0 (`*.ktx2`) files from images. You can use +@ref KtxImporter to import images in this format. + +@section Trade-KtxImageConverter-usage Usage + +This plugin depends on the @ref Trade library and is built if +`WITH_KTXIMAGECONVERTER` is enabled when building Magnum Plugins. To use as +a dynamic plugin, load @cpp "KtxImageConverter" @ce via +@ref Corrade::PluginManager::Manager. + +Additionally, if you're using Magnum as a CMake subproject, bundle the +[magnum-plugins](https://github.com/mosra/magnum-plugins) and do the following: + +@code{.cmake} +set(WITH_KTXIMAGECONVERTER ON CACHE BOOL "" FORCE) +add_subdirectory(magnum-plugins EXCLUDE_FROM_ALL) + +# So the dynamically loaded plugin gets built implicitly +add_dependencies(your-app MagnumPlugins::KtxImageConverter) +@endcode + +To use as a static plugin or as a dependency of another plugin with CMake, put +[FindMagnumPlugins.cmake](https://github.com/mosra/magnum-plugins/blob/master/modules/FindMagnumPlugins.cmake) +into your `modules/` directory, request the `KtxImageConverter` component +of the `MagnumPlugins` package and link to the +`MagnumPlugins::KtxImageConverter` target: + +@code{.cmake} +find_package(MagnumPlugins REQUIRED KtxImageConverter) + +# ... +target_link_libraries(your-app PRIVATE MagnumPlugins::KtxImageConverter) +@endcode + +See @ref building-plugins, @ref cmake-plugins, @ref plugins and +@ref file-formats for more information. + +@section Trade-KtxImageConverter-behavior Behavior and limitations + +@subsection Trade-KtxImageConverter-behavior-formats Supported formats + +The following formats can be written: + +- all formats in @ref PixelFormat +- all formats in @ref CompressedPixelFormat, except for 3D ASTC formats + +@subsection Trade-KtxImageConverter-behavior-cube-array Cube map and array images + +Cube map and array images can be written but there is currently no way to mark +them properly in the metadata. Exported files will be 3D images with cube map +faces and array layers exposed as depth slices. + +@subsection Trade-KtxImageConverter-behavior-supercompression Supercompression + +Supercompression is not supported. + +@section Trade-KtxImageConverter-configuration Plugin-specific configuration + +It's possible to tune various metadata options through @ref configuration(). +See below for all options and their default values: + +@snippet MagnumPlugins/KtxImageConverter/KtxImageConverter.conf config + +See @ref plugins-configuration for more information and an example showing how +to edit the configuration values. +*/ class MAGNUM_KTXIMAGECONVERTER_EXPORT KtxImageConverter: public AbstractImageConverter { public: /** @brief Plugin manager constructor */ From d704d1157eaf9693a68e5e83b32fc24c333faca4 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Thu, 19 Aug 2021 19:00:53 +0200 Subject: [PATCH 73/95] KtxImporter: docs++ --- src/MagnumPlugins/KtxImporter/KtxImporter.h | 41 +++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.h b/src/MagnumPlugins/KtxImporter/KtxImporter.h index e2df69149..e724b3410 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.h +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.h @@ -110,10 +110,51 @@ will succeed but a warning will be emitted. The importer recognizes @ref ImporterFlag::Verbose, printing additional info when the flag is enabled. +@subsection Trade-KtxImporter-behavior-types Image types + +All image types supported by KTX2 are imported, including 1D, 2D, cube maps, +and 3D images. They can, in turn, all have multiple array layers as well as +multiple mip levels. The image type can be determined from @ref texture() and +@ref TextureData::type(). + +For layered images and (layered) cube maps, the array layers and faces are +exposed as an additional image dimension. 1D array textures import +@ref ImageData2D with n y-slices, (layered) 2D textures and cube maps import +@ref ImageData3D with 6*n z-slices. 3D array textures behave differently: +because there is no `ImageData4D`, each layer is imported as a separate +@ref ImageData3D, with @ref image3DCount() determining the number of layers. + +@subsection Trade-KtxImporter-behavior-multilevel Multilevel images + +Files with multiple mip levels are imported with the largest level first, with +the size of each following level divided by 2, rounded down. Mip chains can be +incomplete, ie. they don't have to extend all the way down to a level of size +1x1. + +@subsection Trade-KtxImporter-behavior-cube Cube maps + +Cube map faces are imported in the order +X, -X, +Y, -Y, +Z, -Z as seen from a +left-handed coordinate system (+X is right, +Y is up, +Z is forward). Layered +cube maps are stored as multiple sets of faces, ie. all faces +X through -Z for +the first layer, then all faces of the second layer, etc. + +Incomplete cube maps (determined by the `KTXcubemapIncomplete` metadata entry) +are imported as a 2D array image, but information about which faces it contains +can't be imported. + +@subsection Trade-KtxImporter-behavior-supercompression Supercompression + +Importing files with [supercompression](https://github.khronos.org/KTX-Specification/#supercompressionSchemes) +is not supported. + @subsection Trade-KtxImporter-behavior-swizzle Swizzle support Explicit swizzling via the KTXswizzle header entry supports BGR and BGRA. Any other non-identity channel remapping is unsupported and results in an error. + +For reasons similar to the restriction on axis-flips, compressed formats don't +support any swizzling, and the import fails if an image with a compressed +format contains a swizzle that isn't RGBA. */ class MAGNUM_KTXIMPORTER_EXPORT KtxImporter: public AbstractImporter { public: From 03af7b915550a69d6530437ae273e68ef93ff343 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Thu, 19 Aug 2021 19:01:06 +0200 Subject: [PATCH 74/95] KtxImageConverter: docs++ --- .../KtxImageConverter/KtxImageConverter.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h index f458c0c34..49c038c2a 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h @@ -109,9 +109,17 @@ Cube map and array images can be written but there is currently no way to mark them properly in the metadata. Exported files will be 3D images with cube map faces and array layers exposed as depth slices. +@subsection Trade-KtxImageConverter-behavior-multilevel Multilevel images + +All image types can be saved with multiple levels by using the list +variants of @ref convertToFile() / @ref convertToData(). Largest level is +expected to be first, with each following level having width and height divided +by two, rounded down. Incomplete mip chains are supported. + @subsection Trade-KtxImageConverter-behavior-supercompression Supercompression -Supercompression is not supported. +Saving files with [supercompression](https://github.khronos.org/KTX-Specification/#supercompressionSchemes) +is not supported. @section Trade-KtxImageConverter-configuration Plugin-specific configuration From c62fd59b83bbbb0c8760ef4659d7ee6c7a7bcafa Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Tue, 24 Aug 2021 19:16:20 +0200 Subject: [PATCH 75/95] KtxImporter: cleanup --- src/MagnumPlugins/KtxImporter/KtxImporter.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp index 5b5f6c303..664dff4ec 100644 --- a/src/MagnumPlugins/KtxImporter/KtxImporter.cpp +++ b/src/MagnumPlugins/KtxImporter/KtxImporter.cpp @@ -83,8 +83,7 @@ enum SwizzleType : UnsignedByte { BGRA }; -inline SwizzleType& operator ^=(SwizzleType& a, SwizzleType b) -{ +inline SwizzleType& operator ^=(SwizzleType& a, SwizzleType b) { /* This is meant to toggle single enum values, make sure it's not being used for other bit-fiddling crimes */ CORRADE_INTERNAL_ASSERT(a == SwizzleType::None || a == b); @@ -308,7 +307,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { return; } } - + Error() << "Trade::KtxImporter::openData(): wrong file signature"; return; } @@ -347,7 +346,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { /* Number of array layers, imported as extra image dimensions (except for 3D images, there it's one Image3D per layer). - layerCount == 1 is a 2D array image with one level, we export it as such + layerCount == 1 is an array image with one level, we export it as such so that there are no surprises. This is equivalent to how we handle depth == 1. */ const bool isLayered = header.layerCount > 0; @@ -476,7 +475,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { if(header.supercompressionScheme == Implementation::SuperCompressionScheme::None && level.byteLength != level.uncompressedByteLength) { - Warning{} << "Trade::KtxImporter::openData(): byte length" << level.byteLength + Warning{} << "Trade::KtxImporter::openData(): byte length" << level.byteLength << "is not equal to uncompressed byte length" << level.uncompressedByteLength << "for an image without supercompression, ignoring the latter"; } @@ -497,7 +496,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { std::size_t imageLength; if(f->pixelFormat.isCompressed) { - const Vector3i blockSize = f->pixelFormat.blockSize; + const Vector3i& blockSize = f->pixelFormat.blockSize; const Vector3i blockCount = (levelSize + (blockSize - Vector3i{1}))/blockSize; imageLength = blockCount.product()*f->pixelFormat.size; } else @@ -515,7 +514,7 @@ void KtxImporter::doOpenData(const Containers::ArrayView data) { f->imageData[image][i] = {levelSize, f->in.suffix(offset).prefix(imageLength)}; } - /* Shrink to next power of 2 */ + /* Halve each dimension, rounding down */ mipSize = Math::max(mipSize >> 1, 1); } From 015eaa9c113794ea8f964bd479e8ad4b2d366310 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Tue, 24 Aug 2021 19:17:52 +0200 Subject: [PATCH 76/95] KtxImageConverter: remove double colon from default writer name --- .../KtxImageConverter/KtxImageConverter.conf | 2 +- .../Test/KtxImageConverterTest.cpp | 2 +- src/MagnumPlugins/KtxImporter/Test/2d-d16.ktx2 | Bin 236 -> 236 bytes .../KtxImporter/Test/2d-d24s8.ktx2 | Bin 276 -> 276 bytes .../KtxImporter/Test/2d-d32fs8.ktx2 | Bin 328 -> 328 bytes src/MagnumPlugins/KtxImporter/Test/2d-s8.ktx2 | Bin 224 -> 224 bytes .../KtxImporter/Test/3d-mipmaps.ktx2 | Bin 432 -> 432 bytes 7 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.conf b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.conf index b27d564fb..2d4ee3401 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.conf +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.conf @@ -11,5 +11,5 @@ orientation=ruo # be empty or 4 characters long, valid characters are r,g,b,a,0,1. swizzle= # Name of the tool writing the image file, saved in the file header -writerName=Magnum::KtxImageConverter +writerName=Magnum KtxImageConverter # [configuration_] diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index d3e694bb2..e9e47c809 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -939,7 +939,7 @@ void KtxImageConverterTest::configurationSwizzleInvalid() { void KtxImageConverterTest::configurationWriterName() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); /* Default value */ - CORRADE_COMPARE(converter->configuration().value("writerName"), "Magnum::KtxImageConverter"); + CORRADE_COMPARE(converter->configuration().value("writerName"), "Magnum KtxImageConverter"); CORRADE_VERIFY(converter->configuration().setValue("writerName", "KtxImageConverterTest&$%1234@\x02\n\r\t\x15!")); const UnsignedByte bytes[4]{}; diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-d16.ktx2 b/src/MagnumPlugins/KtxImporter/Test/2d-d16.ktx2 index 462783271da82da0e5a902ec64bc7c0733cbdbd2..a257d8e18f1334ad1a03bf382002366a84d99841 100644 GIT binary patch delta 71 zcmaFE_=a)98V6+t1_tkti1MP$lGGvw-^BF1(p&}ak_yk<#Pn3>{JgT%B9H_FP;bv;JrM&;Fm|Kj(je|APO8{sRE2)Ev10 delta 71 zcmaFE_=a)98V3~y1_tkti1MP$lGGvw-^BF1(p)Pm?~)47+{E-$=lr~~)FO}!P;bv;JrM&;Fm|Kj(je|APO8{sREAcpVM^ diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-d24s8.ktx2 b/src/MagnumPlugins/KtxImporter/Test/2d-d24s8.ktx2 index a76185663f6667696968f27f5f0ea8a793cd732d..99de518c2e5ecd3300a6e4bdfdd466f83ae7da62 100644 GIT binary patch delta 49 zcmbQjG=*ux9(82~1_tkti1MP$lGGvw-^BF1(p&}ak_yk<#Pn3>{JgT%B9O$y$A$or C4G~iS delta 49 zcmbQjG=*ux9(5H41_tkti1MP$lGGvw-^BF1(p)Pm?~)47+{E-$=lr~~)FPnF#K(pJ Dkz*04 diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-d32fs8.ktx2 b/src/MagnumPlugins/KtxImporter/Test/2d-d32fs8.ktx2 index 33afcc6c0a7218cdd7d2dad143e35b21e82bce9d..70d92acb2b2b497ea9e0bf7a471fe93ddf4e3337 100644 GIT binary patch delta 34 qcmX@Xbb@KZ9#Lfm1_tkti1MP$lGGvw-^BF1(p-g!m-Ht7_XGgRBMh7X delta 35 rcmX@Xbb@KZ9x)XL1_tkti1MP$lGGvw-^BF1(p)R6i5K-IF?s+1()A1T diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-s8.ktx2 b/src/MagnumPlugins/KtxImporter/Test/2d-s8.ktx2 index d89d6d25762dcbff05eb93710af8a3b59b8021c3..7d2b5820eb4664790b610718418d0c203458d92f 100644 GIT binary patch delta 59 zcmaFB_<(W38WUv(1_tkti1MP$lGGvw-^BF1(p&}ak_yk<#Pn3>{JgT%B9H_FBNH@~ From bd776a58c4cccfc515689bf100c210ba1d78f7b2 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Tue, 24 Aug 2021 19:21:47 +0200 Subject: [PATCH 77/95] KtxImageConverter: make samples() constexpr again praying that GCC 4 doesn't explode --- .../KtxImageConverter/KtxImageConverter.cpp | 141 +++++++++--------- 1 file changed, 70 insertions(+), 71 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index f0413b2fc..d22f5d6d0 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -28,7 +28,6 @@ #include #include -#include #include #include #include @@ -198,7 +197,7 @@ struct SampleData { /* For pixel formats where not all channels share the same suffix (only combined depth + stencil for now) we have to specify it manually */ /** @todo Is there a good way to automate this in formatMapping.hpp? */ - Containers::Optional suffix; + Implementation::VkFormatSuffix suffix; }; Containers::Pair> samples(PixelFormat format) { @@ -209,29 +208,26 @@ Containers::Pair fillDataFormatDescriptor(Format format, Implementation:: from the main suffix */ UnsignedByte sampleFormatFlags; Containers::Pair sampleLowerUpper; - if(sampleContent.suffix) { + if(sampleContent.suffix != Implementation::VkFormatSuffix{}) { CORRADE_INTERNAL_ASSERT(!isCompressedFormat); - sampleFormatFlags = channelFormat(*sampleContent.suffix); - sampleLowerUpper = channelMapping(*sampleContent.suffix, sample.bitLength + 1); + sampleFormatFlags = channelFormat(sampleContent.suffix); + sampleLowerUpper = channelMapping(sampleContent.suffix, sample.bitLength + 1); } else { sampleFormatFlags = formatFlags; sampleLowerUpper = lowerUpper; From 33788eeb383b0cf794799082f240805830fe6278 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Tue, 24 Aug 2021 19:22:51 +0200 Subject: [PATCH 78/95] KtxImageConverter: too many levels is an error now, and more helpful --- .../KtxImageConverter/KtxImageConverter.cpp | 9 +++++---- .../KtxImageConverter/Test/KtxImageConverterTest.cpp | 7 +++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index d22f5d6d0..330a07f9d 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -794,10 +794,11 @@ Containers::Array KtxImageConverter::convertLevels(Containers::ArrayView size = imageLevels.front().size(); const UnsignedInt numMipmaps = Math::min(imageLevels.size(), Math::log2(size.max()) + 1); - if(imageLevels.size() > numMipmaps) - Warning{} << "Trade::KtxImageConverter::convertToData(): expected at most" << - numMipmaps << "mip level images but got" << imageLevels.size() << Debug::nospace << - ", extra images will be ignored"; + if(imageLevels.size() > numMipmaps) { + Error{} << "Trade::KtxImageConverter::convertToData(): there can be only" << numMipmaps << + "levels with base image size" << imageLevels[0].size() << "but got" << imageLevels.size(); + return {}; + } Containers::Array levelIndex{numMipmaps}; diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index e9e47c809..e59ac0810 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -489,14 +489,13 @@ void KtxImageConverterTest::tooManyLevels() { const UnsignedByte bytes[4]{}; std::ostringstream out; - Warning redirectWarning{&out}; - CORRADE_VERIFY(converter->convertToData({ + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertToData({ ImageView2D{PixelFormat::RGB8Unorm, {1, 1}, bytes}, ImageView2D{PixelFormat::RGB8Unorm, {1, 1}, bytes} })); CORRADE_COMPARE(out.str(), - "Trade::KtxImageConverter::convertToData(): expected at most 1 mip " - "level images but got 2, extra images will be ignored\n"); + "Trade::KtxImageConverter::convertToData(): there can be only 1 levels with base image size Vector(1, 1) but got 2\n"); } void KtxImageConverterTest::levelWrongSize() { From 4fba2c6311c32493cdb55e22a4342a37bf61deed Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Tue, 24 Aug 2021 19:23:57 +0200 Subject: [PATCH 79/95] KtxImageConverter: make short orientation error more helpful --- src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp | 4 ++-- .../KtxImageConverter/Test/KtxImageConverterTest.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 330a07f9d..fdc266e3d 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -723,8 +723,8 @@ Containers::Array KtxImageConverter::convertLevels(Containers::ArrayView Date: Tue, 24 Aug 2021 19:25:36 +0200 Subject: [PATCH 80/95] KtxImageConverter: move private member template into anonymous namespace --- .../KtxImageConverter/KtxImageConverter.cpp | 48 +++++++++---------- .../KtxImageConverter/KtxImageConverter.h | 4 -- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index fdc266e3d..83f78294a 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -680,24 +680,11 @@ void endianSwap(Containers::ArrayView data, UnsignedInt typeSize) { CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } -} - -KtxImageConverter::KtxImageConverter(PluginManager::AbstractManager& manager, const std::string& plugin): AbstractImageConverter{manager, plugin} {} - -ImageConverterFeatures KtxImageConverter::doFeatures() const { - return ImageConverterFeature::ConvertLevels1DToData | - ImageConverterFeature::ConvertLevels2DToData | - ImageConverterFeature::ConvertLevels3DToData | - ImageConverterFeature::ConvertCompressedLevels1DToData | - ImageConverterFeature::ConvertCompressedLevels2DToData | - ImageConverterFeature::ConvertCompressedLevels3DToData; -} - /* Using a template template parameter to deduce the image dimensions while matching both ImageView and CompressedImageView. Matching on the ImageView typedefs doesn't work, so we need the extra parameter of BasicImageView. */ template class View> -Containers::Array KtxImageConverter::convertLevels(Containers::ArrayView> imageLevels) { +Containers::Array convertLevels(Containers::ArrayView> imageLevels, const Corrade::Utility::ConfigurationGroup& configuration) { const auto format = imageLevels.front().format(); if(isFormatImplementationSpecific(format)) { Error{} << "Trade::KtxImageConverter::convertToData(): implementation-specific formats are not supported"; @@ -717,9 +704,9 @@ Containers::Array KtxImageConverter::convertLevels(Containers::ArrayView KtxImageConverter::convertLevels(Containers::ArrayView KtxImageConverter::convertLevels(Containers::ArrayView KtxImageConverter::doConvertToData(Containers::ArrayView imageLevels) { - return convertLevels(imageLevels); + return convertLevels(imageLevels, configuration()); } Containers::Array KtxImageConverter::doConvertToData(Containers::ArrayView imageLevels) { - return convertLevels(imageLevels); + return convertLevels(imageLevels, configuration()); } Containers::Array KtxImageConverter::doConvertToData(Containers::ArrayView imageLevels) { - return convertLevels(imageLevels); + return convertLevels(imageLevels, configuration()); } Containers::Array KtxImageConverter::doConvertToData(Containers::ArrayView imageLevels) { - return convertLevels(imageLevels); + return convertLevels(imageLevels, configuration()); } Containers::Array KtxImageConverter::doConvertToData(Containers::ArrayView imageLevels) { - return convertLevels(imageLevels); + return convertLevels(imageLevels, configuration()); } Containers::Array KtxImageConverter::doConvertToData(Containers::ArrayView imageLevels) { - return convertLevels(imageLevels); + return convertLevels(imageLevels, configuration()); } }} diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h index 49c038c2a..b532b66e3 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.h @@ -146,10 +146,6 @@ class MAGNUM_KTXIMAGECONVERTER_EXPORT KtxImageConverter: public AbstractImageCon Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(Containers::ArrayView imageLevels) override; Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(Containers::ArrayView imageLevels) override; Containers::Array MAGNUM_KTXIMAGECONVERTER_LOCAL doConvertToData(Containers::ArrayView imageLevels) override; - - private: - template class View> - Containers::Array convertLevels(Containers::ArrayView> imageLevels); }; }} From 47cef7f7f947b1718caff54c7c5a65bbaa277f7e Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Tue, 24 Aug 2021 19:27:37 +0200 Subject: [PATCH 81/95] KtxImageConverter: clarify comments --- src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 83f78294a..3cca01cd8 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -566,7 +566,8 @@ Containers::Array fillDataFormatDescriptor(Format format, Implementation:: The signed channel format flag is still set, however. */ const auto lowerUpper = channelMapping(suffix, mappingBitLength); const UnsignedByte formatFlags = channelFormat(suffix); - /* For non-compressed RGBA channels, we get */ + /* For non-compressed RGBA channels, we get the 1-byte channel data + and then multiply by the actual typeSize in the loop below */ const UnsignedByte bitRangeMultiplier = isDepthStencil ? 1 : typeSize; UnsignedShort extent = 0; @@ -798,7 +799,8 @@ Containers::Array convertLevels(Containers::ArrayView mipSize = Math::max(size >> mip, 1); From 53ecb2f745e00f8b6cc0c59239bbcf3c39f3cdf8 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Tue, 24 Aug 2021 19:28:09 +0200 Subject: [PATCH 82/95] KtxImageConverter: make codecov happy --- .../KtxImageConverter/KtxImageConverter.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 3cca01cd8..921973973 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -183,7 +183,7 @@ UnsignedByte formatTypeSize(PixelFormat format) { return 4; } - CORRADE_ASSERT_UNREACHABLE("componentSize(): unsupported format" << format, {}); + CORRADE_ASSERT_UNREACHABLE("componentSize(): unsupported format" << format, {}); /* LCOV_EXCL_LINE */ } UnsignedByte formatTypeSize(CompressedPixelFormat) { @@ -437,10 +437,11 @@ Containers::Pair channelMapping(Implementation::VkFormatSuffix suffix, UnsignedInt bitLength) { @@ -500,7 +501,7 @@ Containers::Pair channelMapping(Implementation::VkForm return {Corrade::Utility::bitCast(-1.0f), Corrade::Utility::bitCast(1.0f)}; } - CORRADE_ASSERT_UNREACHABLE("channelMapping(): invalid format suffix" << UnsignedInt(suffix), {}); + CORRADE_ASSERT_UNREACHABLE("channelMapping(): invalid format suffix" << UnsignedInt(suffix), {}); /* LCOV_EXCL_LINE */ } template From 2cc1f2976f5d610a2c5200f5c7d03d2b47ec0c87 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Tue, 24 Aug 2021 19:28:28 +0200 Subject: [PATCH 83/95] KtxImageConverter: cleanup --- .../KtxImageConverter/KtxImageConverter.cpp | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 921973973..b5075049c 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -484,13 +485,12 @@ Containers::Pair channelMapping(Implementation::VkForm case Implementation::VkFormatSuffix::UNORM: case Implementation::VkFormatSuffix::SRGB: return {0u, typeMask}; - case Implementation::VkFormatSuffix::SNORM: - { + case Implementation::VkFormatSuffix::SNORM: { /* Remove sign bit to get largest positive value. If we flip the bits of that, we get the sign-extended lowest negative value. */ const UnsignedInt positiveTypeMask = typeMask >> 1; return {~positiveTypeMask, positiveTypeMask}; - } + } case Implementation::VkFormatSuffix::UINT: return {0u, 1u}; case Implementation::VkFormatSuffix::SINT: @@ -557,7 +557,7 @@ Containers::Array fillDataFormatDescriptor(Format format, Implementation:: constexpr bool isCompressedFormat = std::is_same::value; const bool isDepthStencil = !isCompressedFormat && sampleData.second().front().id != Implementation::KdfBasicBlockSample::ChannelId::Red; - + const UnsignedByte typeSize = formatTypeSize(format); /* Compressed integer formats must use 32-bit lower/upper */ const UnsignedByte mappingBitLength = (isCompressedFormat ? sizeof(UnsignedInt) : typeSize)*8; @@ -615,12 +615,12 @@ Containers::Array fillDataFormatDescriptor(Format format, Implementation:: pixel size, e.g. Depth24Unorm. */ CORRADE_INTERNAL_ASSERT(extent <= unitDataSize*8); + CORRADE_INTERNAL_ASSERT(offset == dfdSize); + Utility::Endianness::littleEndianInPlace(length); Utility::Endianness::littleEndianInPlace(header.vendorId, header.descriptorType, header.versionNumber, header.descriptorBlockSize); - CORRADE_INTERNAL_ASSERT(offset == dfdSize); - return data; } @@ -640,14 +640,9 @@ UnsignedInt leastCommonMultiple(UnsignedInt a, UnsignedInt b) { template void copyPixels(const BasicImageView& image, Containers::ArrayView pixels) { - std::size_t sizes[dimensions + 1]; - sizes[dimensions] = image.pixelSize(); - for(UnsignedInt i = 0; i != dimensions; ++i) { - sizes[dimensions - 1 - i] = image.size()[i]; - } - /* Copy the pixels into output, dropping padding (if any) */ - Utility::copy(image.pixels(), Containers::StridedArrayView{pixels, sizes}); + const Containers::StridedArrayView srcPixels = image.pixels(); + Utility::copy(srcPixels, Containers::StridedArrayView{pixels, srcPixels.size()}); } template @@ -736,13 +731,11 @@ Containers::Array convertLevels(Containers::ArrayView keyValueMap[]{ - /* Origin left, bottom, back (increasing right, up, out) */ Containers::pair("KTXorientation"_s, Containers::StringView{orientation}.prefix(Math::min(size_t(dimensions), orientation.size()))), Containers::pair("KTXswizzle"_s, Containers::StringView{swizzle}), Containers::pair("KTXwriter"_s, Containers::StringView{writerName}) @@ -873,7 +866,7 @@ Containers::Array convertLevels(Containers::ArrayView Date: Tue, 24 Aug 2021 20:29:44 +0200 Subject: [PATCH 84/95] KtxImageConverter: don't test unsupported format messages friendly reminder to implement support --- .../KtxImageConverter/Test/KtxImageConverterTest.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index 9253fff22..583566baa 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -413,16 +413,10 @@ void KtxImageConverterTest::unsupportedCompressedFormat() { CORRADE_ITERATION(format); CORRADE_INTERNAL_ASSERT(Containers::arraySize(bytes) >= compressedBlockDataSize(CompressedPixelFormat(format))); - std::ostringstream out; - Error redirectError{&out}; CORRADE_VERIFY(!converter->convertToData(CompressedImageView2D{format, {1, 1}, bytes})); - /** @todo Is there a better way to do this? */ - std::ostringstream formattedOut; - Debug redirectDebug{&formattedOut}; - Debug{} << "Trade::KtxImageConverter::convertToData(): unsupported format" << format; - - CORRADE_COMPARE(out.str(), formattedOut.str()); + /* Not testing the output message so that it shows up as a friendly + nagging reminder to add support for these formats */ } } From 256d392b0c7c8f94cd316aca2486660595d1768f Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 25 Aug 2021 04:34:49 +0200 Subject: [PATCH 85/95] Ktx{Importer, ImageConverter}: test compressed 3D images with mipmaps Similarly to uncompressed mipmapped 3D images, we have to generate them with the converter tests. We, however, have to manually generate the .bin data with compressed blocks. Not ideal, but better than not testing these at all. --- .../Test/KtxImageConverterTest.cpp | 40 +++++++++++++++--- .../Test/3d-compressed-mipmaps-mip0.bin | Bin 0 -> 360 bytes .../Test/3d-compressed-mipmaps-mip1.bin | Bin 0 -> 32 bytes .../Test/3d-compressed-mipmaps-mip2.bin | Bin 0 -> 8 bytes .../Test/3d-compressed-mipmaps-mip3.bin | Bin 0 -> 8 bytes .../Test/3d-compressed-mipmaps.ktx2 | Bin 0 -> 696 bytes .../KtxImporter/Test/KtxImporterTest.cpp | 34 ++++++++++++++- .../KtxImporter/Test/generate.sh | 24 ++++++++++- 8 files changed, 90 insertions(+), 8 deletions(-) create mode 100644 src/MagnumPlugins/KtxImporter/Test/3d-compressed-mipmaps-mip0.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/3d-compressed-mipmaps-mip1.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/3d-compressed-mipmaps-mip2.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/3d-compressed-mipmaps-mip3.bin create mode 100644 src/MagnumPlugins/KtxImporter/Test/3d-compressed-mipmaps.ktx2 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index 583566baa..c51304a8b 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -84,10 +84,7 @@ struct KtxImageConverterTest: TestSuite::Tester { void convert3D(); void convert3DMipmaps(); void convert3DCompressed(); - /** @todo 3D compressed + mipmaps. We could generate it here, but we can't - verify the output is correct using an external viewer. Revisit - once toktx or PVRTexTool support generating these? - Then again, we're doing it for depth-stencil formats, too... */ + void convert3DCompressedMipmaps(); /** @todo Add tests for cube and layered (and combined) images once the converter supports those */ @@ -292,7 +289,8 @@ KtxImageConverterTest::KtxImageConverterTest() { addTests({&KtxImageConverterTest::convert2DCompressedMipmaps, &KtxImageConverterTest::convert3D, &KtxImageConverterTest::convert3DMipmaps, - &KtxImageConverterTest::convert3DCompressed}); + &KtxImageConverterTest::convert3DCompressed, + &KtxImageConverterTest::convert3DCompressedMipmaps}); addInstancedTests({&KtxImageConverterTest::convertFormats}, Containers::arraySize(ConvertFormatsData)); @@ -765,6 +763,38 @@ void KtxImageConverterTest::convert3DCompressed() { CORRADE_COMPARE_AS(output, expected, TestSuite::Compare::Container); } +void KtxImageConverterTest::convert3DCompressedMipmaps() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + converter->configuration().setValue("orientation", "rdi"); + + /* Same as convert3DMipmaps, we generate this file here because none of the + tools can do it. The other compressed .bin data is extracted from files + created by toktx/PVRTexTool. In this case we handishly created data from + existing 2D ETC2 data. Oh well, better than nothing until there's a + better way to generate these images. */ + + constexpr Vector3i size{9, 10, 5}; + const auto mip0 = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d-compressed-mipmaps-mip0.bin")); + const auto mip1 = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d-compressed-mipmaps-mip1.bin")); + const auto mip2 = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d-compressed-mipmaps-mip2.bin")); + const auto mip3 = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d-compressed-mipmaps-mip3.bin")); + + const CompressedImageView3D inputImages[4]{ + CompressedImageView3D{CompressedPixelFormat::Etc2RGB8Srgb, Math::max(size >> 0, 1), mip0}, + CompressedImageView3D{CompressedPixelFormat::Etc2RGB8Srgb, Math::max(size >> 1, 1), mip1}, + CompressedImageView3D{CompressedPixelFormat::Etc2RGB8Srgb, Math::max(size >> 2, 1), mip2}, + CompressedImageView3D{CompressedPixelFormat::Etc2RGB8Srgb, Math::max(size >> 3, 1), mip3} + }; + + const auto output = converter->convertToData(inputImages); + CORRADE_VERIFY(output); + + const auto expected = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d-compressed-mipmaps.ktx2")); + CORRADE_COMPARE_AS(std::string(output.data(), output.size()), + Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d-compressed-mipmaps.ktx2"), + TestSuite::Compare::StringToFile); +} + void KtxImageConverterTest::convertFormats() { auto&& data = ConvertFormatsData[testCaseInstanceId()]; setTestCaseDescription(data.name); diff --git a/src/MagnumPlugins/KtxImporter/Test/3d-compressed-mipmaps-mip0.bin b/src/MagnumPlugins/KtxImporter/Test/3d-compressed-mipmaps-mip0.bin new file mode 100644 index 0000000000000000000000000000000000000000..c221dfab1ad2656b04805249091ef81ffe3c3127 GIT binary patch literal 360 zcmb0|Nsi5OV@C7Z9@oF$jRfSimd>h7CYk2Z-+gu>%mJg8-0X zAjm+Y4WN7luq*=ujMjjPb3nymv;q!skli4_#Gt{jkdcXjnVEqBriX#So(Uq$!0;ak zgn=BO_wtJ}Q}ap^OEUBG7>ZId85ooy;^jq|C8eWhOx importer = _manager.instantiate("KtxImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "3d-compressed-mipmaps.ktx2"))); + + CORRADE_COMPARE(importer->image3DCount(), 1); + CORRADE_COMPARE(importer->image3DLevelCount(0), 4); + + Vector3i mipSize{9, 10, 5}; + for(UnsignedInt i = 0; i != importer->image3DLevelCount(0); ++i) { + CORRADE_ITERATION(i); + + auto image = importer->image3D(0, i); + CORRADE_VERIFY(image); + + CORRADE_VERIFY(image->isCompressed()); + CORRADE_COMPARE(image->compressedFormat(), CompressedPixelFormat::Etc2RGB8Srgb); + CORRADE_COMPARE(image->size(), mipSize); + + const Vector3i blockSize = compressedBlockSize(image->compressedFormat()); + const Vector3i blockCount = (mipSize + (blockSize - Vector3i{1}))/blockSize; + CORRADE_COMPARE(image->data().size(), blockCount.product()*compressedBlockDataSize(image->compressedFormat())); + /* Compressed .bin data is manually generated in generate.sh, don't + need to save it like the 1D/2D files */ + const auto data = Utility::Directory::read( + Utility::Directory::join(KTXIMPORTER_TEST_DIR, Utility::formatString("3d-compressed-mipmaps-mip{}.bin", i))); + CORRADE_COMPARE_AS(image->data(), data, TestSuite::Compare::Container); + + mipSize = Math::max(mipSize >> 1, 1); + } +} + void KtxImporterTest::keyValueDataEmpty() { Containers::Pointer importer = _manager.instantiate("KtxImporter"); auto fileData = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-rgb.ktx2")); diff --git a/src/MagnumPlugins/KtxImporter/Test/generate.sh b/src/MagnumPlugins/KtxImporter/Test/generate.sh index e1027a5a0..4ad0fcc0f 100644 --- a/src/MagnumPlugins/KtxImporter/Test/generate.sh +++ b/src/MagnumPlugins/KtxImporter/Test/generate.sh @@ -80,7 +80,8 @@ printf 'i' | dd conv=notrunc of=3d.ktx2 bs=1 seek=201 # We can't patch a 2D array texture with mipmaps into a 3D texture because the # number of layers stays the same, unlike shrinking z in mipmap levels -# 3d-mipmaps.ktx2 is generated by running KtxImageConverterTest --save-diagnostic +# 3d-mipmaps.ktx2 and 3d-compressed-mipmaps.ktx2 are generated by running +# KtxImageConverterTest --save-diagnostic PVRTexToolCLI -i black.png,pattern.png,pattern.png,black.png,black.png,pattern.png -o 3d-layers.ktx2 -array -f r8g8b8,UBN,sRGB printf '\x03\x00\x00\x00\x02\x00\x00\x00' | dd conv=notrunc of=3d-layers.ktx2 bs=1 seek=28 # TODO: patch up KTXorientation for 3d-layers.ktx2 if we need it for the converter tests @@ -111,6 +112,25 @@ printf '\x03\x00\x00\x00\x00\x00\x00\x00' | dd conv=notrunc of=3d-compressed.ktx printf '\x13' | dd conv=notrunc of=3d-compressed.ktx2 bs=1 seek=148 printf 'i' | dd conv=notrunc of=3d-compressed.ktx2 bs=1 seek=169 +# Unlike 3d-mipmaps.ktx2, we don't have any verifiable data to check in viewers +# for 3d-compressed-mipmaps.ktx to make sure the converter output is plausible. +# Instead of randomly generating bytes, some of the existing 2D ETC2 .bin files +# are used to manually create the mipmaps. Those are the only files that aren't +# generated by either this script or by KtxImporter/KtxImageConvert with +# --save-diagnostic. Horrible hack, but it's better than nothing. +# We can simply repeat all the blocks for each z-slice. +# Last slice (if any) is all zeros to check the order. +yes 2d-compressed-mipmaps-mip0.bin | head -n 4 | xargs cat > 3d-compressed-mipmaps-mip0.bin +size=$(stat -c%s 2d-compressed-mipmaps-mip0.bin) +dd if=/dev/zero bs=1 count=$size >> 3d-compressed-mipmaps-mip0.bin + +cp 2d-compressed-mipmaps-mip1.bin 3d-compressed-mipmaps-mip1.bin +size=$(stat -c%s 2d-compressed-mipmaps-mip1.bin) +dd if=/dev/zero bs=1 count=$size >> 3d-compressed-mipmaps-mip1.bin + +cp 2d-compressed-mipmaps-mip2.bin 3d-compressed-mipmaps-mip2.bin +cp 2d-compressed-mipmaps-mip3.bin 3d-compressed-mipmaps-mip3.bin + # TODO: -# 3D mips so we don't have to generate our own in the converter tests +# 3D (compressed) mips so we don't have to generate our own in the converter tests # Should be possible once https://github.com/KhronosGroup/KTX-Software/pull/468 made it into a release From a85db6d0b6391c17aec1dcb51f680c9ea84007e7 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 25 Aug 2021 04:36:15 +0200 Subject: [PATCH 86/95] KtxImageConverter: correctly test compressed 2D images with mipmaps oops --- .../Test/KtxImageConverterTest.cpp | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index c51304a8b..fcc2bc95b 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -679,26 +679,25 @@ void KtxImageConverterTest::convert2DCompressed() { void KtxImageConverterTest::convert2DCompressedMipmaps() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); converter->configuration().setValue("orientation", "rd"); - converter->configuration().setValue("writerName", WriterToktx); - - const Vector2i size{4, 3}; - const auto mip0 = Containers::arrayCast(Containers::arrayView( - PatternRgbData[Containers::arraySize(PatternRgbData) - 1])); - const Color3ub mip1[2]{0xffffff_rgb, 0x007f7f_rgb}; - const Color3ub mip2[1]{0x000000_rgb}; + converter->configuration().setValue("writerName", WriterPVRTexTool); - PixelStorage storage; - storage.setAlignment(1); - const ImageView2D inputImages[3]{ - ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 0, 1), mip0}, - ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 1, 1), mip1}, - ImageView2D{storage, PixelFormat::RGB8Srgb, Math::max(size >> 2, 1), mip2} + constexpr Vector2i size{9, 10}; + const auto mip0 = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-compressed-mipmaps-mip0.bin")); + const auto mip1 = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-compressed-mipmaps-mip1.bin")); + const auto mip2 = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-compressed-mipmaps-mip2.bin")); + const auto mip3 = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-compressed-mipmaps-mip3.bin")); + + const CompressedImageView2D inputImages[4]{ + CompressedImageView2D{CompressedPixelFormat::Etc2RGB8Srgb, Math::max(size >> 0, 1), mip0}, + CompressedImageView2D{CompressedPixelFormat::Etc2RGB8Srgb, Math::max(size >> 1, 1), mip1}, + CompressedImageView2D{CompressedPixelFormat::Etc2RGB8Srgb, Math::max(size >> 2, 1), mip2}, + CompressedImageView2D{CompressedPixelFormat::Etc2RGB8Srgb, Math::max(size >> 3, 1), mip3} }; const auto output = converter->convertToData(inputImages); CORRADE_VERIFY(output); - const auto expected = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-mipmaps.ktx2")); + const auto expected = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "2d-compressed-mipmaps.ktx2")); CORRADE_COMPARE_AS(output, expected, TestSuite::Compare::Container); } From a63a549fccaa586bf832888ab4090636d08fa68c Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 25 Aug 2021 16:26:30 +0200 Subject: [PATCH 87/95] KtxImageConverter: fix DFD for signed normalized and combined depth-stencil formats --- .../KtxImageConverter/KtxImageConverter.cpp | 40 ++++++++++++------ .../KtxImporter/Test/2d-d32fs8.ktx2 | Bin 328 -> 328 bytes 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index b5075049c..040dcebad 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -467,7 +467,7 @@ UnsignedByte channelFormat(Implementation::VkFormatSuffix suffix) { CORRADE_ASSERT_UNREACHABLE("channelFormat(): invalid format suffix" << UnsignedInt(suffix), {}); /* LCOV_EXCL_LINE */ } -Containers::Pair channelMapping(Implementation::VkFormatSuffix suffix, UnsignedInt bitLength) { +Containers::Pair channelMapping(Implementation::VkFormatSuffix suffix, UnsignedInt bitLength, bool isCompressed) { /* sampleLower and sampleUpper define how to interpret the range of values found in a channel. samplerLower = black value or -1 for signed values @@ -487,9 +487,12 @@ Containers::Pair channelMapping(Implementation::VkForm return {0u, typeMask}; case Implementation::VkFormatSuffix::SNORM: { /* Remove sign bit to get largest positive value. If we flip the - bits of that, we get the sign-extended lowest negative value. */ + bits of that, we get the sign-extended smallest negative value. */ const UnsignedInt positiveTypeMask = typeMask >> 1; - return {~positiveTypeMask, positiveTypeMask}; + /* Uncompressed formats need -MAX (= MIN + 1) for symmetry around 0 + but block-compressed formats need INT32_MIN according to the + KDF spec. */ + return {~positiveTypeMask + UnsignedInt(!isCompressed), positiveTypeMask}; } case Implementation::VkFormatSuffix::UINT: return {0u, 1u}; @@ -513,7 +516,7 @@ Containers::Array fillDataFormatDescriptor(Format format, Implementation:: const std::size_t dfdSamplesSize = sampleData.second().size()*sizeof(Implementation::KdfBasicBlockSample); const std::size_t dfdBlockSize = sizeof(Implementation::KdfBasicBlockHeader) + dfdSamplesSize; const std::size_t dfdSize = sizeof(UnsignedInt) + dfdBlockSize; - CORRADE_INTERNAL_ASSERT(dfdSize % 4 == 0); + CORRADE_INTERNAL_ASSERT(dfdSize%4 == 0); Containers::Array data{ValueInit, dfdSize}; std::size_t offset = 0; @@ -548,7 +551,6 @@ Containers::Array fillDataFormatDescriptor(Format format, Implementation:: if(unitSize[i] > 1) header.texelBlockDimension[i] = unitSize[i] - 1; } - header.bytesPlane[0] = unitDataSize; /* Sample blocks, one per channel */ auto samples = Containers::arrayCast(data.suffix(offset)); @@ -562,10 +564,9 @@ Containers::Array fillDataFormatDescriptor(Format format, Implementation:: /* Compressed integer formats must use 32-bit lower/upper */ const UnsignedByte mappingBitLength = (isCompressedFormat ? sizeof(UnsignedInt) : typeSize)*8; /* @todo BC6h has unsigned floats, but the spec says to use a sampleLower - of -1.0. Is this an error? - https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#bc6h_channel - The signed channel format flag is still set, however. */ - const auto lowerUpper = channelMapping(suffix, mappingBitLength); + of -1.0. The signed channel format flag is still set, however. + See https://github.com/KhronosGroup/DataFormat/issues/16 */ + const auto lowerUpper = channelMapping(suffix, mappingBitLength, isCompressedFormat); const UnsignedByte formatFlags = channelFormat(suffix); /* For non-compressed RGBA channels, we get the 1-byte channel data and then multiply by the actual typeSize in the loop below */ @@ -586,7 +587,7 @@ Containers::Array fillDataFormatDescriptor(Format format, Implementation:: if(sampleContent.suffix != Implementation::VkFormatSuffix{}) { CORRADE_INTERNAL_ASSERT(!isCompressedFormat); sampleFormatFlags = channelFormat(sampleContent.suffix); - sampleLowerUpper = channelMapping(sampleContent.suffix, sample.bitLength + 1); + sampleLowerUpper = channelMapping(sampleContent.suffix, sample.bitLength + 1, isCompressedFormat); } else { sampleFormatFlags = formatFlags; sampleLowerUpper = lowerUpper; @@ -612,9 +613,22 @@ Containers::Array fillDataFormatDescriptor(Format format, Implementation:: /* Make sure channel bit ranges returned by samples() are plausible. Can't use equals because some formats have channels smaller than the - pixel size, e.g. Depth24Unorm. */ + pixel size (mainly the combined depth formats). */ + CORRADE_INTERNAL_ASSERT(extent%8 == 0); CORRADE_INTERNAL_ASSERT(extent <= unitDataSize*8); + /* The byte count is the actual occupied number of bytes. For most formats + this is equal to unitDataSize, but for some formats with different-sized + channels it can be less (e.g. Depth16UnormStencil8UI). Depth24Unorm is + an odd exception because as far as Vulkan is concerned, it's a packed + type (_PACK32), so the byte count is 4, not 3. The check below works + because Depth24Unorm is the only single-channel format where + extent/8 < unitDataSize. */ + if(samples.size() > 1) + header.bytesPlane[0] = extent/8; + else + header.bytesPlane[0] = unitDataSize; + CORRADE_INTERNAL_ASSERT(offset == dfdSize); Utility::Endianness::littleEndianInPlace(length); @@ -629,7 +643,7 @@ UnsignedInt leastCommonMultiple(UnsignedInt a, UnsignedInt b) { /* Greatest common divisor */ while(b != 0) { - const UnsignedInt t = a % b; + const UnsignedInt t = a%b; a = b; b = t; } @@ -750,7 +764,7 @@ Containers::Array convertLevels(Containers::ArrayView Date: Wed, 25 Aug 2021 16:29:04 +0200 Subject: [PATCH 88/95] KtxImageConverter: test DFD against output of dfdutils Raw DFD data is bundled with the test, generated from a patch to a dfdutils program, see README.md --- .../KtxImageConverter/Test/CMakeLists.txt | 2 + .../Test/KtxImageConverterTest.cpp | 147 ++++++++++++++---- .../KtxImageConverter/Test/README.md | 15 ++ .../KtxImageConverter/Test/configure.h.cmake | 1 + ..._VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG.dfd | Bin 0 -> 44 bytes ..._VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG.dfd | Bin 0 -> 44 bytes ...4_VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG.dfd | Bin 0 -> 44 bytes ...5_VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG.dfd | Bin 0 -> 44 bytes ...00_VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT.dfd | Bin 0 -> 44 bytes ...01_VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT.dfd | Bin 0 -> 44 bytes ...02_VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT.dfd | Bin 0 -> 44 bytes ...03_VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT.dfd | Bin 0 -> 44 bytes ...04_VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT.dfd | Bin 0 -> 44 bytes ...05_VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT.dfd | Bin 0 -> 44 bytes ...06_VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT.dfd | Bin 0 -> 44 bytes ...07_VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT.dfd | Bin 0 -> 44 bytes ...8_VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT.dfd | Bin 0 -> 44 bytes ...9_VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT.dfd | Bin 0 -> 44 bytes ...0_VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT.dfd | Bin 0 -> 44 bytes ..._VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT.dfd | Bin 0 -> 44 bytes ..._VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT.dfd | Bin 0 -> 44 bytes ..._VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT.dfd | Bin 0 -> 44 bytes .../Test/dfd/100_VK_FORMAT_R32_SFLOAT.dfd | Bin 0 -> 44 bytes .../Test/dfd/101_VK_FORMAT_R32G32_UINT.dfd | Bin 0 -> 60 bytes .../Test/dfd/102_VK_FORMAT_R32G32_SINT.dfd | Bin 0 -> 60 bytes .../Test/dfd/103_VK_FORMAT_R32G32_SFLOAT.dfd | Bin 0 -> 60 bytes .../Test/dfd/104_VK_FORMAT_R32G32B32_UINT.dfd | Bin 0 -> 76 bytes .../Test/dfd/105_VK_FORMAT_R32G32B32_SINT.dfd | Bin 0 -> 76 bytes .../dfd/106_VK_FORMAT_R32G32B32_SFLOAT.dfd | Bin 0 -> 76 bytes .../dfd/107_VK_FORMAT_R32G32B32A32_UINT.dfd | Bin 0 -> 92 bytes .../dfd/108_VK_FORMAT_R32G32B32A32_SINT.dfd | Bin 0 -> 92 bytes .../dfd/109_VK_FORMAT_R32G32B32A32_SFLOAT.dfd | Bin 0 -> 92 bytes .../Test/dfd/10_VK_FORMAT_R8_SNORM.dfd | Bin 0 -> 44 bytes .../Test/dfd/124_VK_FORMAT_D16_UNORM.dfd | Bin 0 -> 44 bytes .../dfd/125_VK_FORMAT_X8_D24_UNORM_PACK32.dfd | Bin 0 -> 44 bytes .../Test/dfd/126_VK_FORMAT_D32_SFLOAT.dfd | Bin 0 -> 44 bytes .../Test/dfd/127_VK_FORMAT_S8_UINT.dfd | Bin 0 -> 44 bytes .../dfd/128_VK_FORMAT_D16_UNORM_S8_UINT.dfd | Bin 0 -> 60 bytes .../dfd/129_VK_FORMAT_D24_UNORM_S8_UINT.dfd | Bin 0 -> 60 bytes .../dfd/130_VK_FORMAT_D32_SFLOAT_S8_UINT.dfd | Bin 0 -> 60 bytes .../dfd/131_VK_FORMAT_BC1_RGB_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../dfd/132_VK_FORMAT_BC1_RGB_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes .../133_VK_FORMAT_BC1_RGBA_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../dfd/134_VK_FORMAT_BC1_RGBA_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes .../dfd/135_VK_FORMAT_BC2_UNORM_BLOCK.dfd | Bin 0 -> 60 bytes .../Test/dfd/136_VK_FORMAT_BC2_SRGB_BLOCK.dfd | Bin 0 -> 60 bytes .../dfd/137_VK_FORMAT_BC3_UNORM_BLOCK.dfd | Bin 0 -> 60 bytes .../Test/dfd/138_VK_FORMAT_BC3_SRGB_BLOCK.dfd | Bin 0 -> 60 bytes .../dfd/139_VK_FORMAT_BC4_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../Test/dfd/13_VK_FORMAT_R8_UINT.dfd | Bin 0 -> 44 bytes .../dfd/140_VK_FORMAT_BC4_SNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../dfd/141_VK_FORMAT_BC5_UNORM_BLOCK.dfd | Bin 0 -> 60 bytes .../dfd/142_VK_FORMAT_BC5_SNORM_BLOCK.dfd | Bin 0 -> 60 bytes .../dfd/143_VK_FORMAT_BC6H_UFLOAT_BLOCK.dfd | Bin 0 -> 44 bytes .../dfd/144_VK_FORMAT_BC6H_SFLOAT_BLOCK.dfd | Bin 0 -> 44 bytes .../dfd/145_VK_FORMAT_BC7_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../Test/dfd/146_VK_FORMAT_BC7_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes .../147_VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../148_VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes ...49_VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK.dfd | Bin 0 -> 60 bytes .../Test/dfd/14_VK_FORMAT_R8_SINT.dfd | Bin 0 -> 44 bytes ...150_VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK.dfd | Bin 0 -> 60 bytes ...51_VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK.dfd | Bin 0 -> 60 bytes ...152_VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK.dfd | Bin 0 -> 60 bytes .../dfd/153_VK_FORMAT_EAC_R11_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../dfd/154_VK_FORMAT_EAC_R11_SNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../155_VK_FORMAT_EAC_R11G11_UNORM_BLOCK.dfd | Bin 0 -> 60 bytes .../156_VK_FORMAT_EAC_R11G11_SNORM_BLOCK.dfd | Bin 0 -> 60 bytes .../157_VK_FORMAT_ASTC_4x4_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../dfd/158_VK_FORMAT_ASTC_4x4_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes .../159_VK_FORMAT_ASTC_5x4_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../Test/dfd/15_VK_FORMAT_R8_SRGB.dfd | Bin 0 -> 44 bytes .../dfd/160_VK_FORMAT_ASTC_5x4_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes .../161_VK_FORMAT_ASTC_5x5_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../dfd/162_VK_FORMAT_ASTC_5x5_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes .../163_VK_FORMAT_ASTC_6x5_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../dfd/164_VK_FORMAT_ASTC_6x5_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes .../165_VK_FORMAT_ASTC_6x6_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../dfd/166_VK_FORMAT_ASTC_6x6_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes .../167_VK_FORMAT_ASTC_8x5_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../dfd/168_VK_FORMAT_ASTC_8x5_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes .../169_VK_FORMAT_ASTC_8x6_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../Test/dfd/16_VK_FORMAT_R8G8_UNORM.dfd | Bin 0 -> 60 bytes .../dfd/170_VK_FORMAT_ASTC_8x6_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes .../171_VK_FORMAT_ASTC_8x8_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../dfd/172_VK_FORMAT_ASTC_8x8_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes .../173_VK_FORMAT_ASTC_10x5_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../174_VK_FORMAT_ASTC_10x5_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes .../175_VK_FORMAT_ASTC_10x6_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../176_VK_FORMAT_ASTC_10x6_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes .../177_VK_FORMAT_ASTC_10x8_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../178_VK_FORMAT_ASTC_10x8_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes .../179_VK_FORMAT_ASTC_10x10_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../Test/dfd/17_VK_FORMAT_R8G8_SNORM.dfd | Bin 0 -> 60 bytes .../180_VK_FORMAT_ASTC_10x10_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes .../181_VK_FORMAT_ASTC_12x10_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../182_VK_FORMAT_ASTC_12x10_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes .../183_VK_FORMAT_ASTC_12x12_UNORM_BLOCK.dfd | Bin 0 -> 44 bytes .../184_VK_FORMAT_ASTC_12x12_SRGB_BLOCK.dfd | Bin 0 -> 44 bytes .../Test/dfd/20_VK_FORMAT_R8G8_UINT.dfd | Bin 0 -> 60 bytes .../Test/dfd/21_VK_FORMAT_R8G8_SINT.dfd | Bin 0 -> 60 bytes .../Test/dfd/22_VK_FORMAT_R8G8_SRGB.dfd | Bin 0 -> 60 bytes .../Test/dfd/23_VK_FORMAT_R8G8B8_UNORM.dfd | Bin 0 -> 76 bytes .../Test/dfd/24_VK_FORMAT_R8G8B8_SNORM.dfd | Bin 0 -> 76 bytes .../Test/dfd/27_VK_FORMAT_R8G8B8_UINT.dfd | Bin 0 -> 76 bytes .../Test/dfd/28_VK_FORMAT_R8G8B8_SINT.dfd | Bin 0 -> 76 bytes .../Test/dfd/29_VK_FORMAT_R8G8B8_SRGB.dfd | Bin 0 -> 76 bytes .../Test/dfd/37_VK_FORMAT_R8G8B8A8_UNORM.dfd | Bin 0 -> 92 bytes .../Test/dfd/38_VK_FORMAT_R8G8B8A8_SNORM.dfd | Bin 0 -> 92 bytes .../Test/dfd/41_VK_FORMAT_R8G8B8A8_UINT.dfd | Bin 0 -> 92 bytes .../Test/dfd/42_VK_FORMAT_R8G8B8A8_SINT.dfd | Bin 0 -> 92 bytes .../Test/dfd/43_VK_FORMAT_R8G8B8A8_SRGB.dfd | Bin 0 -> 92 bytes .../Test/dfd/70_VK_FORMAT_R16_UNORM.dfd | Bin 0 -> 44 bytes .../Test/dfd/71_VK_FORMAT_R16_SNORM.dfd | Bin 0 -> 44 bytes .../Test/dfd/74_VK_FORMAT_R16_UINT.dfd | Bin 0 -> 44 bytes .../Test/dfd/75_VK_FORMAT_R16_SINT.dfd | Bin 0 -> 44 bytes .../Test/dfd/76_VK_FORMAT_R16_SFLOAT.dfd | Bin 0 -> 44 bytes .../Test/dfd/77_VK_FORMAT_R16G16_UNORM.dfd | Bin 0 -> 60 bytes .../Test/dfd/78_VK_FORMAT_R16G16_SNORM.dfd | Bin 0 -> 60 bytes .../Test/dfd/81_VK_FORMAT_R16G16_UINT.dfd | Bin 0 -> 60 bytes .../Test/dfd/82_VK_FORMAT_R16G16_SINT.dfd | Bin 0 -> 60 bytes .../Test/dfd/83_VK_FORMAT_R16G16_SFLOAT.dfd | Bin 0 -> 60 bytes .../Test/dfd/84_VK_FORMAT_R16G16B16_UNORM.dfd | Bin 0 -> 76 bytes .../Test/dfd/85_VK_FORMAT_R16G16B16_SNORM.dfd | Bin 0 -> 76 bytes .../Test/dfd/88_VK_FORMAT_R16G16B16_UINT.dfd | Bin 0 -> 76 bytes .../Test/dfd/89_VK_FORMAT_R16G16B16_SINT.dfd | Bin 0 -> 76 bytes .../dfd/90_VK_FORMAT_R16G16B16_SFLOAT.dfd | Bin 0 -> 76 bytes .../dfd/91_VK_FORMAT_R16G16B16A16_UNORM.dfd | Bin 0 -> 92 bytes .../dfd/92_VK_FORMAT_R16G16B16A16_SNORM.dfd | Bin 0 -> 92 bytes .../dfd/95_VK_FORMAT_R16G16B16A16_UINT.dfd | Bin 0 -> 92 bytes .../dfd/96_VK_FORMAT_R16G16B16A16_SINT.dfd | Bin 0 -> 92 bytes .../dfd/97_VK_FORMAT_R16G16B16A16_SFLOAT.dfd | Bin 0 -> 92 bytes .../Test/dfd/98_VK_FORMAT_R32_UINT.dfd | Bin 0 -> 44 bytes .../Test/dfd/99_VK_FORMAT_R32_SINT.dfd | Bin 0 -> 44 bytes .../Test/dfd/9_VK_FORMAT_R8_UNORM.dfd | Bin 0 -> 44 bytes .../Test/testbidirectionalmapping.c.patch | 56 +++++++ 136 files changed, 194 insertions(+), 27 deletions(-) create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/README.md create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054000_VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054001_VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054004_VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054005_VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066000_VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066001_VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066002_VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066003_VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066004_VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066005_VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066006_VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066007_VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066008_VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066009_VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066010_VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066011_VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066012_VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066013_VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/100_VK_FORMAT_R32_SFLOAT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/101_VK_FORMAT_R32G32_UINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/102_VK_FORMAT_R32G32_SINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/103_VK_FORMAT_R32G32_SFLOAT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/104_VK_FORMAT_R32G32B32_UINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/105_VK_FORMAT_R32G32B32_SINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/106_VK_FORMAT_R32G32B32_SFLOAT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/107_VK_FORMAT_R32G32B32A32_UINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/108_VK_FORMAT_R32G32B32A32_SINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/109_VK_FORMAT_R32G32B32A32_SFLOAT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/10_VK_FORMAT_R8_SNORM.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/124_VK_FORMAT_D16_UNORM.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/125_VK_FORMAT_X8_D24_UNORM_PACK32.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/126_VK_FORMAT_D32_SFLOAT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/127_VK_FORMAT_S8_UINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/128_VK_FORMAT_D16_UNORM_S8_UINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/129_VK_FORMAT_D24_UNORM_S8_UINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/130_VK_FORMAT_D32_SFLOAT_S8_UINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/131_VK_FORMAT_BC1_RGB_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/132_VK_FORMAT_BC1_RGB_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/133_VK_FORMAT_BC1_RGBA_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/134_VK_FORMAT_BC1_RGBA_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/135_VK_FORMAT_BC2_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/136_VK_FORMAT_BC2_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/137_VK_FORMAT_BC3_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/138_VK_FORMAT_BC3_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/139_VK_FORMAT_BC4_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/13_VK_FORMAT_R8_UINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/140_VK_FORMAT_BC4_SNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/141_VK_FORMAT_BC5_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/142_VK_FORMAT_BC5_SNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/143_VK_FORMAT_BC6H_UFLOAT_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/144_VK_FORMAT_BC6H_SFLOAT_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/145_VK_FORMAT_BC7_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/146_VK_FORMAT_BC7_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/147_VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/148_VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/149_VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/14_VK_FORMAT_R8_SINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/150_VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/151_VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/152_VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/153_VK_FORMAT_EAC_R11_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/154_VK_FORMAT_EAC_R11_SNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/155_VK_FORMAT_EAC_R11G11_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/156_VK_FORMAT_EAC_R11G11_SNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/157_VK_FORMAT_ASTC_4x4_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/158_VK_FORMAT_ASTC_4x4_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/159_VK_FORMAT_ASTC_5x4_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/15_VK_FORMAT_R8_SRGB.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/160_VK_FORMAT_ASTC_5x4_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/161_VK_FORMAT_ASTC_5x5_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/162_VK_FORMAT_ASTC_5x5_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/163_VK_FORMAT_ASTC_6x5_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/164_VK_FORMAT_ASTC_6x5_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/165_VK_FORMAT_ASTC_6x6_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/166_VK_FORMAT_ASTC_6x6_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/167_VK_FORMAT_ASTC_8x5_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/168_VK_FORMAT_ASTC_8x5_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/169_VK_FORMAT_ASTC_8x6_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/16_VK_FORMAT_R8G8_UNORM.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/170_VK_FORMAT_ASTC_8x6_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/171_VK_FORMAT_ASTC_8x8_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/172_VK_FORMAT_ASTC_8x8_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/173_VK_FORMAT_ASTC_10x5_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/174_VK_FORMAT_ASTC_10x5_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/175_VK_FORMAT_ASTC_10x6_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/176_VK_FORMAT_ASTC_10x6_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/177_VK_FORMAT_ASTC_10x8_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/178_VK_FORMAT_ASTC_10x8_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/179_VK_FORMAT_ASTC_10x10_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/17_VK_FORMAT_R8G8_SNORM.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/180_VK_FORMAT_ASTC_10x10_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/181_VK_FORMAT_ASTC_12x10_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/182_VK_FORMAT_ASTC_12x10_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/183_VK_FORMAT_ASTC_12x12_UNORM_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/184_VK_FORMAT_ASTC_12x12_SRGB_BLOCK.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/20_VK_FORMAT_R8G8_UINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/21_VK_FORMAT_R8G8_SINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/22_VK_FORMAT_R8G8_SRGB.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/23_VK_FORMAT_R8G8B8_UNORM.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/24_VK_FORMAT_R8G8B8_SNORM.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/27_VK_FORMAT_R8G8B8_UINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/28_VK_FORMAT_R8G8B8_SINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/29_VK_FORMAT_R8G8B8_SRGB.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/37_VK_FORMAT_R8G8B8A8_UNORM.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/38_VK_FORMAT_R8G8B8A8_SNORM.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/41_VK_FORMAT_R8G8B8A8_UINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/42_VK_FORMAT_R8G8B8A8_SINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/43_VK_FORMAT_R8G8B8A8_SRGB.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/70_VK_FORMAT_R16_UNORM.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/71_VK_FORMAT_R16_SNORM.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/74_VK_FORMAT_R16_UINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/75_VK_FORMAT_R16_SINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/76_VK_FORMAT_R16_SFLOAT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/77_VK_FORMAT_R16G16_UNORM.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/78_VK_FORMAT_R16G16_SNORM.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/81_VK_FORMAT_R16G16_UINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/82_VK_FORMAT_R16G16_SINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/83_VK_FORMAT_R16G16_SFLOAT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/84_VK_FORMAT_R16G16B16_UNORM.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/85_VK_FORMAT_R16G16B16_SNORM.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/88_VK_FORMAT_R16G16B16_UINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/89_VK_FORMAT_R16G16B16_SINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/90_VK_FORMAT_R16G16B16_SFLOAT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/91_VK_FORMAT_R16G16B16A16_UNORM.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/92_VK_FORMAT_R16G16B16A16_SNORM.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/95_VK_FORMAT_R16G16B16A16_UINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/96_VK_FORMAT_R16G16B16A16_SINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/97_VK_FORMAT_R16G16B16A16_SFLOAT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/98_VK_FORMAT_R32_UINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/99_VK_FORMAT_R32_SINT.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/9_VK_FORMAT_R8_UNORM.dfd create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/testbidirectionalmapping.c.patch diff --git a/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt b/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt index 1f6c528a0..6e92dd6bb 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt @@ -26,8 +26,10 @@ if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) set(KTXIMPORTER_TEST_DIR ".") + set(KTXIMAGECONVERTER_TEST_DIR ".") else() set(KTXIMPORTER_TEST_DIR ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test) + set(KTXIMAGECONVERTER_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}) endif() # CMake before 3.8 has broken $ expressions for iOS (see diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index fcc2bc95b..8b0b2f508 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -62,6 +63,9 @@ struct KtxImageConverterTest: TestSuite::Tester { void implementationSpecificFormat(); void implementationSpecificCompressedFormat(); + void dataFormatDescriptor(); + void dataFormatDescriptorCompressed(); + /* Non-default compressed pixel storage is currently not supported. It's firing an internal assert, so we're not testing that. */ void pixelStorage(); @@ -111,8 +115,11 @@ struct KtxImageConverterTest: TestSuite::Tester { /* Explicitly forbid system-wide plugin dependencies */ PluginManager::Manager _converterManager{"nonexistent"}; PluginManager::Manager _importerManager{"nonexistent"}; + + std::unordered_map> dataFormatDescriptors; }; +using namespace Containers::Literals; using namespace Math::Literals; /* Origin top-left-back */ @@ -260,6 +267,30 @@ const struct { {"invalid characters", "1012", "invalid characters in swizzle 1012"} }; +Containers::Array readDataFormatDescriptor(Containers::ArrayView fileData) { + CORRADE_INTERNAL_ASSERT(fileData.size() >= sizeof(Implementation::KtxHeader)); + const Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); + + const UnsignedInt offset = Utility::Endianness::littleEndian(header.dfdByteOffset); + const UnsignedInt length = Utility::Endianness::littleEndian(header.dfdByteLength); + Containers::Array data{ValueInit, length}; + Utility::copy(fileData.suffix(offset).prefix(length), data); + + return data; +} + +Containers::String readKeyValueData(Containers::ArrayView fileData) { + CORRADE_INTERNAL_ASSERT(fileData.size() >= sizeof(Implementation::KtxHeader)); + const Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); + + const UnsignedInt offset = Utility::Endianness::littleEndian(header.kvdByteOffset); + const UnsignedInt length = Utility::Endianness::littleEndian(header.kvdByteLength); + Containers::String data{ValueInit, length}; + Utility::copy(fileData.suffix(offset).prefix(length), data); + + return data; +} + KtxImageConverterTest::KtxImageConverterTest() { addTests({&KtxImageConverterTest::supportedFormat, &KtxImageConverterTest::supportedCompressedFormat, @@ -267,6 +298,9 @@ KtxImageConverterTest::KtxImageConverterTest() { &KtxImageConverterTest::implementationSpecificFormat, &KtxImageConverterTest::implementationSpecificCompressedFormat, + &KtxImageConverterTest::dataFormatDescriptor, + &KtxImageConverterTest::dataFormatDescriptorCompressed, + &KtxImageConverterTest::pixelStorage, &KtxImageConverterTest::tooManyLevels, @@ -327,24 +361,43 @@ KtxImageConverterTest::KtxImageConverterTest() { #ifdef KTXIMPORTER_PLUGIN_FILENAME CORRADE_INTERNAL_ASSERT_OUTPUT(_importerManager.load(KTXIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); #endif -} -using namespace Containers::Literals; + /* Map VkFormat to DFD test file. The VkFormat value is in the file name. */ + const std::string folder = Utility::Directory::join(KTXIMAGECONVERTER_TEST_DIR, "dfd"); + const auto files = Utility::Directory::list(folder, Utility::Directory::Flag::SkipDirectories | Utility::Directory::Flag::SkipSpecial); + CORRADE_INTERNAL_ASSERT(!files.empty()); + for(const auto& f: files) { + Containers::StringView file{f}; + if(file.hasSuffix(".dfd"_s)) { + const auto found = file.find("_VK_FORMAT_"_s); + CORRADE_INTERNAL_ASSERT(!found.isEmpty()); + const std::size_t prefix = found.data() - f.data(); + CORRADE_INTERNAL_ASSERT(prefix > 0); + std::size_t read = 0; + const Implementation::VkFormat format = std::stoi(file.prefix(prefix), &read); + CORRADE_INTERNAL_ASSERT(read == prefix); + auto dfd = Utility::Directory::read(Utility::Directory::join(folder, file)); + CORRADE_INTERNAL_ASSERT(!dfd.empty()); + dataFormatDescriptors.emplace(format, std::move(dfd)); + } + } +} void KtxImageConverterTest::supportedFormat() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); - const UnsignedByte data[32]{}; + const UnsignedByte bytes[32]{}; /* All the formats in PixelFormat are supported */ - /** @todo This needs to be extended when new formats are added to PixelFormat */ + /** @todo This needs to be extended when new formats are added to + PixelFormat. In dataFormatDescriptor as well. */ constexpr PixelFormat start = PixelFormat::R8Unorm; constexpr PixelFormat end = PixelFormat::Depth32FStencil8UI; - for(UnsignedInt format = UnsignedInt(start); format != UnsignedInt(end); ++format) { + for(UnsignedInt format = UnsignedInt(start); format <= UnsignedInt(end); ++format) { CORRADE_ITERATION(format); - CORRADE_INTERNAL_ASSERT(Containers::arraySize(data) >= pixelSize(PixelFormat(format))); - CORRADE_VERIFY(converter->convertToData(ImageView2D{PixelFormat(format), {1, 1}, data})); + CORRADE_INTERNAL_ASSERT(Containers::arraySize(bytes) >= pixelSize(PixelFormat(format))); + CORRADE_VERIFY(converter->convertToData(ImageView2D{PixelFormat(format), {1, 1}, bytes})); } } @@ -387,14 +440,16 @@ void KtxImageConverterTest::supportedCompressedFormat() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); const UnsignedByte bytes[32]{}; - const auto unsupported = Containers::arrayView(UnsupportedCompressedFormats); - /** @todo This needs to be extended when new formats are added to CompressedPixelFormat */ + /** @todo This needs to be extended when new formats are added to + CompressedPixelFormat. In dataFormatDescriptorCompressed as well. */ constexpr CompressedPixelFormat start = CompressedPixelFormat::Bc1RGBUnorm; constexpr CompressedPixelFormat end = CompressedPixelFormat::PvrtcRGBA4bppSrgb; - for(UnsignedInt format = UnsignedInt(start); format != UnsignedInt(end); ++format) { - if(std::find(unsupported.begin(), unsupported.end(), CompressedPixelFormat(format)) == unsupported.end()) { + for(UnsignedInt format = UnsignedInt(start); format <= UnsignedInt(end); ++format) { + if(std::find(std::begin(UnsupportedCompressedFormats), std::end(UnsupportedCompressedFormats), + CompressedPixelFormat(format)) == std::end(UnsupportedCompressedFormats)) + { CORRADE_ITERATION(format); CORRADE_INTERNAL_ASSERT(Containers::arraySize(bytes) >= compressedBlockDataSize(CompressedPixelFormat(format))); CORRADE_VERIFY(converter->convertToData(CompressedImageView2D{CompressedPixelFormat(format), {1, 1}, bytes})); @@ -447,6 +502,56 @@ void KtxImageConverterTest::implementationSpecificCompressedFormat() { "Trade::KtxImageConverter::convertToData(): implementation-specific formats are not supported\n"); } +void KtxImageConverterTest::dataFormatDescriptor() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + const UnsignedByte bytes[32]{}; + + constexpr PixelFormat start = PixelFormat::R8Unorm; + constexpr PixelFormat end = PixelFormat::Depth32FStencil8UI; + + for(UnsignedInt format = UnsignedInt(start); format <= UnsignedInt(end); ++format) { + CORRADE_ITERATION(format); + CORRADE_INTERNAL_ASSERT(Containers::arraySize(bytes) >= pixelSize(PixelFormat(format))); + const auto output = converter->convertToData(ImageView2D{PixelFormat(format), {1, 1}, bytes}); + CORRADE_VERIFY(output); + + const Implementation::KtxHeader& header = *reinterpret_cast(output.data()); + const Implementation::VkFormat vkFormat = Utility::Endianness::littleEndian(header.vkFormat); + + const auto dfd = readDataFormatDescriptor(output); + CORRADE_COMPARE(dataFormatDescriptors.count(vkFormat), 1); + CORRADE_COMPARE_AS(dfd, dataFormatDescriptors[vkFormat], TestSuite::Compare::Container); + } +} + +void KtxImageConverterTest::dataFormatDescriptorCompressed() { + Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); + + const UnsignedByte bytes[32]{}; + + constexpr CompressedPixelFormat start = CompressedPixelFormat::Bc1RGBUnorm; + constexpr CompressedPixelFormat end = CompressedPixelFormat::PvrtcRGBA4bppSrgb; + + for(UnsignedInt format = UnsignedInt(start); format <= UnsignedInt(end); ++format) { + if(std::find(std::begin(UnsupportedCompressedFormats), std::end(UnsupportedCompressedFormats), + CompressedPixelFormat(format)) == std::end(UnsupportedCompressedFormats)) + { + CORRADE_ITERATION(format); + CORRADE_INTERNAL_ASSERT(Containers::arraySize(bytes) >= compressedBlockDataSize(CompressedPixelFormat(format))); + const auto output = converter->convertToData(CompressedImageView2D{CompressedPixelFormat(format), {1, 1}, bytes}); + CORRADE_VERIFY(output); + + const Implementation::KtxHeader& header = *reinterpret_cast(output.data()); + const Implementation::VkFormat vkFormat = Utility::Endianness::littleEndian(header.vkFormat); + + const auto dfd = readDataFormatDescriptor(output); + CORRADE_COMPARE(dataFormatDescriptors.count(vkFormat), 1); + CORRADE_COMPARE_AS(dfd, dataFormatDescriptors[vkFormat], TestSuite::Compare::Container); + } + } +} + void KtxImageConverterTest::pixelStorage() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); @@ -530,7 +635,7 @@ void KtxImageConverterTest::convert1DMipmaps() { converter->configuration().removeValue("orientation"); converter->configuration().setValue("writerName", WriterToktx); - const Math::Vector<1, Int> size{4}; + constexpr Math::Vector<1, Int> size{4}; const Color3ub mip0[4]{0xff0000_rgb, 0xffffff_rgb, 0x000000_rgb, 0x007f7f_rgb}; const Color3ub mip1[2]{0xffffff_rgb, 0x007f7f_rgb}; const Color3ub mip2[1]{0x000000_rgb}; @@ -574,7 +679,7 @@ void KtxImageConverterTest::convert1DCompressedMipmaps() { converter->configuration().setValue("orientation", "r"); converter->configuration().setValue("writerName", WriterPVRTexTool); - const Math::Vector<1, Int> size{7}; + constexpr Math::Vector<1, Int> size{7}; const auto mip0 = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d-compressed-mipmaps-mip0.bin")); const auto mip1 = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d-compressed-mipmaps-mip1.bin")); const auto mip2 = Utility::Directory::read(Utility::Directory::join(KTXIMPORTER_TEST_DIR, "1d-compressed-mipmaps-mip2.bin")); @@ -612,7 +717,7 @@ void KtxImageConverterTest::convert2DMipmaps() { converter->configuration().setValue("orientation", "rd"); converter->configuration().setValue("writerName", WriterToktx); - const Vector2i size{4, 3}; + constexpr Vector2i size{4, 3}; const auto mip0 = Containers::arrayCast(Containers::arrayView( PatternRgbData[Containers::arraySize(PatternRgbData) - 1])); const Color3ub mip1[2]{0xffffff_rgb, 0x007f7f_rgb}; @@ -638,7 +743,7 @@ void KtxImageConverterTest::convert2DMipmapsIncomplete() { converter->configuration().setValue("orientation", "rd"); converter->configuration().setValue("writerName", WriterToktx); - const Vector2i size{4, 3}; + constexpr Vector2i size{4, 3}; const auto mip0 = Containers::arrayCast(Containers::arrayView( PatternRgbData[Containers::arraySize(PatternRgbData) - 1])); const Color3ub mip1[2]{0xffffff_rgb, 0x007f7f_rgb}; @@ -849,18 +954,6 @@ void KtxImageConverterTest::pvrtcRgb() { CORRADE_COMPARE_AS(image->data(), inputImage.data(), TestSuite::Compare::Container); } -Containers::String readKeyValueData(Containers::ArrayView fileData) { - CORRADE_INTERNAL_ASSERT(fileData.size() >= sizeof(Implementation::KtxHeader)); - const Implementation::KtxHeader& header = *reinterpret_cast(fileData.data()); - - const UnsignedInt offset = Utility::Endianness::littleEndian(header.kvdByteOffset); - const UnsignedInt length = Utility::Endianness::littleEndian(header.kvdByteLength); - Containers::String data{ValueInit, length}; - Utility::copy(fileData.suffix(offset).prefix(length), data); - - return data; -} - void KtxImageConverterTest::configurationOrientation() { Containers::Pointer converter = _converterManager.instantiate("KtxImageConverter"); /* Default value */ diff --git a/src/MagnumPlugins/KtxImageConverter/Test/README.md b/src/MagnumPlugins/KtxImageConverter/Test/README.md new file mode 100644 index 000000000..a031dc7ac --- /dev/null +++ b/src/MagnumPlugins/KtxImageConverter/Test/README.md @@ -0,0 +1,15 @@ +Updating test files +=================== + +The files in the dfd folder are created using a patch to `testbidirectionalmapping` from [dfdutils](https://github.com/KhronosGroup/dfdutils): + +```bash +git clone https://github.com/KhronosGroup/dfdutils.git +cd dfdutils +git checkout 659a739bf60bdcd730b2159d658fb22e805854c2 +git apply path/to/KtxImageConverter/Test/testbidirectionalmapping.c.patch +make testbidirectionalmapping +# if the above doesn't work, run gcc directly: +# gcc testbidirectionalmapping.c interpretdfd.c createdfd.c -o testbidirectionalmapping -I. -g -W -Wall -std=c99 -pedantic +./testbidirectionalmapping +``` diff --git a/src/MagnumPlugins/KtxImageConverter/Test/configure.h.cmake b/src/MagnumPlugins/KtxImageConverter/Test/configure.h.cmake index 03c42a37c..facf5f6d6 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/configure.h.cmake +++ b/src/MagnumPlugins/KtxImageConverter/Test/configure.h.cmake @@ -27,3 +27,4 @@ #cmakedefine KTXIMAGECONVERTER_PLUGIN_FILENAME "${KTXIMAGECONVERTER_PLUGIN_FILENAME}" #cmakedefine KTXIMPORTER_PLUGIN_FILENAME "${KTXIMPORTER_PLUGIN_FILENAME}" #define KTXIMPORTER_TEST_DIR "${KTXIMPORTER_TEST_DIR}" +#define KTXIMAGECONVERTER_TEST_DIR "${KTXIMAGECONVERTER_TEST_DIR}" diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054000_VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054000_VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG.dfd new file mode 100644 index 0000000000000000000000000000000000000000..6460d713fc7c6845b42d6b4570341b50aa9142a1 GIT binary patch literal 44 hcmdO4fB+^24TdF*j126|3=AAlF$M;EIO{(U001#g1xf$_ literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054001_VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054001_VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG.dfd new file mode 100644 index 0000000000000000000000000000000000000000..0299edf42b662fb56ba2115358e96452e65d2dc5 GIT binary patch literal 44 hcmdO4fB+^24TdF*j10`o3=AAlF$M;EIO{(U001!F1x5e> literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054004_VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054004_VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG.dfd new file mode 100644 index 0000000000000000000000000000000000000000..614d6e32dddc3b9c81600c36a4e342d07df6a718 GIT binary patch literal 44 hcmdO4fB+^24TdF*ObqPI3=AAlF$M;EIO{(U001#;1xo+` literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054005_VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054005_VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG.dfd new file mode 100644 index 0000000000000000000000000000000000000000..feded5796471665b6a861565aa337e0b8f81b6cc GIT binary patch literal 44 hcmdO4fB+^24TdF*ObpD-3=AAlF$M;EIO{(U001!j1xEk? literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066000_VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066000_VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..cb061e8dcbdf14dffcaa05e7cefe18456f4a1c28 GIT binary patch literal 44 lcmdO4fB+^24TeRGj10`o3=9HLF$RYE0}y7zeg=jHdjLG51Wo_| literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066001_VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066001_VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..1f94dccc96214cb2bc1afe82cbf5c503d10e09aa GIT binary patch literal 44 lcmdO4fB+^24TeRGj0`Ny3=9HLF$RYE0}y7zeg=jHdjLGX1Wy0} literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066002_VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066002_VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..cbd1ef6b4d7f924d7cdfd274dff4f50a6d8b85a7 GIT binary patch literal 44 lcmdO4fB+^24TeRGj0`L+3=9HLF$RYE0}y7zeg=jHdjLGy1W*6~ literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066003_VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066003_VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..f065e8df5d8ba0fc0e6694c3221b4c2bacf3251b GIT binary patch literal 44 lcmdO4fB+^24TeRGj0~(S3=9HLF$RYE0}y7zeg=jHdjLH31W^D0 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066004_VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066004_VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..23fdb20cf62f8a5a0d8149d4b9c3299e0e7b2993 GIT binary patch literal 44 lcmdO4fB+^24TeRGj0~)-3=9HLF$RYE0}y7zeg=jHdjLHU1X2J1 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066005_VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066005_VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..dfcbf6f78034d9b3609ba1913b18f4e208cc519c GIT binary patch literal 44 lcmdO4fB+^24TeRGj12573=9HLF$RYE0}y7zeg=jHdjLHx1XBP2 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066006_VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066006_VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..dde26116526e64d07842eeec9f49f218d815ae75 GIT binary patch literal 44 lcmdO4fB+^24TeRGj126o3=9HLF$RYE0}y7zeg=jHdjLI11XKV3 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066007_VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066007_VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..37ce3db22790eb5294f158d869366f554d8cf44b GIT binary patch literal 44 lcmdO4fB+^24TeRGj127T3=9HLF$RYE0}y7zeg=jHdjLIt1Xch5 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066008_VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066008_VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..ed3f76df9a2fd2e73d1198190364b0bed913b92f GIT binary patch literal 44 lcmdO4fB+^24TeRGj0~JC3=9HLF$RYE0}y7zeg=jHdjLIU1XTb4 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066009_VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066009_VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..f0e716014f790a8b02dc66e61198dd9347dfc508 GIT binary patch literal 44 lcmdO4fB+^24TeRGj0~Kt3=9HLF$RYE0}y7zeg=jHdjLIv1Xch5 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066010_VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066010_VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..2628a3f0c83570c3556e2cfd6afafaa88dee188a GIT binary patch literal 44 lcmdO4fB+^24TeRGj0~LY3=9HLF$RYE0}y7zeg=jHdjLJQ1Xut7 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066011_VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066011_VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..7630cb5fd1f02f9461d5462bec835f5381cf4bf4 GIT binary patch literal 44 lcmdO4fB+^24TeRGj0~Kd3=9HLF$RYE0}y7zeg=jHdjLJ`1X=(9 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066012_VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066012_VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..084ffdf9dc0878d56892a94b410f9bd5ae45f130 GIT binary patch literal 44 lcmdO4fB+^24TeRGj11hI3=9HLF$RYE0}y7zeg=jHdjLKp1Y7_B literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066013_VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066013_VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..9629431d55546928fc143e80d0a567a2923c477f GIT binary patch literal 44 lcmdO4fB+^24TeRGj11h|3=9HLF$RYE0}y7zeg=jHdjLLK1YQ6D literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/100_VK_FORMAT_R32_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/100_VK_FORMAT_R32_SFLOAT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..62bf065bedf2ddc580b5df739f401b4a0829e6d7 GIT binary patch literal 44 icmdO4fB+^24F*O=Mlgp3D#pMde*nU4*w4VwU=IKxeFHlH literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/101_VK_FORMAT_R32G32_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/101_VK_FORMAT_R32G32_UINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..83099d3108409b21d2120f8a7bd4a86450b0db3e GIT binary patch literal 60 lcmcCvfB+^23kF6;Mlgp1D#pMd4`+d76d2?o3V>`70{|lG0OJ4v literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/102_VK_FORMAT_R32G32_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/102_VK_FORMAT_R32G32_SINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..ca4c12437cc34ba80758665761e3e841e5cc2e12 GIT binary patch literal 60 mcmcCvfB+^23kF6;Mlgp1D#pMd?*L-{2Lh0c0)xCGG9Lh-_6b`6 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/103_VK_FORMAT_R32G32_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/103_VK_FORMAT_R32G32_SFLOAT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..1deaf3ecf4ebb12db3ff82135794d4abf3848b91 GIT binary patch literal 60 ocmcCvfB+^23kF6;Mlgp1D#pMde*nU4*w4VwV6VU+e-N1u0CFYt<8 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/104_VK_FORMAT_R32G32B32_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/104_VK_FORMAT_R32G32B32_UINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..c97af3b5e09623b4345aed2ef7b33638ef06bdf5 GIT binary patch literal 76 tcmeZafB+^24+cg?Mlgp5D#pMd4`+d76d2?o3V>`7!+}Ab38nzV2LM2-0cijL literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/105_VK_FORMAT_R32G32B32_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/105_VK_FORMAT_R32G32B32_SINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..c92d53924a95679f307b24bf53b7b523f934e98c GIT binary patch literal 76 scmeZafB+^24+cg?Mlgp5D#pMd?*L-{2Lh0c0)xCGGT(tg-U*ox0Ap(o9RL6T literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/106_VK_FORMAT_R32G32B32_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/106_VK_FORMAT_R32G32B32_SFLOAT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..c17db614013b0e711e85856b5a7b444f5832a014 GIT binary patch literal 76 ucmeZafB+^24+cg?Mlgp5D#pMde*nU4*w4VwV6VU+e-N4Pz#xAJnGXQ@JPDux literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/107_VK_FORMAT_R32G32B32A32_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/107_VK_FORMAT_R32G32B32A32_UINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..4cefd46899928068e2352501a4a7cbe201efd131 GIT binary patch literal 92 zcma!HfB+_j2nI$*MleSJD#pMd4`+d76d2?o3V>`7!+}Ab38nzVPhgPeN9F?nYqJ6z literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/108_VK_FORMAT_R32G32B32A32_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/108_VK_FORMAT_R32G32B32A32_SINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..b6c08e71254c90563b87431b4aeb44f3ebe595a9 GIT binary patch literal 92 ycma!HfB+_j2nI$*MleSJD#pMd?*L-{2Lh0c0)xCGGT(tg-U*qXz##9B%m)B==@Se9 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/109_VK_FORMAT_R32G32B32A32_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/109_VK_FORMAT_R32G32B32A32_SFLOAT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..e2f81b4448098ce1d34045a18befe013b40aaca1 GIT binary patch literal 92 zcma!HfB+_j2nI$*MleSJD#pMde*nU4*w4VwV6VU+e-N4Pz#xAJnV-NQe;%0+0J(Y% A5C8xG literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/10_VK_FORMAT_R8_SNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/10_VK_FORMAT_R8_SNORM.dfd new file mode 100644 index 0000000000000000000000000000000000000000..11fd4e5d67149b58b6f30e64967c2ff8ef4d90ca GIT binary patch literal 44 hcmdO4fB+^24F*O=Mlc6Lf|%?MAhPlQ|Nr$s8UQI>1fKu^ literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/124_VK_FORMAT_D16_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/124_VK_FORMAT_D16_UNORM.dfd new file mode 100644 index 0000000000000000000000000000000000000000..40acecb8b743822bb73d16b9019513b0e36bc7d1 GIT binary patch literal 44 gcmdO4fB+^24F*O=Mlgp7D#pOT&j(}v|Ifex02X2bcK`qY literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/125_VK_FORMAT_X8_D24_UNORM_PACK32.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/125_VK_FORMAT_X8_D24_UNORM_PACK32.dfd new file mode 100644 index 0000000000000000000000000000000000000000..37ac93741471622492a7f8a66182570452ba99a3 GIT binary patch literal 44 gcmdO4fB+^24F*O=Mlgp3D#pMd&Ie=u|Nox>02u%SfB*mh literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/126_VK_FORMAT_D32_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/126_VK_FORMAT_D32_SFLOAT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..26e7fa510692f427bc03b1931e50fa1cd61575ef GIT binary patch literal 44 icmdO4fB+^24F*O=Mlgp3D#pMde-6TI*w4VwU=IKyGXqHg literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/127_VK_FORMAT_S8_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/127_VK_FORMAT_S8_UINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..d2679f1aefd078688bc7aa4a62518e0f131b3e9b GIT binary patch literal 44 ecmdO4fB+^24F*O=Mlc6Lf|%^Q5LpHWkQe|C*Z^<< literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/128_VK_FORMAT_D16_UNORM_S8_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/128_VK_FORMAT_D16_UNORM_S8_UINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..c4e74b914769a8b050fef9571a46d454b2e1279d GIT binary patch literal 60 ocmcCvfB+^23kF6;MlgpND#pOT&j(}v|Iff6z`)K6V!Z literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/130_VK_FORMAT_D32_SFLOAT_S8_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/130_VK_FORMAT_D32_SFLOAT_S8_UINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..24fff8eeec7f026d3a22a37f05949e9618a3fab5 GIT binary patch literal 60 qcmcCvfB+^23kF6;MlgpJD#pMde-6TI*w4VwV6VWy&I^+QsRRIE+5?{e literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/131_VK_FORMAT_BC1_RGB_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/131_VK_FORMAT_BC1_RGB_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..ab4130745e0a2aee3387d5ae8747747d59e87742 GIT binary patch literal 44 gcmdO4fB+^24Tc6rMh0eP1_lnO7z2Ynob?|F04!bwA^-pY literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/132_VK_FORMAT_BC1_RGB_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/132_VK_FORMAT_BC1_RGB_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..35a18ce99ff2a9c0e89db8221b9b7856a39ceaac GIT binary patch literal 44 gcmdO4fB+^24Tc6rCI)6^1_lnO7z2Ynob?|F04#h3BLDyZ literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/133_VK_FORMAT_BC1_RGBA_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/133_VK_FORMAT_BC1_RGBA_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..d78e14d42f6d8dec53d2316529b07cc98aff3f9d GIT binary patch literal 44 hcmdO4fB+^24Tc6rMh0eP1_lnO7z2YnBaHbU2mmZ=1tS0e literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/134_VK_FORMAT_BC1_RGBA_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/134_VK_FORMAT_BC1_RGBA_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..5abcfb14c0a40fef1b20b781dd0944ec2cc43d4d GIT binary patch literal 44 hcmdO4fB+^24Tc6rCI)6^1_lnO7z2YnBaHbU2mmaJ1tb6f literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/135_VK_FORMAT_BC2_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/135_VK_FORMAT_BC2_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..8382855ffd7a347c576332b609ab24427b753b93 GIT binary patch literal 60 pcmcCvfB+^23x-BUMh0eP1_lAB7z2YnKaBYw2pkyfVO$0f9{`!@3Dp1q literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/136_VK_FORMAT_BC2_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/136_VK_FORMAT_BC2_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..8f54bc54a1e6e3ad66ddebbc319d8072a178f259 GIT binary patch literal 60 pcmcCvfB+^23x-BUCI)6^1_lAB7z2a7JdF7t2pkyfVO$0f9{`)@3FZI* literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/137_VK_FORMAT_BC3_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/137_VK_FORMAT_BC3_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..476249a7cf6e28fa7ec668ac753e0569185e4654 GIT binary patch literal 60 pcmcCvfB+^23x*~}Mh0eP1_lAB7z2YnKaBYw2pkyfVO$0f9{`#e3Dy7r literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/138_VK_FORMAT_BC3_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/138_VK_FORMAT_BC3_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..894302df1239d6a3f3fa7ad520eb407bf93f4ca0 GIT binary patch literal 60 pcmcCvfB+^23x*~}CI)6^1_lAB7z2a7JdF7t2pkyfVO$0f9{`*e3FiO+ literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/139_VK_FORMAT_BC4_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/139_VK_FORMAT_BC4_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..4763a340ff39b8658e7b18be2869bff8704b65de GIT binary patch literal 44 gcmdO4fB+^24Tfe$Mh0eP1_lnO7z2Ynob?|F04%-*B>(^b literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/13_VK_FORMAT_R8_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/13_VK_FORMAT_R8_UINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..84ba24a585c7ebaad0f49c7e539ea178157c5b5d GIT binary patch literal 44 dcmdO4fB+^24F*O=Mlc6Lf|%?ONgxRl0{{*$0Am0E literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/140_VK_FORMAT_BC4_SNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/140_VK_FORMAT_BC4_SNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..65aec9c66f50924d149c0db3d4e56bcae32d83d4 GIT binary patch literal 44 kcmdO4fB+^24Tfe$Mh0eP1_lnO7z2a71C-hD|NsAb05SdrWdHyG literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/141_VK_FORMAT_BC5_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/141_VK_FORMAT_BC5_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..216adf0994760df5209d07ccf15823507d6f134a GIT binary patch literal 60 ocmcCvfB+^23x*a(Mh0eP1_lAB7z2Ynob?|F92o2wVFDmN0GQJW$^ZZW literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/142_VK_FORMAT_BC5_SNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/142_VK_FORMAT_BC5_SNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..c0a4dd52cd568e22c02c79bc9af878747da59dc8 GIT binary patch literal 60 qcmcCvfB+^23x*a(Mh0eP1_lAB7z2a71C-hD|NsAb2L^jbWIh0=QwmA| literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/143_VK_FORMAT_BC6H_UFLOAT_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/143_VK_FORMAT_BC6H_UFLOAT_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..c4279a78807f7be294d5f598b73c9f0fddaa0cfd GIT binary patch literal 44 icmdO4fB+^24Te@mMh0eP1_lAB7z0Cn1DwgwU=IK<2LiGH literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/144_VK_FORMAT_BC6H_SFLOAT_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/144_VK_FORMAT_BC6H_SFLOAT_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..cf05e902e14f571dddc50def38ec6c1b9804ecf0 GIT binary patch literal 44 lcmdO4fB+^24Te@mMh0eP1_lAB7z0E70SL2UKLbO9JpefZ1Tg>r literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/145_VK_FORMAT_BC7_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/145_VK_FORMAT_BC7_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..9a9b5d0157b65c9a2c063564dabc0129fbc227e2 GIT binary patch literal 44 gcmdO4fB+^24Td&GMh0eP1_lAB7z0B+ob?|F05M(#Z~y=R literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/146_VK_FORMAT_BC7_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/146_VK_FORMAT_BC7_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..521d2a9e08fcdd55729f8df80db1d7833c888a97 GIT binary patch literal 44 gcmdO4fB+^24Td&GCI)6^1_lAB7z0B+ob?|F05N<8aR2}S literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/147_VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/147_VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..0c5f99958d315947780fdab5264fe770641d9de7 GIT binary patch literal 44 icmdO4fB+^24Tgn`j10`o3=AAlF$M;ECK&TS5C8x$m<2@u literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/148_VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/148_VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..4cf1799ea1b92ecdaa0d12ee4863342aeab5676b GIT binary patch literal 44 icmdO4fB+^24Tgn`ObpD-3=AAlF$M;ECK&TS5C8x$wgpB2 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/149_VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/149_VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..32c167e475d97f843e973648465ade7c8c8734f6 GIT binary patch literal 60 mcmcCvfB+^23x~H literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/151_VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/151_VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..03b74db608e7db8477c0e92cc1e8bd1e9c8ba666 GIT binary patch literal 60 ncmcCvfB+^23xTT05!e^g8%>k literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/155_VK_FORMAT_EAC_R11G11_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/155_VK_FORMAT_EAC_R11G11_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..384081191127e95ef99e709ed997493646eca89a GIT binary patch literal 60 ocmcCvfB+^23xk$=Kufz literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/156_VK_FORMAT_EAC_R11G11_SNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/156_VK_FORMAT_EAC_R11G11_SNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..92876d24edc41650868c0aa3071f57f52a0dde07 GIT binary patch literal 60 qcmcCvfB+^23x4h;5=$b0~-!wP2r literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/157_VK_FORMAT_ASTC_4x4_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/157_VK_FORMAT_ASTC_4x4_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..6274b8eb45cf823e806b58f414c64d82f5c499f5 GIT binary patch literal 44 hcmdO4fB+^24TeRGj10`o3=9HLF$RWuIO{(U001=J1&jay literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/158_VK_FORMAT_ASTC_4x4_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/158_VK_FORMAT_ASTC_4x4_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..6f257b967b6699ea3a842d6b4796c96c0f521d69 GIT binary patch literal 44 hcmdO4fB+^24TeRGObpD-3=9HLF$RWuIO{(U001=n1&sgz literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/159_VK_FORMAT_ASTC_5x4_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/159_VK_FORMAT_ASTC_5x4_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..e476a6267e1fad612346b1c982db0c7e58db4e8c GIT binary patch literal 44 hcmdO4fB+^24TeRGj0`Ny3=9HLF$RWuIO{(U001=l1&sgz literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/15_VK_FORMAT_R8_SRGB.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/15_VK_FORMAT_R8_SRGB.dfd new file mode 100644 index 0000000000000000000000000000000000000000..1271b748de7fcc9a13186ef0875046eae3adce7a GIT binary patch literal 44 dcmdO4fB+^24F*O=CNPH)D#pOT4rl!bvH%iA0b&3E literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/160_VK_FORMAT_ASTC_5x4_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/160_VK_FORMAT_ASTC_5x4_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..e66367d83bf99eb109b5c174a07ac431e95559d5 GIT binary patch literal 44 hcmdO4fB+^24TeRGObjf{3=9HLF$RWuIO{(U001=@1&#m! literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/161_VK_FORMAT_ASTC_5x5_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/161_VK_FORMAT_ASTC_5x5_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..fd42160de0feda7998d73219c050d77e60cda08a GIT binary patch literal 44 hcmdO4fB+^24TeRGj0`L+3=9HLF$RWuIO{(U001==1&#m! literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/162_VK_FORMAT_ASTC_5x5_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/162_VK_FORMAT_ASTC_5x5_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..d955ec781fca8d9596eb4d66de32a7276f4009dd GIT binary patch literal 44 hcmdO4fB+^24TeRGObje63=9HLF$RWuIO{(U001>J1&;s# literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/163_VK_FORMAT_ASTC_6x5_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/163_VK_FORMAT_ASTC_6x5_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..0ac72366419f2de26e07fc45f202a817ec619761 GIT binary patch literal 44 hcmdO4fB+^24TeRGj0~(S3=9HLF$RWuIO{(U001>H1&;s# literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/164_VK_FORMAT_ASTC_6x5_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/164_VK_FORMAT_ASTC_6x5_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..347abe77c7ff55d0b28a71c6690f1f56f060d2eb GIT binary patch literal 44 hcmdO4fB+^24TeRGObo0n3=9HLF$RWuIO{(U001>l1&{y$ literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/165_VK_FORMAT_ASTC_6x6_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/165_VK_FORMAT_ASTC_6x6_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..790ec75abf8a2578ec46b18e369d24c55a52c5e0 GIT binary patch literal 44 hcmdO4fB+^24TeRGj0~)-3=9HLF$RWuIO{(U001>i1&{y$ literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/166_VK_FORMAT_ASTC_6x6_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/166_VK_FORMAT_ASTC_6x6_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..4af3333797f0b4acc643677090612e2731ae421c GIT binary patch literal 44 hcmdO4fB+^24TeRGObo273=9HLF$RWuIO{(U001>=1(5&% literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/167_VK_FORMAT_ASTC_8x5_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/167_VK_FORMAT_ASTC_8x5_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..ebb6b9b594ee299937bd6f45869612c0052ce8fe GIT binary patch literal 44 hcmdO4fB+^24TeRGj12573=9HLF$RWuIO{(U001><1(5&% literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/168_VK_FORMAT_ASTC_8x5_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/168_VK_FORMAT_ASTC_8x5_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..953b3cc9c1738080b42a6255ea908263788cd529 GIT binary patch literal 44 hcmdO4fB+^24TeRGObqNS3=9HLF$RWuIO{(U001?I1(E;& literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/169_VK_FORMAT_ASTC_8x6_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/169_VK_FORMAT_ASTC_8x6_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..8c014e164059c2876e8ee20409874657c932c07c GIT binary patch literal 44 hcmdO4fB+^24TeRGj126o3=9HLF$RWuIO{(U001?F1(E;& literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/16_VK_FORMAT_R8G8_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/16_VK_FORMAT_R8G8_UNORM.dfd new file mode 100644 index 0000000000000000000000000000000000000000..d031e0b7dfb415e2825f42e2f9c7745503d8ee88 GIT binary patch literal 60 mcmcCvfB+^23kF6;Mlgp7D#pOT4rl!bvN#ynAqs$O5FY?EDgu!J literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/170_VK_FORMAT_ASTC_8x6_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/170_VK_FORMAT_ASTC_8x6_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..2343973c3760f85bf13f4eb9caf6d9dd3a3bc7f4 GIT binary patch literal 44 hcmdO4fB+^24TeRGObqO-3=9HLF$RWuIO{(U001?j1(N^( literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/171_VK_FORMAT_ASTC_8x8_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/171_VK_FORMAT_ASTC_8x8_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..471bdfa3904cd8770816a2b25badadeff9c64b07 GIT binary patch literal 44 hcmdO4fB+^24TeRGj127T3=9HLF$RWuIO{(U001?*1(W~) literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/172_VK_FORMAT_ASTC_8x8_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/172_VK_FORMAT_ASTC_8x8_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..45f1756773eac1859c5d9204b4b99e9675e1719b GIT binary patch literal 44 hcmdO4fB+^24TeRGObqPo3=9HLF$RWuIO{(U001@E1(g5* literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/173_VK_FORMAT_ASTC_10x5_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/173_VK_FORMAT_ASTC_10x5_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..c36b0e46f577df68ccbd498d977d817273b2b46f GIT binary patch literal 44 hcmdO4fB+^24TeRGj0~JC3=9HLF$RWuIO{(U001?i1(N^( literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/174_VK_FORMAT_ASTC_10x5_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/174_VK_FORMAT_ASTC_10x5_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..8e46bf3a5b09c7579f213ef2eb6c3f0c71a4a33c GIT binary patch literal 44 hcmdO4fB+^24TeRGObnbX3=9HLF$RWuIO{(U001?=1(W~) literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/175_VK_FORMAT_ASTC_10x6_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/175_VK_FORMAT_ASTC_10x6_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..a6559fa8cb4d12a02afd6230a0e7139104081ee0 GIT binary patch literal 44 hcmdO4fB+^24TeRGj0~Kt3=9HLF$RWuIO{(U001?-1(W~) literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/176_VK_FORMAT_ASTC_10x6_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/176_VK_FORMAT_ASTC_10x6_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..f186a44f6ef11282900f4b87b721ddce7866dd1e GIT binary patch literal 44 hcmdO4fB+^24TeRGObnc?3=9HLF$RWuIO{(U001@G1(g5* literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/177_VK_FORMAT_ASTC_10x8_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/177_VK_FORMAT_ASTC_10x8_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..2b5651257d3f478c75c73bb5635d8d5f223940c5 GIT binary patch literal 44 hcmdO4fB+^24TeRGj0~LY3=9HLF$RWuIO{(U001@e1(pB+ literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/178_VK_FORMAT_ASTC_10x8_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/178_VK_FORMAT_ASTC_10x8_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..ccb47642f6b50b7804b25b1493d77acda2bccb4e GIT binary patch literal 44 hcmdO4fB+^24TeRGObndt3=9HLF$RWuIO{(U001@+1(yH- literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/179_VK_FORMAT_ASTC_10x10_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/179_VK_FORMAT_ASTC_10x10_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..b8410ae62bdd6e5d8ddd318bcf81bc9929e62306 GIT binary patch literal 44 hcmdO4fB+^24TeRGj0~Kd3=9HLF$RWuIO{(U001^91(*N; literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/17_VK_FORMAT_R8G8_SNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/17_VK_FORMAT_R8G8_SNORM.dfd new file mode 100644 index 0000000000000000000000000000000000000000..9e8aabbf52e110920cfd24cfd61530a046bd841c GIT binary patch literal 60 ocmcCvfB+^23kF6;Mlgp7D#pOT?f_yo{{R2K9!PUAusb620hV?N4FCWD literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/180_VK_FORMAT_ASTC_10x10_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/180_VK_FORMAT_ASTC_10x10_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..b0934898786c7ebe87eb4ab5d5a59e6f0f550434 GIT binary patch literal 44 hcmdO4fB+^24TeRGObncy3=9HLF$RWuIO{(U001^d1(^T< literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/181_VK_FORMAT_ASTC_12x10_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/181_VK_FORMAT_ASTC_12x10_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..552e27d9acc1b44bb8d652f2ca9c41430f64d646 GIT binary patch literal 44 hcmdO4fB+^24TeRGj11hI3=9HLF$RWuIO{(U001^%1)2Z= literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/182_VK_FORMAT_ASTC_12x10_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/182_VK_FORMAT_ASTC_12x10_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..e0f4232aca64348ddeaa77f85c7fb48d83e640c2 GIT binary patch literal 44 hcmdO4fB+^24TeRGObpzd3=9HLF$RWuIO{(U001_A1)Bf> literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/183_VK_FORMAT_ASTC_12x12_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/183_VK_FORMAT_ASTC_12x12_UNORM_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..7fc45f732e3950b125a32074fec17a4e78ad1464 GIT binary patch literal 44 hcmdO4fB+^24TeRGj11h|3=9HLF$RWuIO{(U001_Y1)Kl? literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/184_VK_FORMAT_ASTC_12x12_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/184_VK_FORMAT_ASTC_12x12_SRGB_BLOCK.dfd new file mode 100644 index 0000000000000000000000000000000000000000..1b2340d1741126cd7a2704a233251db1cf6c461c GIT binary patch literal 44 hcmdO4fB+^24TeRGObp!I3=9HLF$RWuIO{(U001_$1)Tr@ literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/20_VK_FORMAT_R8G8_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/20_VK_FORMAT_R8G8_UINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..5cec481cb9a6e46bab14aacfd9c657ddf315d24e GIT binary patch literal 60 lcmcCvfB+^23kF6;Mlgp7D#pOT4rhU6I2hO=3V>`70{|Ra0F?j$ literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/21_VK_FORMAT_R8G8_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/21_VK_FORMAT_R8G8_SINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..2a3543bf006833e83643998b0dbe17ae8910c4f9 GIT binary patch literal 60 mcmcCvfB+^23kF6;Mlgp7D#pOT?f_!`2Lg}`2Lrn!G9Lh%W(f@d literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/22_VK_FORMAT_R8G8_SRGB.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/22_VK_FORMAT_R8G8_SRGB.dfd new file mode 100644 index 0000000000000000000000000000000000000000..2c76130caeda95251cc4abd4b8d13cbb30b1cc70 GIT binary patch literal 60 lcmcCvfB+^23kF6;CNKv=f|%?ONg(+jh&dS88DRn-J^(aW0+Ij# literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/23_VK_FORMAT_R8G8B8_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/23_VK_FORMAT_R8G8B8_UNORM.dfd new file mode 100644 index 0000000000000000000000000000000000000000..71275102578d0c06bb8aa31166f4bfa0d6d04cdc GIT binary patch literal 76 ucmeZafB+^24+cg?MlgpND#pOT4rl!bvN#ynAqs$O5MO|Soe8D@#0LOt$^*&( literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/24_VK_FORMAT_R8G8B8_SNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/24_VK_FORMAT_R8G8B8_SNORM.dfd new file mode 100644 index 0000000000000000000000000000000000000000..5677522864e0c850d2ff8b8be24318b1b1d0eb60 GIT binary patch literal 76 ucmeZafB+^24+cg?MlgpND#pOT?f_yo{{R2K9!PUAusb621sK?!kof>j8x4d2 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/27_VK_FORMAT_R8G8B8_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/27_VK_FORMAT_R8G8B8_UINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..f1a8ebd12bf67ba56a8a456340ce68465b6e471d GIT binary patch literal 76 tcmeZafB+^24+cg?MlgpND#pOT4rhU6I2hO=3V>`7Lx6#u38nzV2LLca0MGyc literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/28_VK_FORMAT_R8G8B8_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/28_VK_FORMAT_R8G8B8_SINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..376984c7319ff34a266886abc15fa8c2da6fe979 GIT binary patch literal 76 scmeZafB+^24+cg?MlgpND#pOT?f_!`2Lg}`2Lrn!GGBm!-3gfw08;}Ega7~l literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/29_VK_FORMAT_R8G8B8_SRGB.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/29_VK_FORMAT_R8G8B8_SRGB.dfd new file mode 100644 index 0000000000000000000000000000000000000000..7453fd9ebff49a4f2b2692063fe986e4d0378dba GIT binary patch literal 76 ucmeZafB+^24+cg?CNPH?D#pOT4rl!bvN#yn8DRn-z5oL|L<5iw;sXF|2m{Li literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/37_VK_FORMAT_R8G8B8A8_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/37_VK_FORMAT_R8G8B8A8_UNORM.dfd new file mode 100644 index 0000000000000000000000000000000000000000..c5ed154409957ea4db428b27a41d77e984b9e432 GIT binary patch literal 92 zcma!HfB+_j2nI$*Mlgp3D#pOT4rl!bvN#ynAqs$O5MO|Soe8D@#Ft=T=SSuP0J<{; A7ytkO literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/38_VK_FORMAT_R8G8B8A8_SNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/38_VK_FORMAT_R8G8B8A8_SNORM.dfd new file mode 100644 index 0000000000000000000000000000000000000000..1c7745c71e57802f5b720b5216bbcfc84dd9532a GIT binary patch literal 92 zcma!HfB+_j2nI$*Mlgp3D#pOT?f_yo{{R2K9!PUAusb621sK?!kogh}?Ec7n08VKV A5C8xG literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/41_VK_FORMAT_R8G8B8A8_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/41_VK_FORMAT_R8G8B8A8_UINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..371d7274d294c6e1aa99d7c8212569e49a01004d GIT binary patch literal 92 zcma!HfB+_j2nI$*Mlgp3D#pOT4rhU6I2hO=3V>`7Lx6#u38nzVmtbJ$N9F?nOA`Sg literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/42_VK_FORMAT_R8G8B8A8_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/42_VK_FORMAT_R8G8B8A8_SINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..78bf6ea6faec09c320409207314103eb24bc3848 GIT binary patch literal 92 ycma!HfB+_j2nI$*Mlgp3D#pOT?f_!`2Lg}`2Lrn!GGBm!-3gg5!NBg1%m)BgNfHnM literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/43_VK_FORMAT_R8G8B8A8_SRGB.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/43_VK_FORMAT_R8G8B8A8_SRGB.dfd new file mode 100644 index 0000000000000000000000000000000000000000..cff0cc0a3a7b1fa4e5c2d6fe6a90ad617e8c2173 GIT binary patch literal 92 zcma!HfB+_j2nI$*CNPHuD#pOT4rl!bvN#yn8DRn-z5oL|L<5iw;!7~F%OlGJ0J~8I ADF6Tf literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/70_VK_FORMAT_R16_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/70_VK_FORMAT_R16_UNORM.dfd new file mode 100644 index 0000000000000000000000000000000000000000..c6e6febc0945e90e2a0643874539ab2c294e450f GIT binary patch literal 44 fcmdO4fB+^24F*O=Mlgp7D#pOT4`==V&%gix6|4ej literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/71_VK_FORMAT_R16_SNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/71_VK_FORMAT_R16_SNORM.dfd new file mode 100644 index 0000000000000000000000000000000000000000..b75caac56f98d416c51a2ddbff7897eda914ad42 GIT binary patch literal 44 kcmdO4fB+^24F*O=Mlgp7D#pOT?*L*lHvIqpzn*~s043=Jr~m)} literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/74_VK_FORMAT_R16_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/74_VK_FORMAT_R16_UINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..a339eed1984d0983a2e06d97efd1be77175afdc0 GIT binary patch literal 44 ccmdO4fB+^24F*O=Mlgp7D#pOT4`+d701meRYybcN literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/75_VK_FORMAT_R16_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/75_VK_FORMAT_R16_SINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..10e295930d1ef2c2221786e0634695e755c30a6b GIT binary patch literal 44 gcmdO4fB+^24F*O=Mlgp7D#pOT?*L-{2Lg}`04v=Dr~m)} literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/76_VK_FORMAT_R16_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/76_VK_FORMAT_R16_SFLOAT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..c15c3303a99850f4ac45bdb6452bde8484d432b5 GIT binary patch literal 44 icmdO4fB+^24F*O=Mlgp7D#pOTe*nU4*w4VwU=IKwZ38I) literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/77_VK_FORMAT_R16G16_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/77_VK_FORMAT_R16G16_UNORM.dfd new file mode 100644 index 0000000000000000000000000000000000000000..2e535786fea658bace73bbbfe9b798672d8b297b GIT binary patch literal 60 ncmcCvfB+^23kF6;Mlgp3D#pOT4`==V&%hwSzz`70{|Y10IvW5 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/82_VK_FORMAT_R16G16_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/82_VK_FORMAT_R16G16_SINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..e3191449872e090bbff1e9385ae6980de624d402 GIT binary patch literal 60 mcmcCvfB+^23kF6;Mlgp3D#pOT?*L-{2Lh0c00X}xG9Lh(mI)^S literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/83_VK_FORMAT_R16G16_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/83_VK_FORMAT_R16G16_SFLOAT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..9901c10b692355fc2388ba7709c117a993c38b95 GIT binary patch literal 60 ocmcCvfB+^23kF6;Mlgp3D#pOTe*nU4*w4VwU@ySHe-N1u0ByzwDF6Tf literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/84_VK_FORMAT_R16G16B16_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/84_VK_FORMAT_R16G16B16_UNORM.dfd new file mode 100644 index 0000000000000000000000000000000000000000..b7d7c45f8cedfa7077486309827fc770a65490c3 GIT binary patch literal 76 vcmeZafB+^24+cg?MlgpBD#pOT4`==V&%hwSzz`7LxF*x38nzV2LLs?0RsR4 literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/89_VK_FORMAT_R16G16B16_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/89_VK_FORMAT_R16G16B16_SINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..c27fcc0a6640476e46fa7276d3733489bc069e77 GIT binary patch literal 76 scmeZafB+^24+cg?MlgpBD#pOT?*L-{2Lh0c00X}xGGBp#-wBxy09bnsw*UYD literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/90_VK_FORMAT_R16G16B16_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/90_VK_FORMAT_R16G16B16_SFLOAT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..80b84cf076e79861ec84df620b34afa7cb6de768 GIT binary patch literal 76 ucmeZafB+^24+cg?MlgpBD#pOTe*nU4*w4VwU@ySHe-N3kz`%b9nGXQ&ObI#w literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/91_VK_FORMAT_R16G16B16A16_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/91_VK_FORMAT_R16G16B16A16_UNORM.dfd new file mode 100644 index 0000000000000000000000000000000000000000..db6cb4dc28d4d0aa9ccc9db1feea778fb6a3b48f GIT binary patch literal 92 zcma!HfB+_j2nI$*Mlgp1D#pOT4`==V&%hwSzz`7LxF*x38nzVH(=n0YXI>9Rr3LK literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/96_VK_FORMAT_R16G16B16A16_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/96_VK_FORMAT_R16G16B16A16_SINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..fe848226f813483398195177a2e52b4806a83eb5 GIT binary patch literal 92 ycma!HfB+_j2nI$*Mlgp1D#pOT?*L-{2Lh0c00X}xGGBp#-wBy-z`*a1%m)BrDH3D= literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/97_VK_FORMAT_R16G16B16A16_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/97_VK_FORMAT_R16G16B16A16_SFLOAT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..4f1c40be85777a507c296640967961f5004bf609 GIT binary patch literal 92 zcma!HfB+_j2nI$*Mlgp1D#pOTe*nU4*w4VwU@ySHe-N3kz`%b9nQy?re;%0+0HeAL AXaE2J literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/98_VK_FORMAT_R32_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/98_VK_FORMAT_R32_UINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..bdb8ba48a43d29dbbd7155679a57c7fc8be6a3bc GIT binary patch literal 44 ccmdO4fB+^24F*O=Mlgp3D#pMd4`+d701w9iegFUf literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/99_VK_FORMAT_R32_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/99_VK_FORMAT_R32_SINT.dfd new file mode 100644 index 0000000000000000000000000000000000000000..9c3a5e3ccd8b2727a246381db5249a0e8099150c GIT binary patch literal 44 gcmdO4fB+^24F*O=Mlgp3D#pMd?*L-{2Lg}`04(hUx&QzG literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/9_VK_FORMAT_R8_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/9_VK_FORMAT_R8_UNORM.dfd new file mode 100644 index 0000000000000000000000000000000000000000..6b1c29a5e707b0d485330a9d80921124dff992e7 GIT binary patch literal 44 dcmdO4fB+^24F*O=Mlc6Lf|%?ONg(+jhyfBO0bu|D literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/testbidirectionalmapping.c.patch b/src/MagnumPlugins/KtxImageConverter/Test/testbidirectionalmapping.c.patch new file mode 100644 index 000000000..87c8b6d47 --- /dev/null +++ b/src/MagnumPlugins/KtxImageConverter/Test/testbidirectionalmapping.c.patch @@ -0,0 +1,56 @@ +diff --git a/testbidirectionalmapping.c b/testbidirectionalmapping.c +index 480d103..6c80e73 100644 +--- a/testbidirectionalmapping.c ++++ b/testbidirectionalmapping.c +@@ -568,6 +568,19 @@ uint32_t *getmap(enum VkFormat f) + } + } + ++void savemap(unsigned int format, const char* formatname, uint32_t* dfd) ++{ ++ // assumes little-endian system ++ size_t dfdsize = dfd[0]; ++ ++ char filename[256]; ++ sprintf(filename, "%d_%s.dfd", format, formatname); ++ ++ FILE *f = fopen(filename, "wb"); ++ fwrite(dfd, dfdsize, 1, f); ++ fclose(f); ++} ++ + enum VkFormat unmap(uint32_t *dfd) + { + #include "dfd2vk.inl" +@@ -581,6 +594,7 @@ int main() + VkFormat f = unmap(dfd); + if (i != f) printf("Input and output enums differ: %s (%d) -> %s (%d)\n", + formatname(i),i, formatname(f),f); ++ else savemap(i, formatname(f), dfd); + free((void *)dfd); + } + +@@ -591,6 +605,7 @@ int main() + VkFormat f = unmap(dfd); + if (i != f) printf("Input and output enums differ: %s (%d) -> %s (%d)\n", + formatname(i),i, formatname(f),f); ++ else savemap(i, formatname(f), dfd); + free((void *)dfd); + } + +@@ -600,6 +615,7 @@ int main() + VkFormat f = unmap(dfd); + if (i != f) printf("Input and output enums differ: %s (%d) -> %s (%d)\n", + formatname(i),i, formatname(f),f); ++ else savemap(i, formatname(f), dfd); + free((void *)dfd); + } + +@@ -609,6 +625,7 @@ int main() + VkFormat f = unmap(dfd); + if (i != f) printf("Input and output enums differ: %s (%d) -> %s (%d)\n", + formatname(i),i, formatname(f),f); ++ else savemap(i, formatname(f), dfd); + free((void *)dfd); + } + return 0; From 621fa4b7a44da4074a6fd15c88070359a46b89ee Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 25 Aug 2021 16:30:51 +0200 Subject: [PATCH 89/95] Ktx{Importer, ImageConverter}: remove some of the 2D compressed files We only needed that many to get some DFD test coverage, but we have a separate DFD test for all formats now --- .../Test/KtxImageConverterTest.cpp | 3 --- .../KtxImporter/Test/2d-compressed-bc2.bin | Bin 64 -> 0 bytes .../KtxImporter/Test/2d-compressed-bc2.ktx2 | Bin 288 -> 0 bytes .../KtxImporter/Test/2d-compressed-bc4.bin | Bin 32 -> 0 bytes .../KtxImporter/Test/2d-compressed-bc4.ktx2 | Bin 240 -> 0 bytes .../KtxImporter/Test/2d-compressed-bc5.bin | Bin 64 -> 0 bytes .../KtxImporter/Test/2d-compressed-bc5.ktx2 | Bin 288 -> 0 bytes src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt | 6 ------ .../KtxImporter/Test/KtxImporterTest.cpp | 3 --- src/MagnumPlugins/KtxImporter/Test/generate.sh | 5 ----- 10 files changed, 17 deletions(-) delete mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc2.bin delete mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc2.ktx2 delete mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc4.bin delete mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc4.ktx2 delete mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc5.bin delete mode 100644 src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc5.ktx2 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index 8b0b2f508..091a84196 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -204,10 +204,7 @@ const struct { } Convert2DCompressedData[]{ {"PVRTC", "2d-compressed-pvrtc.ktx2", CompressedPixelFormat::PvrtcRGBA4bppSrgb, {8, 8}}, {"BC1", "2d-compressed-bc1.ktx2", CompressedPixelFormat::Bc1RGBASrgb, {8, 8}}, - {"BC2", "2d-compressed-bc2.ktx2", CompressedPixelFormat::Bc2RGBASrgb, {8, 8}}, {"BC3", "2d-compressed-bc3.ktx2", CompressedPixelFormat::Bc3RGBASrgb, {8, 8}}, - {"BC4", "2d-compressed-bc4.ktx2", CompressedPixelFormat::Bc4RUnorm, {8, 8}}, - {"BC5", "2d-compressed-bc5.ktx2", CompressedPixelFormat::Bc5RGUnorm, {8, 8}}, {"ETC2", "2d-compressed-etc2.ktx2", CompressedPixelFormat::Etc2RGB8Srgb, {9, 10}}, {"ASTC", "2d-compressed-astc.ktx2", CompressedPixelFormat::Astc12x10RGBASrgb, {9, 10}} }; diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc2.bin b/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc2.bin deleted file mode 100644 index ac0494d369fcc31ecb1f4e6428e90d1458312de5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64 acmezW9|9PDFn|G+`=7r8m5(kD;{yQl03rk=?d4(2|3KitU=QOmfcQc{9?+TjMVYC2C5a`O`FRXQDL^r4 uhml@iC?7@x0E7ev`2YX_ diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc4.ktx2 b/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc4.ktx2 deleted file mode 100644 index 8f07f23b70a5d7ee496087d604de40a748c0953f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 240 zcmZ4O9TK5nWU!l;ONy(Tfq{V$h&iAbNPz)J41_@-1Bi8icnT0(05Lka02NR`qd|H= zfQdnap_!49fteX%4@A@+LIKJDKp+I9fzHY=%1q5GNi50C&toV`0g6dO#LJ5^OHzv% j0>XkqQY(BilN8EK^$hh4fRaG)|38GTuZPeuK8ywcW}zWR diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc5.bin b/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc5.bin deleted file mode 100644 index fbe2304e7c98014c672414ed5087488127134549..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64 VcmezWp8*Bb*Q0P5{-epG^8pK!2KfL0 diff --git a/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc5.ktx2 b/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc5.ktx2 deleted file mode 100644 index b19b3b9f723bc94d28c4b069597dc48b3b1421af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 288 zcmZ4O9TK5nWU!l;ONy(Pfq{V$h&iAbNPz)J41_@-1Bh*acnJ_&05Ljv02OdRqd|H= zfC;Fpg^`hgnVEq>03rk=?cuEdK;Xb&53`N|#1{g}0-c#(l$n}Wl30?NpT|&?0u+;m sh?f^-mZTOj1cU{Jq*nN3CMlGe>KW=8K#ctVA40>(`g#}(yF9vj0HiY}UH||9 diff --git a/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt b/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt index 09827361f..9b98b72b8 100644 --- a/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt @@ -62,14 +62,8 @@ corrade_add_test(KtxImporterTest KtxImporterTest.cpp 2d-compressed-astc.ktx2 2d-compressed-bc1.bin 2d-compressed-bc1.ktx2 - 2d-compressed-bc2.bin - 2d-compressed-bc2.ktx2 2d-compressed-bc3.bin 2d-compressed-bc3.ktx2 - 2d-compressed-bc4.bin - 2d-compressed-bc4.ktx2 - 2d-compressed-bc5.bin - 2d-compressed-bc5.ktx2 2d-compressed-etc2.bin 2d-compressed-etc2.ktx2 2d-compressed-layers.bin diff --git a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp index 5773dd648..e6fde5b79 100644 --- a/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp +++ b/src/MagnumPlugins/KtxImporter/Test/KtxImporterTest.cpp @@ -294,10 +294,7 @@ const struct { } CompressedImage2DData[]{ {"PVRTC", "2d-compressed-pvrtc.ktx2", CompressedPixelFormat::PvrtcRGBA4bppSrgb, {8, 8}}, {"BC1", "2d-compressed-bc1.ktx2", CompressedPixelFormat::Bc1RGBASrgb, {8, 8}}, - {"BC2", "2d-compressed-bc2.ktx2", CompressedPixelFormat::Bc2RGBASrgb, {8, 8}}, {"BC3", "2d-compressed-bc3.ktx2", CompressedPixelFormat::Bc3RGBASrgb, {8, 8}}, - {"BC4", "2d-compressed-bc4.ktx2", CompressedPixelFormat::Bc4RUnorm, {8, 8}}, - {"BC5", "2d-compressed-bc5.ktx2", CompressedPixelFormat::Bc5RGUnorm, {8, 8}}, {"ETC2", "2d-compressed-etc2.ktx2", CompressedPixelFormat::Etc2RGB8Srgb, {9, 10}}, {"ASTC", "2d-compressed-astc.ktx2", CompressedPixelFormat::Astc12x10RGBASrgb, {9, 10}} }; diff --git a/src/MagnumPlugins/KtxImporter/Test/generate.sh b/src/MagnumPlugins/KtxImporter/Test/generate.sh index 4ad0fcc0f..cff514818 100644 --- a/src/MagnumPlugins/KtxImporter/Test/generate.sh +++ b/src/MagnumPlugins/KtxImporter/Test/generate.sh @@ -88,14 +88,9 @@ printf '\x03\x00\x00\x00\x02\x00\x00\x00' | dd conv=notrunc of=3d-layers.ktx2 bs # Compressed # PVRTC and BC* don't support non-power-of-2 -# Generating a whole slew of formats here mainly for testing the DFD output of -# KtxImageConverter, best way to get known good data from an external tool PVRTexToolCLI -i pattern-pot.png -o 2d-compressed-pvrtc.ktx2 -f PVRTC1_4,UBN,sRGB PVRTexToolCLI -i pattern-pot.png -o 2d-compressed-bc1.ktx2 -f BC1,UBN,sRGB -PVRTexToolCLI -i pattern-pot.png -o 2d-compressed-bc2.ktx2 -f BC2,UBN,sRGB PVRTexToolCLI -i pattern-pot.png -o 2d-compressed-bc3.ktx2 -f BC3,UBN,sRGB -PVRTexToolCLI -i pattern-pot.png -o 2d-compressed-bc4.ktx2 -f BC4,UBN,sRGB -PVRTexToolCLI -i pattern-pot.png -o 2d-compressed-bc5.ktx2 -f BC5,UBN,sRGB PVRTexToolCLI -i pattern-uneven.png -o 2d-compressed-etc2.ktx2 -f ETC2_RGB,UBN,sRGB PVRTexToolCLI -i pattern-uneven.png -o 2d-compressed-astc.ktx2 -f ASTC_12x10,UBN,sRGB From c8390b285636eb1163402561052d636223cae97e Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 25 Aug 2021 16:31:30 +0200 Subject: [PATCH 90/95] Ktx{Importer, ImageConverter}: update list of test files Needed for Emscripten tests --- .../KtxImageConverter/Test/CMakeLists.txt | 48 ++++++++++++++++++- .../KtxImporter/Test/CMakeLists.txt | 5 ++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt b/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt index 6e92dd6bb..02813c03a 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt @@ -49,8 +49,54 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) +file(GLOB DFD_FILES "dfd/*.dfd") corrade_add_test(KtxImageConverterTest KtxImageConverterTest.cpp - LIBRARIES Magnum::Trade) + LIBRARIES Magnum::Trade + FILES + ${DFD_FILES} + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/1d.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/1d-mipmaps.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/1d-compressed-bc1.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/1d-compressed-bc1.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/1d-compressed-etc2.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/1d-compressed-etc2.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/1d-compressed-mipmaps.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/1d-compressed-mipmaps-mip0.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/1d-compressed-mipmaps-mip1.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/1d-compressed-mipmaps-mip2.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-rgb.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-rgb32.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-rgbf32.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-s8.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-d16.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-d24s8.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-d32fs8.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-mipmaps.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-mipmaps-incomplete.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-compressed-pvrtc.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-compressed-pvrtc.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc1.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc1.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc3.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-compressed-bc3.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-compressed-etc2.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-compressed-etc2.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-compressed-astc.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-compressed-astc.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-compressed-mipmaps.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-compressed-mipmaps-mip0.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-compressed-mipmaps-mip1.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-compressed-mipmaps-mip2.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-compressed-mipmaps-mip3.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/3d.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/3d-compressed.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/3d-compressed.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/3d-compressed-mipmaps.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/3d-compressed-mipmaps-mip0.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/3d-compressed-mipmaps-mip1.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/3d-compressed-mipmaps-mip2.bin + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/3d-compressed-mipmaps-mip3.bin) + target_include_directories(KtxImageConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$ ${PROJECT_SOURCE_DIR}/src) diff --git a/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt b/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt index 9b98b72b8..1d67de4fd 100644 --- a/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/KtxImporter/Test/CMakeLists.txt @@ -89,6 +89,11 @@ corrade_add_test(KtxImporterTest KtxImporterTest.cpp 2d-s8.ktx2 3d-compressed.bin 3d-compressed.ktx2 + 3d-compressed-mipmaps-mip0.bin + 3d-compressed-mipmaps-mip1.bin + 3d-compressed-mipmaps-mip2.bin + 3d-compressed-mipmaps-mip3.bin + 3d-compressed-mipmaps.ktx2 3d-layers.ktx2 3d-mipmaps.ktx2 3d.ktx2 From f0c5b404847b24af3563e445bd116336bd393c01 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 25 Aug 2021 16:31:47 +0200 Subject: [PATCH 91/95] KtxImageConverter: enable on Emscripten CI again --- package/ci/emscripten.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/ci/emscripten.sh b/package/ci/emscripten.sh index c34964f3c..e0e6fd5d4 100755 --- a/package/ci/emscripten.sh +++ b/package/ci/emscripten.sh @@ -97,7 +97,7 @@ cmake .. \ -DWITH_ICOIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=OFF \ -DWITH_JPEGIMPORTER=OFF \ - -DWITH_KTXIMAGECONVERTER=OFF \ + -DWITH_KTXIMAGECONVERTER=ON \ -DWITH_KTXIMPORTER=ON \ -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ From cc3a023c32f83ac965912e8aa06ce41d97c2d567 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 25 Aug 2021 16:54:28 +0200 Subject: [PATCH 92/95] KtxImageConverter: fix GCC 4.8 error --- .../KtxImageConverter/KtxImageConverter.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp index 040dcebad..01e08a5a9 100644 --- a/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp +++ b/src/MagnumPlugins/KtxImageConverter/KtxImageConverter.cpp @@ -691,6 +691,12 @@ void endianSwap(Containers::ArrayView data, UnsignedInt typeSize) { CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } +using namespace Containers::Literals; + +/* Having this inside convertLevels() leads to errors with GCC 4.8: + "cannot initialize aggregate [...] with a compound literal" */ +constexpr Containers::StringView ValidOrientations[3]{"rl"_s, "du"_s, "io"_s}; + /* Using a template template parameter to deduce the image dimensions while matching both ImageView and CompressedImageView. Matching on the ImageView typedefs doesn't work, so we need the extra parameter of BasicImageView. */ @@ -713,7 +719,6 @@ Containers::Array convertLevels(Containers::ArrayView convertLevels(Containers::ArrayView Date: Wed, 25 Aug 2021 17:46:08 +0200 Subject: [PATCH 93/95] KtxImporter: clarify test file comments --- src/MagnumPlugins/KtxImporter/Test/README.md | 12 +++++++++++- src/MagnumPlugins/KtxImporter/Test/generate.sh | 4 +--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/MagnumPlugins/KtxImporter/Test/README.md b/src/MagnumPlugins/KtxImporter/Test/README.md index 60e07fdc3..f85ded477 100644 --- a/src/MagnumPlugins/KtxImporter/Test/README.md +++ b/src/MagnumPlugins/KtxImporter/Test/README.md @@ -5,4 +5,14 @@ Most of the `*.ktx2?` files are created using [Khronos Texture Tools](https://gi Install both of those and then execute the `generate.sh` script to create the test files. -The remaining files (various .ktx2 files, .bin files for compressed block data) are generated by running both KtxImageConverterTest and KtxImporterTest (in that order!) with `--save-diagnostic [path/to/this/folder]` until they stop failing. Use a third-party viewer to make sure the .ktx2 files load correctly and look plausible. +The remaining files (various .ktx2 files, .bin files for compressed block data) are generated by running both KtxImageConverterTest and KtxImporterTest with `--save-diagnostic [path/to/this/folder]` until they stop failing. The order should be: + +```bash +# Generates 1D and 2D .bin files +# Needs to be run multiple times until all mips are saved +KtxImporterTest -S [path/to/KtxImporter/Test] +# Generates depth/stencil .ktx2 and 3d[-compressed]-mipmaps.ktx +KtxImageConverterTest -S [path/to/KtxImporter/Test] +``` + +Use a third-party viewer to make sure the .ktx2 files load correctly and look plausible. diff --git a/src/MagnumPlugins/KtxImporter/Test/generate.sh b/src/MagnumPlugins/KtxImporter/Test/generate.sh index cff514818..80f019126 100644 --- a/src/MagnumPlugins/KtxImporter/Test/generate.sh +++ b/src/MagnumPlugins/KtxImporter/Test/generate.sh @@ -110,9 +110,7 @@ printf 'i' | dd conv=notrunc of=3d-compressed.ktx2 bs=1 seek=169 # Unlike 3d-mipmaps.ktx2, we don't have any verifiable data to check in viewers # for 3d-compressed-mipmaps.ktx to make sure the converter output is plausible. # Instead of randomly generating bytes, some of the existing 2D ETC2 .bin files -# are used to manually create the mipmaps. Those are the only files that aren't -# generated by either this script or by KtxImporter/KtxImageConvert with -# --save-diagnostic. Horrible hack, but it's better than nothing. +# are used to manually create the mipmaps. A hack, but better than nothing. # We can simply repeat all the blocks for each z-slice. # Last slice (if any) is all zeros to check the order. yes 2d-compressed-mipmaps-mip0.bin | head -n 4 | xargs cat > 3d-compressed-mipmaps-mip0.bin From 91d87a1c11020ac48d896fee0874c70edce13cbf Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Wed, 25 Aug 2021 17:47:05 +0200 Subject: [PATCH 94/95] KtxImageConverter: fix DFD file reading in tests under Emscripten --- src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt | 3 ++- .../KtxImageConverter/Test/KtxImageConverterTest.cpp | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt b/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt index 02813c03a..68dcb3569 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt @@ -49,7 +49,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) -file(GLOB DFD_FILES "dfd/*.dfd") +file(GLOB DFD_FILES "${CMAKE_CURRENT_SOURCE_DIR}/dfd/*.dfd") corrade_add_test(KtxImageConverterTest KtxImageConverterTest.cpp LIBRARIES Magnum::Trade FILES @@ -89,6 +89,7 @@ corrade_add_test(KtxImageConverterTest KtxImageConverterTest.cpp ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-compressed-mipmaps-mip2.bin ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/2d-compressed-mipmaps-mip3.bin ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/3d.ktx2 + ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/3d-mipmaps.ktx2 ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/3d-compressed.ktx2 ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/3d-compressed.bin ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/3d-compressed-mipmaps.ktx2 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index 091a84196..904a83d82 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -359,8 +359,14 @@ KtxImageConverterTest::KtxImageConverterTest() { CORRADE_INTERNAL_ASSERT_OUTPUT(_importerManager.load(KTXIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); #endif - /* Map VkFormat to DFD test file. The VkFormat value is in the file name. */ + #ifdef CORRADE_TARGET_EMSCRIPTEN + /* Test files are embedded into the root of the virtual filesystem */ + const std::string folder = KTXIMAGECONVERTER_TEST_DIR; + #else const std::string folder = Utility::Directory::join(KTXIMAGECONVERTER_TEST_DIR, "dfd"); + #endif + + /* Map VkFormat to DFD test file. The VkFormat value is in the file name. */ const auto files = Utility::Directory::list(folder, Utility::Directory::Flag::SkipDirectories | Utility::Directory::Flag::SkipSpecial); CORRADE_INTERNAL_ASSERT(!files.empty()); for(const auto& f: files) { From 76dd7692e6d0209a4628260c19a9b119b1dba3b7 Mon Sep 17 00:00:00 2001 From: Pablo Escobar Date: Tue, 31 Aug 2021 16:06:14 +0200 Subject: [PATCH 95/95] KtxImageConverter: merge DFD test files --- .../KtxImageConverter/Test/CMakeLists.txt | 3 +- .../Test/KtxImageConverterTest.cpp | 51 ++++++++---------- .../KtxImageConverter/Test/README.md | 2 +- .../KtxImageConverter/Test/dfd-data.bin | Bin 0 -> 12832 bytes ..._VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG.dfd | Bin 44 -> 0 bytes ..._VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG.dfd | Bin 44 -> 0 bytes ...4_VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG.dfd | Bin 44 -> 0 bytes ...5_VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG.dfd | Bin 44 -> 0 bytes ...00_VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT.dfd | Bin 44 -> 0 bytes ...01_VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT.dfd | Bin 44 -> 0 bytes ...02_VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT.dfd | Bin 44 -> 0 bytes ...03_VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT.dfd | Bin 44 -> 0 bytes ...04_VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT.dfd | Bin 44 -> 0 bytes ...05_VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT.dfd | Bin 44 -> 0 bytes ...06_VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT.dfd | Bin 44 -> 0 bytes ...07_VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT.dfd | Bin 44 -> 0 bytes ...8_VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT.dfd | Bin 44 -> 0 bytes ...9_VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT.dfd | Bin 44 -> 0 bytes ...0_VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT.dfd | Bin 44 -> 0 bytes ..._VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT.dfd | Bin 44 -> 0 bytes ..._VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT.dfd | Bin 44 -> 0 bytes ..._VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT.dfd | Bin 44 -> 0 bytes .../Test/dfd/100_VK_FORMAT_R32_SFLOAT.dfd | Bin 44 -> 0 bytes .../Test/dfd/101_VK_FORMAT_R32G32_UINT.dfd | Bin 60 -> 0 bytes .../Test/dfd/102_VK_FORMAT_R32G32_SINT.dfd | Bin 60 -> 0 bytes .../Test/dfd/103_VK_FORMAT_R32G32_SFLOAT.dfd | Bin 60 -> 0 bytes .../Test/dfd/104_VK_FORMAT_R32G32B32_UINT.dfd | Bin 76 -> 0 bytes .../Test/dfd/105_VK_FORMAT_R32G32B32_SINT.dfd | Bin 76 -> 0 bytes .../dfd/106_VK_FORMAT_R32G32B32_SFLOAT.dfd | Bin 76 -> 0 bytes .../dfd/107_VK_FORMAT_R32G32B32A32_UINT.dfd | Bin 92 -> 0 bytes .../dfd/108_VK_FORMAT_R32G32B32A32_SINT.dfd | Bin 92 -> 0 bytes .../dfd/109_VK_FORMAT_R32G32B32A32_SFLOAT.dfd | Bin 92 -> 0 bytes .../Test/dfd/10_VK_FORMAT_R8_SNORM.dfd | Bin 44 -> 0 bytes .../Test/dfd/124_VK_FORMAT_D16_UNORM.dfd | Bin 44 -> 0 bytes .../dfd/125_VK_FORMAT_X8_D24_UNORM_PACK32.dfd | Bin 44 -> 0 bytes .../Test/dfd/126_VK_FORMAT_D32_SFLOAT.dfd | Bin 44 -> 0 bytes .../Test/dfd/127_VK_FORMAT_S8_UINT.dfd | Bin 44 -> 0 bytes .../dfd/128_VK_FORMAT_D16_UNORM_S8_UINT.dfd | Bin 60 -> 0 bytes .../dfd/129_VK_FORMAT_D24_UNORM_S8_UINT.dfd | Bin 60 -> 0 bytes .../dfd/130_VK_FORMAT_D32_SFLOAT_S8_UINT.dfd | Bin 60 -> 0 bytes .../dfd/131_VK_FORMAT_BC1_RGB_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../dfd/132_VK_FORMAT_BC1_RGB_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes .../133_VK_FORMAT_BC1_RGBA_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../dfd/134_VK_FORMAT_BC1_RGBA_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes .../dfd/135_VK_FORMAT_BC2_UNORM_BLOCK.dfd | Bin 60 -> 0 bytes .../Test/dfd/136_VK_FORMAT_BC2_SRGB_BLOCK.dfd | Bin 60 -> 0 bytes .../dfd/137_VK_FORMAT_BC3_UNORM_BLOCK.dfd | Bin 60 -> 0 bytes .../Test/dfd/138_VK_FORMAT_BC3_SRGB_BLOCK.dfd | Bin 60 -> 0 bytes .../dfd/139_VK_FORMAT_BC4_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../Test/dfd/13_VK_FORMAT_R8_UINT.dfd | Bin 44 -> 0 bytes .../dfd/140_VK_FORMAT_BC4_SNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../dfd/141_VK_FORMAT_BC5_UNORM_BLOCK.dfd | Bin 60 -> 0 bytes .../dfd/142_VK_FORMAT_BC5_SNORM_BLOCK.dfd | Bin 60 -> 0 bytes .../dfd/143_VK_FORMAT_BC6H_UFLOAT_BLOCK.dfd | Bin 44 -> 0 bytes .../dfd/144_VK_FORMAT_BC6H_SFLOAT_BLOCK.dfd | Bin 44 -> 0 bytes .../dfd/145_VK_FORMAT_BC7_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../Test/dfd/146_VK_FORMAT_BC7_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes .../147_VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../148_VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes ...49_VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK.dfd | Bin 60 -> 0 bytes .../Test/dfd/14_VK_FORMAT_R8_SINT.dfd | Bin 44 -> 0 bytes ...150_VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK.dfd | Bin 60 -> 0 bytes ...51_VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK.dfd | Bin 60 -> 0 bytes ...152_VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK.dfd | Bin 60 -> 0 bytes .../dfd/153_VK_FORMAT_EAC_R11_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../dfd/154_VK_FORMAT_EAC_R11_SNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../155_VK_FORMAT_EAC_R11G11_UNORM_BLOCK.dfd | Bin 60 -> 0 bytes .../156_VK_FORMAT_EAC_R11G11_SNORM_BLOCK.dfd | Bin 60 -> 0 bytes .../157_VK_FORMAT_ASTC_4x4_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../dfd/158_VK_FORMAT_ASTC_4x4_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes .../159_VK_FORMAT_ASTC_5x4_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../Test/dfd/15_VK_FORMAT_R8_SRGB.dfd | Bin 44 -> 0 bytes .../dfd/160_VK_FORMAT_ASTC_5x4_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes .../161_VK_FORMAT_ASTC_5x5_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../dfd/162_VK_FORMAT_ASTC_5x5_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes .../163_VK_FORMAT_ASTC_6x5_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../dfd/164_VK_FORMAT_ASTC_6x5_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes .../165_VK_FORMAT_ASTC_6x6_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../dfd/166_VK_FORMAT_ASTC_6x6_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes .../167_VK_FORMAT_ASTC_8x5_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../dfd/168_VK_FORMAT_ASTC_8x5_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes .../169_VK_FORMAT_ASTC_8x6_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../Test/dfd/16_VK_FORMAT_R8G8_UNORM.dfd | Bin 60 -> 0 bytes .../dfd/170_VK_FORMAT_ASTC_8x6_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes .../171_VK_FORMAT_ASTC_8x8_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../dfd/172_VK_FORMAT_ASTC_8x8_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes .../173_VK_FORMAT_ASTC_10x5_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../174_VK_FORMAT_ASTC_10x5_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes .../175_VK_FORMAT_ASTC_10x6_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../176_VK_FORMAT_ASTC_10x6_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes .../177_VK_FORMAT_ASTC_10x8_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../178_VK_FORMAT_ASTC_10x8_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes .../179_VK_FORMAT_ASTC_10x10_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../Test/dfd/17_VK_FORMAT_R8G8_SNORM.dfd | Bin 60 -> 0 bytes .../180_VK_FORMAT_ASTC_10x10_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes .../181_VK_FORMAT_ASTC_12x10_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../182_VK_FORMAT_ASTC_12x10_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes .../183_VK_FORMAT_ASTC_12x12_UNORM_BLOCK.dfd | Bin 44 -> 0 bytes .../184_VK_FORMAT_ASTC_12x12_SRGB_BLOCK.dfd | Bin 44 -> 0 bytes .../Test/dfd/20_VK_FORMAT_R8G8_UINT.dfd | Bin 60 -> 0 bytes .../Test/dfd/21_VK_FORMAT_R8G8_SINT.dfd | Bin 60 -> 0 bytes .../Test/dfd/22_VK_FORMAT_R8G8_SRGB.dfd | Bin 60 -> 0 bytes .../Test/dfd/23_VK_FORMAT_R8G8B8_UNORM.dfd | Bin 76 -> 0 bytes .../Test/dfd/24_VK_FORMAT_R8G8B8_SNORM.dfd | Bin 76 -> 0 bytes .../Test/dfd/27_VK_FORMAT_R8G8B8_UINT.dfd | Bin 76 -> 0 bytes .../Test/dfd/28_VK_FORMAT_R8G8B8_SINT.dfd | Bin 76 -> 0 bytes .../Test/dfd/29_VK_FORMAT_R8G8B8_SRGB.dfd | Bin 76 -> 0 bytes .../Test/dfd/37_VK_FORMAT_R8G8B8A8_UNORM.dfd | Bin 92 -> 0 bytes .../Test/dfd/38_VK_FORMAT_R8G8B8A8_SNORM.dfd | Bin 92 -> 0 bytes .../Test/dfd/41_VK_FORMAT_R8G8B8A8_UINT.dfd | Bin 92 -> 0 bytes .../Test/dfd/42_VK_FORMAT_R8G8B8A8_SINT.dfd | Bin 92 -> 0 bytes .../Test/dfd/43_VK_FORMAT_R8G8B8A8_SRGB.dfd | Bin 92 -> 0 bytes .../Test/dfd/70_VK_FORMAT_R16_UNORM.dfd | Bin 44 -> 0 bytes .../Test/dfd/71_VK_FORMAT_R16_SNORM.dfd | Bin 44 -> 0 bytes .../Test/dfd/74_VK_FORMAT_R16_UINT.dfd | Bin 44 -> 0 bytes .../Test/dfd/75_VK_FORMAT_R16_SINT.dfd | Bin 44 -> 0 bytes .../Test/dfd/76_VK_FORMAT_R16_SFLOAT.dfd | Bin 44 -> 0 bytes .../Test/dfd/77_VK_FORMAT_R16G16_UNORM.dfd | Bin 60 -> 0 bytes .../Test/dfd/78_VK_FORMAT_R16G16_SNORM.dfd | Bin 60 -> 0 bytes .../Test/dfd/81_VK_FORMAT_R16G16_UINT.dfd | Bin 60 -> 0 bytes .../Test/dfd/82_VK_FORMAT_R16G16_SINT.dfd | Bin 60 -> 0 bytes .../Test/dfd/83_VK_FORMAT_R16G16_SFLOAT.dfd | Bin 60 -> 0 bytes .../Test/dfd/84_VK_FORMAT_R16G16B16_UNORM.dfd | Bin 76 -> 0 bytes .../Test/dfd/85_VK_FORMAT_R16G16B16_SNORM.dfd | Bin 76 -> 0 bytes .../Test/dfd/88_VK_FORMAT_R16G16B16_UINT.dfd | Bin 76 -> 0 bytes .../Test/dfd/89_VK_FORMAT_R16G16B16_SINT.dfd | Bin 76 -> 0 bytes .../dfd/90_VK_FORMAT_R16G16B16_SFLOAT.dfd | Bin 76 -> 0 bytes .../dfd/91_VK_FORMAT_R16G16B16A16_UNORM.dfd | Bin 92 -> 0 bytes .../dfd/92_VK_FORMAT_R16G16B16A16_SNORM.dfd | Bin 92 -> 0 bytes .../dfd/95_VK_FORMAT_R16G16B16A16_UINT.dfd | Bin 92 -> 0 bytes .../dfd/96_VK_FORMAT_R16G16B16A16_SINT.dfd | Bin 92 -> 0 bytes .../dfd/97_VK_FORMAT_R16G16B16A16_SFLOAT.dfd | Bin 92 -> 0 bytes .../Test/dfd/98_VK_FORMAT_R32_UINT.dfd | Bin 44 -> 0 bytes .../Test/dfd/99_VK_FORMAT_R32_SINT.dfd | Bin 44 -> 0 bytes .../Test/dfd/9_VK_FORMAT_R8_UNORM.dfd | Bin 44 -> 0 bytes .../Test/testbidirectionalmapping.c.patch | 46 +++++++++------- 136 files changed, 50 insertions(+), 52 deletions(-) create mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd-data.bin delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054000_VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054001_VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054004_VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054005_VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066000_VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066001_VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066002_VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066003_VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066004_VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066005_VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066006_VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066007_VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066008_VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066009_VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066010_VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066011_VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066012_VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066013_VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/100_VK_FORMAT_R32_SFLOAT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/101_VK_FORMAT_R32G32_UINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/102_VK_FORMAT_R32G32_SINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/103_VK_FORMAT_R32G32_SFLOAT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/104_VK_FORMAT_R32G32B32_UINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/105_VK_FORMAT_R32G32B32_SINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/106_VK_FORMAT_R32G32B32_SFLOAT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/107_VK_FORMAT_R32G32B32A32_UINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/108_VK_FORMAT_R32G32B32A32_SINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/109_VK_FORMAT_R32G32B32A32_SFLOAT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/10_VK_FORMAT_R8_SNORM.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/124_VK_FORMAT_D16_UNORM.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/125_VK_FORMAT_X8_D24_UNORM_PACK32.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/126_VK_FORMAT_D32_SFLOAT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/127_VK_FORMAT_S8_UINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/128_VK_FORMAT_D16_UNORM_S8_UINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/129_VK_FORMAT_D24_UNORM_S8_UINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/130_VK_FORMAT_D32_SFLOAT_S8_UINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/131_VK_FORMAT_BC1_RGB_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/132_VK_FORMAT_BC1_RGB_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/133_VK_FORMAT_BC1_RGBA_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/134_VK_FORMAT_BC1_RGBA_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/135_VK_FORMAT_BC2_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/136_VK_FORMAT_BC2_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/137_VK_FORMAT_BC3_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/138_VK_FORMAT_BC3_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/139_VK_FORMAT_BC4_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/13_VK_FORMAT_R8_UINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/140_VK_FORMAT_BC4_SNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/141_VK_FORMAT_BC5_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/142_VK_FORMAT_BC5_SNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/143_VK_FORMAT_BC6H_UFLOAT_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/144_VK_FORMAT_BC6H_SFLOAT_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/145_VK_FORMAT_BC7_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/146_VK_FORMAT_BC7_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/147_VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/148_VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/149_VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/14_VK_FORMAT_R8_SINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/150_VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/151_VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/152_VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/153_VK_FORMAT_EAC_R11_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/154_VK_FORMAT_EAC_R11_SNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/155_VK_FORMAT_EAC_R11G11_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/156_VK_FORMAT_EAC_R11G11_SNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/157_VK_FORMAT_ASTC_4x4_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/158_VK_FORMAT_ASTC_4x4_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/159_VK_FORMAT_ASTC_5x4_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/15_VK_FORMAT_R8_SRGB.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/160_VK_FORMAT_ASTC_5x4_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/161_VK_FORMAT_ASTC_5x5_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/162_VK_FORMAT_ASTC_5x5_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/163_VK_FORMAT_ASTC_6x5_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/164_VK_FORMAT_ASTC_6x5_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/165_VK_FORMAT_ASTC_6x6_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/166_VK_FORMAT_ASTC_6x6_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/167_VK_FORMAT_ASTC_8x5_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/168_VK_FORMAT_ASTC_8x5_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/169_VK_FORMAT_ASTC_8x6_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/16_VK_FORMAT_R8G8_UNORM.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/170_VK_FORMAT_ASTC_8x6_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/171_VK_FORMAT_ASTC_8x8_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/172_VK_FORMAT_ASTC_8x8_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/173_VK_FORMAT_ASTC_10x5_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/174_VK_FORMAT_ASTC_10x5_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/175_VK_FORMAT_ASTC_10x6_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/176_VK_FORMAT_ASTC_10x6_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/177_VK_FORMAT_ASTC_10x8_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/178_VK_FORMAT_ASTC_10x8_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/179_VK_FORMAT_ASTC_10x10_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/17_VK_FORMAT_R8G8_SNORM.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/180_VK_FORMAT_ASTC_10x10_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/181_VK_FORMAT_ASTC_12x10_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/182_VK_FORMAT_ASTC_12x10_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/183_VK_FORMAT_ASTC_12x12_UNORM_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/184_VK_FORMAT_ASTC_12x12_SRGB_BLOCK.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/20_VK_FORMAT_R8G8_UINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/21_VK_FORMAT_R8G8_SINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/22_VK_FORMAT_R8G8_SRGB.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/23_VK_FORMAT_R8G8B8_UNORM.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/24_VK_FORMAT_R8G8B8_SNORM.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/27_VK_FORMAT_R8G8B8_UINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/28_VK_FORMAT_R8G8B8_SINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/29_VK_FORMAT_R8G8B8_SRGB.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/37_VK_FORMAT_R8G8B8A8_UNORM.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/38_VK_FORMAT_R8G8B8A8_SNORM.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/41_VK_FORMAT_R8G8B8A8_UINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/42_VK_FORMAT_R8G8B8A8_SINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/43_VK_FORMAT_R8G8B8A8_SRGB.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/70_VK_FORMAT_R16_UNORM.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/71_VK_FORMAT_R16_SNORM.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/74_VK_FORMAT_R16_UINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/75_VK_FORMAT_R16_SINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/76_VK_FORMAT_R16_SFLOAT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/77_VK_FORMAT_R16G16_UNORM.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/78_VK_FORMAT_R16G16_SNORM.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/81_VK_FORMAT_R16G16_UINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/82_VK_FORMAT_R16G16_SINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/83_VK_FORMAT_R16G16_SFLOAT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/84_VK_FORMAT_R16G16B16_UNORM.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/85_VK_FORMAT_R16G16B16_SNORM.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/88_VK_FORMAT_R16G16B16_UINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/89_VK_FORMAT_R16G16B16_SINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/90_VK_FORMAT_R16G16B16_SFLOAT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/91_VK_FORMAT_R16G16B16A16_UNORM.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/92_VK_FORMAT_R16G16B16A16_SNORM.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/95_VK_FORMAT_R16G16B16A16_UINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/96_VK_FORMAT_R16G16B16A16_SINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/97_VK_FORMAT_R16G16B16A16_SFLOAT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/98_VK_FORMAT_R32_UINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/99_VK_FORMAT_R32_SINT.dfd delete mode 100644 src/MagnumPlugins/KtxImageConverter/Test/dfd/9_VK_FORMAT_R8_UNORM.dfd diff --git a/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt b/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt index 68dcb3569..8b17313c8 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/KtxImageConverter/Test/CMakeLists.txt @@ -49,11 +49,10 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) -file(GLOB DFD_FILES "${CMAKE_CURRENT_SOURCE_DIR}/dfd/*.dfd") corrade_add_test(KtxImageConverterTest KtxImageConverterTest.cpp LIBRARIES Magnum::Trade FILES - ${DFD_FILES} + dfd-data.bin ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/1d.ktx2 ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/1d-mipmaps.ktx2 ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/KtxImporter/Test/1d-compressed-bc1.ktx2 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp index 904a83d82..675598c8d 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp +++ b/src/MagnumPlugins/KtxImageConverter/Test/KtxImageConverterTest.cpp @@ -116,7 +116,8 @@ struct KtxImageConverterTest: TestSuite::Tester { PluginManager::Manager _converterManager{"nonexistent"}; PluginManager::Manager _importerManager{"nonexistent"}; - std::unordered_map> dataFormatDescriptors; + Containers::Array dfdData; + std::unordered_map> dfdMap; }; using namespace Containers::Literals; @@ -359,31 +360,23 @@ KtxImageConverterTest::KtxImageConverterTest() { CORRADE_INTERNAL_ASSERT_OUTPUT(_importerManager.load(KTXIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); #endif - #ifdef CORRADE_TARGET_EMSCRIPTEN - /* Test files are embedded into the root of the virtual filesystem */ - const std::string folder = KTXIMAGECONVERTER_TEST_DIR; - #else - const std::string folder = Utility::Directory::join(KTXIMAGECONVERTER_TEST_DIR, "dfd"); - #endif - - /* Map VkFormat to DFD test file. The VkFormat value is in the file name. */ - const auto files = Utility::Directory::list(folder, Utility::Directory::Flag::SkipDirectories | Utility::Directory::Flag::SkipSpecial); - CORRADE_INTERNAL_ASSERT(!files.empty()); - for(const auto& f: files) { - Containers::StringView file{f}; - if(file.hasSuffix(".dfd"_s)) { - const auto found = file.find("_VK_FORMAT_"_s); - CORRADE_INTERNAL_ASSERT(!found.isEmpty()); - const std::size_t prefix = found.data() - f.data(); - CORRADE_INTERNAL_ASSERT(prefix > 0); - std::size_t read = 0; - const Implementation::VkFormat format = std::stoi(file.prefix(prefix), &read); - CORRADE_INTERNAL_ASSERT(read == prefix); - auto dfd = Utility::Directory::read(Utility::Directory::join(folder, file)); - CORRADE_INTERNAL_ASSERT(!dfd.empty()); - dataFormatDescriptors.emplace(format, std::move(dfd)); - } + /* Extract VkFormat and DFD content from merged DFD file */ + dfdData = Utility::Directory::read(Utility::Directory::join(KTXIMAGECONVERTER_TEST_DIR, "dfd-data.bin")); + CORRADE_INTERNAL_ASSERT(!dfdData.empty()); + CORRADE_INTERNAL_ASSERT(dfdData.size()%4 == 0); + std::size_t offset = 0; + while(offset < dfdData.size()) { + /* Each entry is a VkFormat, followed directly by the DFD. The first + uint32_t of the DFD is its size. */ + const Implementation::VkFormat format = *reinterpret_cast(dfdData.data() + offset); + offset += sizeof(format); + const UnsignedInt size = *reinterpret_cast(dfdData.data() + offset); + CORRADE_INTERNAL_ASSERT(size > 0); + CORRADE_INTERNAL_ASSERT(size%4 == 0); + dfdMap.emplace(format, dfdData.suffix(offset).prefix(size)); + offset += size; } + CORRADE_INTERNAL_ASSERT(offset == dfdData.size()); } void KtxImageConverterTest::supportedFormat() { @@ -523,8 +516,8 @@ void KtxImageConverterTest::dataFormatDescriptor() { const Implementation::VkFormat vkFormat = Utility::Endianness::littleEndian(header.vkFormat); const auto dfd = readDataFormatDescriptor(output); - CORRADE_COMPARE(dataFormatDescriptors.count(vkFormat), 1); - CORRADE_COMPARE_AS(dfd, dataFormatDescriptors[vkFormat], TestSuite::Compare::Container); + CORRADE_COMPARE(dfdMap.count(vkFormat), 1); + CORRADE_COMPARE_AS(dfd, dfdMap[vkFormat], TestSuite::Compare::Container); } } @@ -549,8 +542,8 @@ void KtxImageConverterTest::dataFormatDescriptorCompressed() { const Implementation::VkFormat vkFormat = Utility::Endianness::littleEndian(header.vkFormat); const auto dfd = readDataFormatDescriptor(output); - CORRADE_COMPARE(dataFormatDescriptors.count(vkFormat), 1); - CORRADE_COMPARE_AS(dfd, dataFormatDescriptors[vkFormat], TestSuite::Compare::Container); + CORRADE_COMPARE(dfdMap.count(vkFormat), 1); + CORRADE_COMPARE_AS(dfd, dfdMap[vkFormat], TestSuite::Compare::Container); } } } diff --git a/src/MagnumPlugins/KtxImageConverter/Test/README.md b/src/MagnumPlugins/KtxImageConverter/Test/README.md index a031dc7ac..1dc687054 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/README.md +++ b/src/MagnumPlugins/KtxImageConverter/Test/README.md @@ -1,7 +1,7 @@ Updating test files =================== -The files in the dfd folder are created using a patch to `testbidirectionalmapping` from [dfdutils](https://github.com/KhronosGroup/dfdutils): +The file dfd-data.bin is created using a patch to `testbidirectionalmapping` from [dfdutils](https://github.com/KhronosGroup/dfdutils): ```bash git clone https://github.com/KhronosGroup/dfdutils.git diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd-data.bin b/src/MagnumPlugins/KtxImageConverter/Test/dfd-data.bin new file mode 100644 index 0000000000000000000000000000000000000000..22d191bc01e6c26d853d4177e246171575da577a GIT binary patch literal 12832 zcmbuF*^^{N6~?owyQjLk2YSF^W^{mVPz;wbM4t@lu(AoakWqrg2!x3cP}~sr1=p@& zbKh_ml*Ji$1Q*aZYj`CBcSV8!!QD68@5`*ro9CXqnN^w*S$$94-}&;KlQ;9;TQfal z%qBWp-#pLsdOdgSKY6$i57%HVngw^BAGG`EM`)n+&1KP_-}|;e%(bb1JVkrCrN6tS z&(Y@8_k-cHLtl))2=4IIqi)@8mQmn@a_9`k8nm=FF3%Kdf6XWdyUC!U7 z=`9r`hrEgF z?`d)VV$y~g`nH2VE9e#8l0Le#v$KuAC!tq(;;wPblhG@@B@Fo;@=QkZzIpyOyhO8XwP} zj$YBT6btoj*E_@)&Yz+5d^!Ht`kkrt7&Y!X?vo#~-kJG`B=0kDpQ7~Ce8l)zpIZIS0doYMExn&Nce#1j z#~|+49RI246@3dN)<5JQ<|AB>!9HLH7pDBvVN~>qyVfyZv0gk6{TM&(XP{T~Es&Yl zQ;0vz=QEYQ59j}=KbM4Stp6eOO8hbZS+19~KIETO*FPUfX?|^bKGY}7_qjMZYy2Ja zOL^CF^ZOi}spz}-!+eG7GuZ9*8R8G;pR4rM{LWi{F~84O`plBg?V$}DeQdHTX6ON& z-hsW>JyCg<_L-&^uYZ{4JoIvXhOR!n{~%*1Lw}2E!G^v~ZRor88|w1{oUG`__!)Q_ zKkYPr+G+f>N9YxO7k`K+#2@Ssf3Vx}+qymo-&o%?{%QSVe!2Q@!L<^O67RnEwUnv*?F>4%hh!5VY|x&_^%F(WbNI`#`haG>;Q9PwX>=TH8Q;M;ujajsd54xArqSM)8% znRR}ke__hM8b(E*xNEb_R~kS0E72?Z7D(rMY0qDXKg{PtR&&5{t)=<>iQGv&sq=Y zi}m#uc;?nub3KooIzO22ThS}L1rqapTb-Yf5AnYpN4e{}nJ*cSH6w=Q8V}xqUh%ht zLw(`DmD=xwQSqOU5AnkPL~6ea#$o@3cs}6oe!i9CADfX_U+;!fMbA>VD<9~I_4S^* z-h_Hx`9V*tulIt?UH==DaBaSPCuStp*Y(h-=vxjmYyQv|>+5}W{R#DFtq1hQ`g%V+ zbMx2SzgEUn_ia2{H7iGhg)v9_%*PQU1z>*K#)G0zU(ii+kz^C~ioyMQ+?{WQ8 z-@0F5;Dko|qxdWR{dkE#FLT#&{vX4c)c=M#$N#wUpKtt6z(1Y88!Pko$vSU=u#J)L z_NQ===693M&%ykd)w6rZ%#<|57&qA|0?_|^#$P@^ZgnQE4-cJ?s~ipMy|d| zep5Vu{oh{qw^M=a_5XF8Oyj+~a(#Y7@$Gub>+=r9<9~2q|8Me>{GE#L7x}vs4}IL+ zy7cc>JoF3vHx=JAO9lRz;`>GZxZA6-%>pE3;eeg-!p?k{NGW0zsP@A@z5{C z^F75wzrcTA@z5W1*XIX{hkk*-NAW!~EY#SV> z9{L6TCyIyuu)F?0RXp?y{QZjWnbiXSfa3c_{%4AZ{%UvpKUX~T3;bVhzwNwq|2&5M zb5MA`{cDCVJrDn_#UHQe|2@N(^#9S~kM*!#3i1Cl!el&6Tl>beRm-~a@f1BZ3_Y1-QF2~FDF8JSP_||?d_&? diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054004_VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054004_VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG.dfd deleted file mode 100644 index 614d6e32dddc3b9c81600c36a4e342d07df6a718..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TdF*ObqPI3=AAlF$M;EIO{(U001#;1xo+` diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054005_VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000054005_VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG.dfd deleted file mode 100644 index feded5796471665b6a861565aa337e0b8f81b6cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TdF*ObpD-3=AAlF$M;EIO{(U001!j1xEk? diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066000_VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066000_VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT.dfd deleted file mode 100644 index cb061e8dcbdf14dffcaa05e7cefe18456f4a1c28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 lcmdO4fB+^24TeRGj10`o3=9HLF$RYE0}y7zeg=jHdjLG51Wo_| diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066001_VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066001_VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT.dfd deleted file mode 100644 index 1f94dccc96214cb2bc1afe82cbf5c503d10e09aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 lcmdO4fB+^24TeRGj0`Ny3=9HLF$RYE0}y7zeg=jHdjLGX1Wy0} diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066002_VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066002_VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT.dfd deleted file mode 100644 index cbd1ef6b4d7f924d7cdfd274dff4f50a6d8b85a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 lcmdO4fB+^24TeRGj0`L+3=9HLF$RYE0}y7zeg=jHdjLGy1W*6~ diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066003_VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066003_VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT.dfd deleted file mode 100644 index f065e8df5d8ba0fc0e6694c3221b4c2bacf3251b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 lcmdO4fB+^24TeRGj0~(S3=9HLF$RYE0}y7zeg=jHdjLH31W^D0 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066004_VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066004_VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT.dfd deleted file mode 100644 index 23fdb20cf62f8a5a0d8149d4b9c3299e0e7b2993..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 lcmdO4fB+^24TeRGj0~)-3=9HLF$RYE0}y7zeg=jHdjLHU1X2J1 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066005_VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066005_VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT.dfd deleted file mode 100644 index dfcbf6f78034d9b3609ba1913b18f4e208cc519c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 lcmdO4fB+^24TeRGj12573=9HLF$RYE0}y7zeg=jHdjLHx1XBP2 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066006_VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066006_VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT.dfd deleted file mode 100644 index dde26116526e64d07842eeec9f49f218d815ae75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 lcmdO4fB+^24TeRGj126o3=9HLF$RYE0}y7zeg=jHdjLI11XKV3 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066007_VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066007_VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT.dfd deleted file mode 100644 index 37ce3db22790eb5294f158d869366f554d8cf44b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 lcmdO4fB+^24TeRGj127T3=9HLF$RYE0}y7zeg=jHdjLIt1Xch5 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066008_VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066008_VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT.dfd deleted file mode 100644 index ed3f76df9a2fd2e73d1198190364b0bed913b92f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 lcmdO4fB+^24TeRGj0~JC3=9HLF$RYE0}y7zeg=jHdjLIU1XTb4 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066009_VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066009_VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT.dfd deleted file mode 100644 index f0e716014f790a8b02dc66e61198dd9347dfc508..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 lcmdO4fB+^24TeRGj0~Kt3=9HLF$RYE0}y7zeg=jHdjLIv1Xch5 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066010_VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066010_VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT.dfd deleted file mode 100644 index 2628a3f0c83570c3556e2cfd6afafaa88dee188a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 lcmdO4fB+^24TeRGj0~LY3=9HLF$RYE0}y7zeg=jHdjLJQ1Xut7 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066011_VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066011_VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT.dfd deleted file mode 100644 index 7630cb5fd1f02f9461d5462bec835f5381cf4bf4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 lcmdO4fB+^24TeRGj0~Kd3=9HLF$RYE0}y7zeg=jHdjLJ`1X=(9 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066012_VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066012_VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT.dfd deleted file mode 100644 index 084ffdf9dc0878d56892a94b410f9bd5ae45f130..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 lcmdO4fB+^24TeRGj11hI3=9HLF$RYE0}y7zeg=jHdjLKp1Y7_B diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066013_VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/1000066013_VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT.dfd deleted file mode 100644 index 9629431d55546928fc143e80d0a567a2923c477f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 lcmdO4fB+^24TeRGj11h|3=9HLF$RYE0}y7zeg=jHdjLLK1YQ6D diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/100_VK_FORMAT_R32_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/100_VK_FORMAT_R32_SFLOAT.dfd deleted file mode 100644 index 62bf065bedf2ddc580b5df739f401b4a0829e6d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 icmdO4fB+^24F*O=Mlgp3D#pMde*nU4*w4VwU=IKxeFHlH diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/101_VK_FORMAT_R32G32_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/101_VK_FORMAT_R32G32_UINT.dfd deleted file mode 100644 index 83099d3108409b21d2120f8a7bd4a86450b0db3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 lcmcCvfB+^23kF6;Mlgp1D#pMd4`+d76d2?o3V>`70{|lG0OJ4v diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/102_VK_FORMAT_R32G32_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/102_VK_FORMAT_R32G32_SINT.dfd deleted file mode 100644 index ca4c12437cc34ba80758665761e3e841e5cc2e12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 mcmcCvfB+^23kF6;Mlgp1D#pMd?*L-{2Lh0c0)xCGG9Lh-_6b`6 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/103_VK_FORMAT_R32G32_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/103_VK_FORMAT_R32G32_SFLOAT.dfd deleted file mode 100644 index 1deaf3ecf4ebb12db3ff82135794d4abf3848b91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 ocmcCvfB+^23kF6;Mlgp1D#pMde*nU4*w4VwV6VU+e-N1u0CFYt<8 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/104_VK_FORMAT_R32G32B32_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/104_VK_FORMAT_R32G32B32_UINT.dfd deleted file mode 100644 index c97af3b5e09623b4345aed2ef7b33638ef06bdf5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76 tcmeZafB+^24+cg?Mlgp5D#pMd4`+d76d2?o3V>`7!+}Ab38nzV2LM2-0cijL diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/105_VK_FORMAT_R32G32B32_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/105_VK_FORMAT_R32G32B32_SINT.dfd deleted file mode 100644 index c92d53924a95679f307b24bf53b7b523f934e98c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76 scmeZafB+^24+cg?Mlgp5D#pMd?*L-{2Lh0c0)xCGGT(tg-U*ox0Ap(o9RL6T diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/106_VK_FORMAT_R32G32B32_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/106_VK_FORMAT_R32G32B32_SFLOAT.dfd deleted file mode 100644 index c17db614013b0e711e85856b5a7b444f5832a014..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76 ucmeZafB+^24+cg?Mlgp5D#pMde*nU4*w4VwV6VU+e-N4Pz#xAJnGXQ@JPDux diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/107_VK_FORMAT_R32G32B32A32_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/107_VK_FORMAT_R32G32B32A32_UINT.dfd deleted file mode 100644 index 4cefd46899928068e2352501a4a7cbe201efd131..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92 zcma!HfB+_j2nI$*MleSJD#pMd4`+d76d2?o3V>`7!+}Ab38nzVPhgPeN9F?nYqJ6z diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/108_VK_FORMAT_R32G32B32A32_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/108_VK_FORMAT_R32G32B32A32_SINT.dfd deleted file mode 100644 index b6c08e71254c90563b87431b4aeb44f3ebe595a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92 ycma!HfB+_j2nI$*MleSJD#pMd?*L-{2Lh0c0)xCGGT(tg-U*qXz##9B%m)B==@Se9 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/109_VK_FORMAT_R32G32B32A32_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/109_VK_FORMAT_R32G32B32A32_SFLOAT.dfd deleted file mode 100644 index e2f81b4448098ce1d34045a18befe013b40aaca1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92 zcma!HfB+_j2nI$*MleSJD#pMde*nU4*w4VwV6VU+e-N4Pz#xAJnV-NQe;%0+0J(Y% A5C8xG diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/10_VK_FORMAT_R8_SNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/10_VK_FORMAT_R8_SNORM.dfd deleted file mode 100644 index 11fd4e5d67149b58b6f30e64967c2ff8ef4d90ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24F*O=Mlc6Lf|%?MAhPlQ|Nr$s8UQI>1fKu^ diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/124_VK_FORMAT_D16_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/124_VK_FORMAT_D16_UNORM.dfd deleted file mode 100644 index 40acecb8b743822bb73d16b9019513b0e36bc7d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 gcmdO4fB+^24F*O=Mlgp7D#pOT&j(}v|Ifex02X2bcK`qY diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/125_VK_FORMAT_X8_D24_UNORM_PACK32.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/125_VK_FORMAT_X8_D24_UNORM_PACK32.dfd deleted file mode 100644 index 37ac93741471622492a7f8a66182570452ba99a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 gcmdO4fB+^24F*O=Mlgp3D#pMd&Ie=u|Nox>02u%SfB*mh diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/126_VK_FORMAT_D32_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/126_VK_FORMAT_D32_SFLOAT.dfd deleted file mode 100644 index 26e7fa510692f427bc03b1931e50fa1cd61575ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 icmdO4fB+^24F*O=Mlgp3D#pMde-6TI*w4VwU=IKyGXqHg diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/127_VK_FORMAT_S8_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/127_VK_FORMAT_S8_UINT.dfd deleted file mode 100644 index d2679f1aefd078688bc7aa4a62518e0f131b3e9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 ecmdO4fB+^24F*O=Mlc6Lf|%^Q5LpHWkQe|C*Z^<< diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/128_VK_FORMAT_D16_UNORM_S8_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/128_VK_FORMAT_D16_UNORM_S8_UINT.dfd deleted file mode 100644 index c4e74b914769a8b050fef9571a46d454b2e1279d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 ocmcCvfB+^23kF6;MlgpND#pOT&j(}v|Iff6z`)K6V!Z diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/130_VK_FORMAT_D32_SFLOAT_S8_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/130_VK_FORMAT_D32_SFLOAT_S8_UINT.dfd deleted file mode 100644 index 24fff8eeec7f026d3a22a37f05949e9618a3fab5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 qcmcCvfB+^23kF6;MlgpJD#pMde-6TI*w4VwV6VWy&I^+QsRRIE+5?{e diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/131_VK_FORMAT_BC1_RGB_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/131_VK_FORMAT_BC1_RGB_UNORM_BLOCK.dfd deleted file mode 100644 index ab4130745e0a2aee3387d5ae8747747d59e87742..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 gcmdO4fB+^24Tc6rMh0eP1_lnO7z2Ynob?|F04!bwA^-pY diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/132_VK_FORMAT_BC1_RGB_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/132_VK_FORMAT_BC1_RGB_SRGB_BLOCK.dfd deleted file mode 100644 index 35a18ce99ff2a9c0e89db8221b9b7856a39ceaac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 gcmdO4fB+^24Tc6rCI)6^1_lnO7z2Ynob?|F04#h3BLDyZ diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/133_VK_FORMAT_BC1_RGBA_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/133_VK_FORMAT_BC1_RGBA_UNORM_BLOCK.dfd deleted file mode 100644 index d78e14d42f6d8dec53d2316529b07cc98aff3f9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24Tc6rMh0eP1_lnO7z2YnBaHbU2mmZ=1tS0e diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/134_VK_FORMAT_BC1_RGBA_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/134_VK_FORMAT_BC1_RGBA_SRGB_BLOCK.dfd deleted file mode 100644 index 5abcfb14c0a40fef1b20b781dd0944ec2cc43d4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24Tc6rCI)6^1_lnO7z2YnBaHbU2mmaJ1tb6f diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/135_VK_FORMAT_BC2_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/135_VK_FORMAT_BC2_UNORM_BLOCK.dfd deleted file mode 100644 index 8382855ffd7a347c576332b609ab24427b753b93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 pcmcCvfB+^23x-BUMh0eP1_lAB7z2YnKaBYw2pkyfVO$0f9{`!@3Dp1q diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/136_VK_FORMAT_BC2_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/136_VK_FORMAT_BC2_SRGB_BLOCK.dfd deleted file mode 100644 index 8f54bc54a1e6e3ad66ddebbc319d8072a178f259..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 pcmcCvfB+^23x-BUCI)6^1_lAB7z2a7JdF7t2pkyfVO$0f9{`)@3FZI* diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/137_VK_FORMAT_BC3_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/137_VK_FORMAT_BC3_UNORM_BLOCK.dfd deleted file mode 100644 index 476249a7cf6e28fa7ec668ac753e0569185e4654..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 pcmcCvfB+^23x*~}Mh0eP1_lAB7z2YnKaBYw2pkyfVO$0f9{`#e3Dy7r diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/138_VK_FORMAT_BC3_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/138_VK_FORMAT_BC3_SRGB_BLOCK.dfd deleted file mode 100644 index 894302df1239d6a3f3fa7ad520eb407bf93f4ca0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 pcmcCvfB+^23x*~}CI)6^1_lAB7z2a7JdF7t2pkyfVO$0f9{`*e3FiO+ diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/139_VK_FORMAT_BC4_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/139_VK_FORMAT_BC4_UNORM_BLOCK.dfd deleted file mode 100644 index 4763a340ff39b8658e7b18be2869bff8704b65de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 gcmdO4fB+^24Tfe$Mh0eP1_lnO7z2Ynob?|F04%-*B>(^b diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/13_VK_FORMAT_R8_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/13_VK_FORMAT_R8_UINT.dfd deleted file mode 100644 index 84ba24a585c7ebaad0f49c7e539ea178157c5b5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 dcmdO4fB+^24F*O=Mlc6Lf|%?ONgxRl0{{*$0Am0E diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/140_VK_FORMAT_BC4_SNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/140_VK_FORMAT_BC4_SNORM_BLOCK.dfd deleted file mode 100644 index 65aec9c66f50924d149c0db3d4e56bcae32d83d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 kcmdO4fB+^24Tfe$Mh0eP1_lnO7z2a71C-hD|NsAb05SdrWdHyG diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/141_VK_FORMAT_BC5_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/141_VK_FORMAT_BC5_UNORM_BLOCK.dfd deleted file mode 100644 index 216adf0994760df5209d07ccf15823507d6f134a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 ocmcCvfB+^23x*a(Mh0eP1_lAB7z2Ynob?|F92o2wVFDmN0GQJW$^ZZW diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/142_VK_FORMAT_BC5_SNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/142_VK_FORMAT_BC5_SNORM_BLOCK.dfd deleted file mode 100644 index c0a4dd52cd568e22c02c79bc9af878747da59dc8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 qcmcCvfB+^23x*a(Mh0eP1_lAB7z2a71C-hD|NsAb2L^jbWIh0=QwmA| diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/143_VK_FORMAT_BC6H_UFLOAT_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/143_VK_FORMAT_BC6H_UFLOAT_BLOCK.dfd deleted file mode 100644 index c4279a78807f7be294d5f598b73c9f0fddaa0cfd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 icmdO4fB+^24Te@mMh0eP1_lAB7z0Cn1DwgwU=IK<2LiGH diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/144_VK_FORMAT_BC6H_SFLOAT_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/144_VK_FORMAT_BC6H_SFLOAT_BLOCK.dfd deleted file mode 100644 index cf05e902e14f571dddc50def38ec6c1b9804ecf0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 lcmdO4fB+^24Te@mMh0eP1_lAB7z0E70SL2UKLbO9JpefZ1Tg>r diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/145_VK_FORMAT_BC7_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/145_VK_FORMAT_BC7_UNORM_BLOCK.dfd deleted file mode 100644 index 9a9b5d0157b65c9a2c063564dabc0129fbc227e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 gcmdO4fB+^24Td&GMh0eP1_lAB7z0B+ob?|F05M(#Z~y=R diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/146_VK_FORMAT_BC7_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/146_VK_FORMAT_BC7_SRGB_BLOCK.dfd deleted file mode 100644 index 521d2a9e08fcdd55729f8df80db1d7833c888a97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 gcmdO4fB+^24Td&GCI)6^1_lAB7z0B+ob?|F05N<8aR2}S diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/147_VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/147_VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK.dfd deleted file mode 100644 index 0c5f99958d315947780fdab5264fe770641d9de7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 icmdO4fB+^24Tgn`j10`o3=AAlF$M;ECK&TS5C8x$m<2@u diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/148_VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/148_VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK.dfd deleted file mode 100644 index 4cf1799ea1b92ecdaa0d12ee4863342aeab5676b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 icmdO4fB+^24Tgn`ObpD-3=AAlF$M;ECK&TS5C8x$wgpB2 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/149_VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/149_VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK.dfd deleted file mode 100644 index 32c167e475d97f843e973648465ade7c8c8734f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 mcmcCvfB+^23x~H diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/151_VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/151_VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK.dfd deleted file mode 100644 index 03b74db608e7db8477c0e92cc1e8bd1e9c8ba666..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 ncmcCvfB+^23xTT05!e^g8%>k diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/155_VK_FORMAT_EAC_R11G11_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/155_VK_FORMAT_EAC_R11G11_UNORM_BLOCK.dfd deleted file mode 100644 index 384081191127e95ef99e709ed997493646eca89a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 ocmcCvfB+^23xk$=Kufz diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/156_VK_FORMAT_EAC_R11G11_SNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/156_VK_FORMAT_EAC_R11G11_SNORM_BLOCK.dfd deleted file mode 100644 index 92876d24edc41650868c0aa3071f57f52a0dde07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 qcmcCvfB+^23x4h;5=$b0~-!wP2r diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/157_VK_FORMAT_ASTC_4x4_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/157_VK_FORMAT_ASTC_4x4_UNORM_BLOCK.dfd deleted file mode 100644 index 6274b8eb45cf823e806b58f414c64d82f5c499f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGj10`o3=9HLF$RWuIO{(U001=J1&jay diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/158_VK_FORMAT_ASTC_4x4_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/158_VK_FORMAT_ASTC_4x4_SRGB_BLOCK.dfd deleted file mode 100644 index 6f257b967b6699ea3a842d6b4796c96c0f521d69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGObpD-3=9HLF$RWuIO{(U001=n1&sgz diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/159_VK_FORMAT_ASTC_5x4_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/159_VK_FORMAT_ASTC_5x4_UNORM_BLOCK.dfd deleted file mode 100644 index e476a6267e1fad612346b1c982db0c7e58db4e8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGj0`Ny3=9HLF$RWuIO{(U001=l1&sgz diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/15_VK_FORMAT_R8_SRGB.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/15_VK_FORMAT_R8_SRGB.dfd deleted file mode 100644 index 1271b748de7fcc9a13186ef0875046eae3adce7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 dcmdO4fB+^24F*O=CNPH)D#pOT4rl!bvH%iA0b&3E diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/160_VK_FORMAT_ASTC_5x4_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/160_VK_FORMAT_ASTC_5x4_SRGB_BLOCK.dfd deleted file mode 100644 index e66367d83bf99eb109b5c174a07ac431e95559d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGObjf{3=9HLF$RWuIO{(U001=@1&#m! diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/161_VK_FORMAT_ASTC_5x5_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/161_VK_FORMAT_ASTC_5x5_UNORM_BLOCK.dfd deleted file mode 100644 index fd42160de0feda7998d73219c050d77e60cda08a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGj0`L+3=9HLF$RWuIO{(U001==1&#m! diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/162_VK_FORMAT_ASTC_5x5_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/162_VK_FORMAT_ASTC_5x5_SRGB_BLOCK.dfd deleted file mode 100644 index d955ec781fca8d9596eb4d66de32a7276f4009dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGObje63=9HLF$RWuIO{(U001>J1&;s# diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/163_VK_FORMAT_ASTC_6x5_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/163_VK_FORMAT_ASTC_6x5_UNORM_BLOCK.dfd deleted file mode 100644 index 0ac72366419f2de26e07fc45f202a817ec619761..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGj0~(S3=9HLF$RWuIO{(U001>H1&;s# diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/164_VK_FORMAT_ASTC_6x5_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/164_VK_FORMAT_ASTC_6x5_SRGB_BLOCK.dfd deleted file mode 100644 index 347abe77c7ff55d0b28a71c6690f1f56f060d2eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGObo0n3=9HLF$RWuIO{(U001>l1&{y$ diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/165_VK_FORMAT_ASTC_6x6_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/165_VK_FORMAT_ASTC_6x6_UNORM_BLOCK.dfd deleted file mode 100644 index 790ec75abf8a2578ec46b18e369d24c55a52c5e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGj0~)-3=9HLF$RWuIO{(U001>i1&{y$ diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/166_VK_FORMAT_ASTC_6x6_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/166_VK_FORMAT_ASTC_6x6_SRGB_BLOCK.dfd deleted file mode 100644 index 4af3333797f0b4acc643677090612e2731ae421c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGObo273=9HLF$RWuIO{(U001>=1(5&% diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/167_VK_FORMAT_ASTC_8x5_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/167_VK_FORMAT_ASTC_8x5_UNORM_BLOCK.dfd deleted file mode 100644 index ebb6b9b594ee299937bd6f45869612c0052ce8fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGj12573=9HLF$RWuIO{(U001><1(5&% diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/168_VK_FORMAT_ASTC_8x5_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/168_VK_FORMAT_ASTC_8x5_SRGB_BLOCK.dfd deleted file mode 100644 index 953b3cc9c1738080b42a6255ea908263788cd529..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGObqNS3=9HLF$RWuIO{(U001?I1(E;& diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/169_VK_FORMAT_ASTC_8x6_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/169_VK_FORMAT_ASTC_8x6_UNORM_BLOCK.dfd deleted file mode 100644 index 8c014e164059c2876e8ee20409874657c932c07c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGj126o3=9HLF$RWuIO{(U001?F1(E;& diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/16_VK_FORMAT_R8G8_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/16_VK_FORMAT_R8G8_UNORM.dfd deleted file mode 100644 index d031e0b7dfb415e2825f42e2f9c7745503d8ee88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 mcmcCvfB+^23kF6;Mlgp7D#pOT4rl!bvN#ynAqs$O5FY?EDgu!J diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/170_VK_FORMAT_ASTC_8x6_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/170_VK_FORMAT_ASTC_8x6_SRGB_BLOCK.dfd deleted file mode 100644 index 2343973c3760f85bf13f4eb9caf6d9dd3a3bc7f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGObqO-3=9HLF$RWuIO{(U001?j1(N^( diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/171_VK_FORMAT_ASTC_8x8_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/171_VK_FORMAT_ASTC_8x8_UNORM_BLOCK.dfd deleted file mode 100644 index 471bdfa3904cd8770816a2b25badadeff9c64b07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGj127T3=9HLF$RWuIO{(U001?*1(W~) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/172_VK_FORMAT_ASTC_8x8_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/172_VK_FORMAT_ASTC_8x8_SRGB_BLOCK.dfd deleted file mode 100644 index 45f1756773eac1859c5d9204b4b99e9675e1719b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGObqPo3=9HLF$RWuIO{(U001@E1(g5* diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/173_VK_FORMAT_ASTC_10x5_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/173_VK_FORMAT_ASTC_10x5_UNORM_BLOCK.dfd deleted file mode 100644 index c36b0e46f577df68ccbd498d977d817273b2b46f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGj0~JC3=9HLF$RWuIO{(U001?i1(N^( diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/174_VK_FORMAT_ASTC_10x5_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/174_VK_FORMAT_ASTC_10x5_SRGB_BLOCK.dfd deleted file mode 100644 index 8e46bf3a5b09c7579f213ef2eb6c3f0c71a4a33c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGObnbX3=9HLF$RWuIO{(U001?=1(W~) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/175_VK_FORMAT_ASTC_10x6_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/175_VK_FORMAT_ASTC_10x6_UNORM_BLOCK.dfd deleted file mode 100644 index a6559fa8cb4d12a02afd6230a0e7139104081ee0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGj0~Kt3=9HLF$RWuIO{(U001?-1(W~) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/176_VK_FORMAT_ASTC_10x6_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/176_VK_FORMAT_ASTC_10x6_SRGB_BLOCK.dfd deleted file mode 100644 index f186a44f6ef11282900f4b87b721ddce7866dd1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGObnc?3=9HLF$RWuIO{(U001@G1(g5* diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/177_VK_FORMAT_ASTC_10x8_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/177_VK_FORMAT_ASTC_10x8_UNORM_BLOCK.dfd deleted file mode 100644 index 2b5651257d3f478c75c73bb5635d8d5f223940c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGj0~LY3=9HLF$RWuIO{(U001@e1(pB+ diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/178_VK_FORMAT_ASTC_10x8_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/178_VK_FORMAT_ASTC_10x8_SRGB_BLOCK.dfd deleted file mode 100644 index ccb47642f6b50b7804b25b1493d77acda2bccb4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGObndt3=9HLF$RWuIO{(U001@+1(yH- diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/179_VK_FORMAT_ASTC_10x10_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/179_VK_FORMAT_ASTC_10x10_UNORM_BLOCK.dfd deleted file mode 100644 index b8410ae62bdd6e5d8ddd318bcf81bc9929e62306..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGj0~Kd3=9HLF$RWuIO{(U001^91(*N; diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/17_VK_FORMAT_R8G8_SNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/17_VK_FORMAT_R8G8_SNORM.dfd deleted file mode 100644 index 9e8aabbf52e110920cfd24cfd61530a046bd841c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 ocmcCvfB+^23kF6;Mlgp7D#pOT?f_yo{{R2K9!PUAusb620hV?N4FCWD diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/180_VK_FORMAT_ASTC_10x10_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/180_VK_FORMAT_ASTC_10x10_SRGB_BLOCK.dfd deleted file mode 100644 index b0934898786c7ebe87eb4ab5d5a59e6f0f550434..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGObncy3=9HLF$RWuIO{(U001^d1(^T< diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/181_VK_FORMAT_ASTC_12x10_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/181_VK_FORMAT_ASTC_12x10_UNORM_BLOCK.dfd deleted file mode 100644 index 552e27d9acc1b44bb8d652f2ca9c41430f64d646..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGj11hI3=9HLF$RWuIO{(U001^%1)2Z= diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/182_VK_FORMAT_ASTC_12x10_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/182_VK_FORMAT_ASTC_12x10_SRGB_BLOCK.dfd deleted file mode 100644 index e0f4232aca64348ddeaa77f85c7fb48d83e640c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGObpzd3=9HLF$RWuIO{(U001_A1)Bf> diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/183_VK_FORMAT_ASTC_12x12_UNORM_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/183_VK_FORMAT_ASTC_12x12_UNORM_BLOCK.dfd deleted file mode 100644 index 7fc45f732e3950b125a32074fec17a4e78ad1464..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGj11h|3=9HLF$RWuIO{(U001_Y1)Kl? diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/184_VK_FORMAT_ASTC_12x12_SRGB_BLOCK.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/184_VK_FORMAT_ASTC_12x12_SRGB_BLOCK.dfd deleted file mode 100644 index 1b2340d1741126cd7a2704a233251db1cf6c461c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 hcmdO4fB+^24TeRGObp!I3=9HLF$RWuIO{(U001_$1)Tr@ diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/20_VK_FORMAT_R8G8_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/20_VK_FORMAT_R8G8_UINT.dfd deleted file mode 100644 index 5cec481cb9a6e46bab14aacfd9c657ddf315d24e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 lcmcCvfB+^23kF6;Mlgp7D#pOT4rhU6I2hO=3V>`70{|Ra0F?j$ diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/21_VK_FORMAT_R8G8_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/21_VK_FORMAT_R8G8_SINT.dfd deleted file mode 100644 index 2a3543bf006833e83643998b0dbe17ae8910c4f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 mcmcCvfB+^23kF6;Mlgp7D#pOT?f_!`2Lg}`2Lrn!G9Lh%W(f@d diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/22_VK_FORMAT_R8G8_SRGB.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/22_VK_FORMAT_R8G8_SRGB.dfd deleted file mode 100644 index 2c76130caeda95251cc4abd4b8d13cbb30b1cc70..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 lcmcCvfB+^23kF6;CNKv=f|%?ONg(+jh&dS88DRn-J^(aW0+Ij# diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/23_VK_FORMAT_R8G8B8_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/23_VK_FORMAT_R8G8B8_UNORM.dfd deleted file mode 100644 index 71275102578d0c06bb8aa31166f4bfa0d6d04cdc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76 ucmeZafB+^24+cg?MlgpND#pOT4rl!bvN#ynAqs$O5MO|Soe8D@#0LOt$^*&( diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/24_VK_FORMAT_R8G8B8_SNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/24_VK_FORMAT_R8G8B8_SNORM.dfd deleted file mode 100644 index 5677522864e0c850d2ff8b8be24318b1b1d0eb60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76 ucmeZafB+^24+cg?MlgpND#pOT?f_yo{{R2K9!PUAusb621sK?!kof>j8x4d2 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/27_VK_FORMAT_R8G8B8_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/27_VK_FORMAT_R8G8B8_UINT.dfd deleted file mode 100644 index f1a8ebd12bf67ba56a8a456340ce68465b6e471d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76 tcmeZafB+^24+cg?MlgpND#pOT4rhU6I2hO=3V>`7Lx6#u38nzV2LLca0MGyc diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/28_VK_FORMAT_R8G8B8_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/28_VK_FORMAT_R8G8B8_SINT.dfd deleted file mode 100644 index 376984c7319ff34a266886abc15fa8c2da6fe979..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76 scmeZafB+^24+cg?MlgpND#pOT?f_!`2Lg}`2Lrn!GGBm!-3gfw08;}Ega7~l diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/29_VK_FORMAT_R8G8B8_SRGB.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/29_VK_FORMAT_R8G8B8_SRGB.dfd deleted file mode 100644 index 7453fd9ebff49a4f2b2692063fe986e4d0378dba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76 ucmeZafB+^24+cg?CNPH?D#pOT4rl!bvN#yn8DRn-z5oL|L<5iw;sXF|2m{Li diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/37_VK_FORMAT_R8G8B8A8_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/37_VK_FORMAT_R8G8B8A8_UNORM.dfd deleted file mode 100644 index c5ed154409957ea4db428b27a41d77e984b9e432..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92 zcma!HfB+_j2nI$*Mlgp3D#pOT4rl!bvN#ynAqs$O5MO|Soe8D@#Ft=T=SSuP0J<{; A7ytkO diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/38_VK_FORMAT_R8G8B8A8_SNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/38_VK_FORMAT_R8G8B8A8_SNORM.dfd deleted file mode 100644 index 1c7745c71e57802f5b720b5216bbcfc84dd9532a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92 zcma!HfB+_j2nI$*Mlgp3D#pOT?f_yo{{R2K9!PUAusb621sK?!kogh}?Ec7n08VKV A5C8xG diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/41_VK_FORMAT_R8G8B8A8_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/41_VK_FORMAT_R8G8B8A8_UINT.dfd deleted file mode 100644 index 371d7274d294c6e1aa99d7c8212569e49a01004d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92 zcma!HfB+_j2nI$*Mlgp3D#pOT4rhU6I2hO=3V>`7Lx6#u38nzVmtbJ$N9F?nOA`Sg diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/42_VK_FORMAT_R8G8B8A8_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/42_VK_FORMAT_R8G8B8A8_SINT.dfd deleted file mode 100644 index 78bf6ea6faec09c320409207314103eb24bc3848..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92 ycma!HfB+_j2nI$*Mlgp3D#pOT?f_!`2Lg}`2Lrn!GGBm!-3gg5!NBg1%m)BgNfHnM diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/43_VK_FORMAT_R8G8B8A8_SRGB.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/43_VK_FORMAT_R8G8B8A8_SRGB.dfd deleted file mode 100644 index cff0cc0a3a7b1fa4e5c2d6fe6a90ad617e8c2173..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92 zcma!HfB+_j2nI$*CNPHuD#pOT4rl!bvN#yn8DRn-z5oL|L<5iw;!7~F%OlGJ0J~8I ADF6Tf diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/70_VK_FORMAT_R16_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/70_VK_FORMAT_R16_UNORM.dfd deleted file mode 100644 index c6e6febc0945e90e2a0643874539ab2c294e450f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 fcmdO4fB+^24F*O=Mlgp7D#pOT4`==V&%gix6|4ej diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/71_VK_FORMAT_R16_SNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/71_VK_FORMAT_R16_SNORM.dfd deleted file mode 100644 index b75caac56f98d416c51a2ddbff7897eda914ad42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 kcmdO4fB+^24F*O=Mlgp7D#pOT?*L*lHvIqpzn*~s043=Jr~m)} diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/74_VK_FORMAT_R16_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/74_VK_FORMAT_R16_UINT.dfd deleted file mode 100644 index a339eed1984d0983a2e06d97efd1be77175afdc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 ccmdO4fB+^24F*O=Mlgp7D#pOT4`+d701meRYybcN diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/75_VK_FORMAT_R16_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/75_VK_FORMAT_R16_SINT.dfd deleted file mode 100644 index 10e295930d1ef2c2221786e0634695e755c30a6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 gcmdO4fB+^24F*O=Mlgp7D#pOT?*L-{2Lg}`04v=Dr~m)} diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/76_VK_FORMAT_R16_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/76_VK_FORMAT_R16_SFLOAT.dfd deleted file mode 100644 index c15c3303a99850f4ac45bdb6452bde8484d432b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 icmdO4fB+^24F*O=Mlgp7D#pOTe*nU4*w4VwU=IKwZ38I) diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/77_VK_FORMAT_R16G16_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/77_VK_FORMAT_R16G16_UNORM.dfd deleted file mode 100644 index 2e535786fea658bace73bbbfe9b798672d8b297b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 ncmcCvfB+^23kF6;Mlgp3D#pOT4`==V&%hwSzz`70{|Y10IvW5 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/82_VK_FORMAT_R16G16_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/82_VK_FORMAT_R16G16_SINT.dfd deleted file mode 100644 index e3191449872e090bbff1e9385ae6980de624d402..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 mcmcCvfB+^23kF6;Mlgp3D#pOT?*L-{2Lh0c00X}xG9Lh(mI)^S diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/83_VK_FORMAT_R16G16_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/83_VK_FORMAT_R16G16_SFLOAT.dfd deleted file mode 100644 index 9901c10b692355fc2388ba7709c117a993c38b95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 ocmcCvfB+^23kF6;Mlgp3D#pOTe*nU4*w4VwU@ySHe-N1u0ByzwDF6Tf diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/84_VK_FORMAT_R16G16B16_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/84_VK_FORMAT_R16G16B16_UNORM.dfd deleted file mode 100644 index b7d7c45f8cedfa7077486309827fc770a65490c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76 vcmeZafB+^24+cg?MlgpBD#pOT4`==V&%hwSzz`7LxF*x38nzV2LLs?0RsR4 diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/89_VK_FORMAT_R16G16B16_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/89_VK_FORMAT_R16G16B16_SINT.dfd deleted file mode 100644 index c27fcc0a6640476e46fa7276d3733489bc069e77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76 scmeZafB+^24+cg?MlgpBD#pOT?*L-{2Lh0c00X}xGGBp#-wBxy09bnsw*UYD diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/90_VK_FORMAT_R16G16B16_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/90_VK_FORMAT_R16G16B16_SFLOAT.dfd deleted file mode 100644 index 80b84cf076e79861ec84df620b34afa7cb6de768..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76 ucmeZafB+^24+cg?MlgpBD#pOTe*nU4*w4VwU@ySHe-N3kz`%b9nGXQ&ObI#w diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/91_VK_FORMAT_R16G16B16A16_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/91_VK_FORMAT_R16G16B16A16_UNORM.dfd deleted file mode 100644 index db6cb4dc28d4d0aa9ccc9db1feea778fb6a3b48f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92 zcma!HfB+_j2nI$*Mlgp1D#pOT4`==V&%hwSzz`7LxF*x38nzVH(=n0YXI>9Rr3LK diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/96_VK_FORMAT_R16G16B16A16_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/96_VK_FORMAT_R16G16B16A16_SINT.dfd deleted file mode 100644 index fe848226f813483398195177a2e52b4806a83eb5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92 ycma!HfB+_j2nI$*Mlgp1D#pOT?*L-{2Lh0c00X}xGGBp#-wBy-z`*a1%m)BrDH3D= diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/97_VK_FORMAT_R16G16B16A16_SFLOAT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/97_VK_FORMAT_R16G16B16A16_SFLOAT.dfd deleted file mode 100644 index 4f1c40be85777a507c296640967961f5004bf609..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92 zcma!HfB+_j2nI$*Mlgp1D#pOTe*nU4*w4VwU@ySHe-N3kz`%b9nQy?re;%0+0HeAL AXaE2J diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/98_VK_FORMAT_R32_UINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/98_VK_FORMAT_R32_UINT.dfd deleted file mode 100644 index bdb8ba48a43d29dbbd7155679a57c7fc8be6a3bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 ccmdO4fB+^24F*O=Mlgp3D#pMd4`+d701w9iegFUf diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/99_VK_FORMAT_R32_SINT.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/99_VK_FORMAT_R32_SINT.dfd deleted file mode 100644 index 9c3a5e3ccd8b2727a246381db5249a0e8099150c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 gcmdO4fB+^24F*O=Mlgp3D#pMd?*L-{2Lg}`04(hUx&QzG diff --git a/src/MagnumPlugins/KtxImageConverter/Test/dfd/9_VK_FORMAT_R8_UNORM.dfd b/src/MagnumPlugins/KtxImageConverter/Test/dfd/9_VK_FORMAT_R8_UNORM.dfd deleted file mode 100644 index 6b1c29a5e707b0d485330a9d80921124dff992e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44 dcmdO4fB+^24F*O=Mlc6Lf|%?ONg(+jhyfBO0bu|D diff --git a/src/MagnumPlugins/KtxImageConverter/Test/testbidirectionalmapping.c.patch b/src/MagnumPlugins/KtxImageConverter/Test/testbidirectionalmapping.c.patch index 87c8b6d47..2018115d2 100644 --- a/src/MagnumPlugins/KtxImageConverter/Test/testbidirectionalmapping.c.patch +++ b/src/MagnumPlugins/KtxImageConverter/Test/testbidirectionalmapping.c.patch @@ -1,56 +1,62 @@ diff --git a/testbidirectionalmapping.c b/testbidirectionalmapping.c -index 480d103..6c80e73 100644 +index 480d103..8ab065b 100644 --- a/testbidirectionalmapping.c +++ b/testbidirectionalmapping.c -@@ -568,6 +568,19 @@ uint32_t *getmap(enum VkFormat f) +@@ -568,6 +568,14 @@ uint32_t *getmap(enum VkFormat f) } } -+void savemap(unsigned int format, const char* formatname, uint32_t* dfd) ++void appendmap(uint32_t format, FILE* file, uint32_t* dfd) +{ -+ // assumes little-endian system -+ size_t dfdsize = dfd[0]; -+ -+ char filename[256]; -+ sprintf(filename, "%d_%s.dfd", format, formatname); -+ -+ FILE *f = fopen(filename, "wb"); -+ fwrite(dfd, dfdsize, 1, f); -+ fclose(f); ++ // assumes little-endian system ++ uint32_t dfdsize = dfd[0]; ++ fwrite(&format, sizeof(format), 1, file); ++ fwrite(dfd, dfdsize, 1, file); +} + enum VkFormat unmap(uint32_t *dfd) { #include "dfd2vk.inl" -@@ -581,6 +594,7 @@ int main() +@@ -575,12 +583,15 @@ enum VkFormat unmap(uint32_t *dfd) + + int main() + { ++ FILE *file = fopen("dfd-data.bin", "wb"); ++ + unsigned int i; + for (i = 1; i <= 184; ++i) { + uint32_t *dfd = getmap((enum VkFormat)i); VkFormat f = unmap(dfd); if (i != f) printf("Input and output enums differ: %s (%d) -> %s (%d)\n", formatname(i),i, formatname(f),f); -+ else savemap(i, formatname(f), dfd); ++ else appendmap(i, file, dfd); free((void *)dfd); } -@@ -591,6 +605,7 @@ int main() +@@ -591,6 +602,7 @@ int main() VkFormat f = unmap(dfd); if (i != f) printf("Input and output enums differ: %s (%d) -> %s (%d)\n", formatname(i),i, formatname(f),f); -+ else savemap(i, formatname(f), dfd); ++ else appendmap(i, file, dfd); free((void *)dfd); } -@@ -600,6 +615,7 @@ int main() +@@ -600,6 +612,7 @@ int main() VkFormat f = unmap(dfd); if (i != f) printf("Input and output enums differ: %s (%d) -> %s (%d)\n", formatname(i),i, formatname(f),f); -+ else savemap(i, formatname(f), dfd); ++ else appendmap(i, file, dfd); free((void *)dfd); } -@@ -609,6 +625,7 @@ int main() +@@ -609,7 +622,10 @@ int main() VkFormat f = unmap(dfd); if (i != f) printf("Input and output enums differ: %s (%d) -> %s (%d)\n", formatname(i),i, formatname(f),f); -+ else savemap(i, formatname(f), dfd); ++ else appendmap(i, file, dfd); free((void *)dfd); } ++ ++ fclose(file); return 0; + }