diff --git a/doc/changelog.dox b/doc/changelog.dox index bfc144f07..cde469879 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -58,6 +58,9 @@ See also: @ref PixelFormat::RGB8Srgb and @ref PixelFormat::RGBA8Srgb sRGB pixel formats as well as their conversion to corresponding GL/Vulkan formats in @ref GL::pixelFormat() and @ref Vk::vkFormat(Magnum::PixelFormat) +- New @ref compressedBlockSize() and @ref compressedBlockDataSize() utilities + for querying parameters of @ref CompressedPixelFormat entries, similar to + what @ref pixelSize() is for @ref PixelFormat @subsubsection changelog-latest-new-audio Audio library diff --git a/src/Magnum/Implementation/compressedPixelFormatMapping.hpp b/src/Magnum/Implementation/compressedPixelFormatMapping.hpp new file mode 100644 index 000000000..6ce235854 --- /dev/null +++ b/src/Magnum/Implementation/compressedPixelFormatMapping.hpp @@ -0,0 +1,116 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/* each entry is width, height, depth, size in bytes */ +#ifdef _c +/* https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#_bc1_with_no_alpha */ +_c(Bc1RGBUnorm, 4, 4, 1, 64) +_c(Bc1RGBSrgb, 4, 4, 1, 64) +/* https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#_bc1_with_alpha */ +_c(Bc1RGBAUnorm, 4, 4, 1, 64) +_c(Bc1RGBASrgb, 4, 4, 1, 64) +/* https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#_bc2 */ +_c(Bc2RGBAUnorm, 4, 4, 1, 128) +_c(Bc2RGBASrgb, 4, 4, 1, 128) +/* https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#_bc3 */ +_c(Bc3RGBAUnorm, 4, 4, 1, 128) +_c(Bc3RGBASrgb, 4, 4, 1, 128) + +/* https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#_bc4_unsigned */ +_c(Bc4RUnorm, 4, 4, 1, 64) +_c(Bc4RSnorm, 4, 4, 1, 64) +/* https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#_bc5_unsigned */ +_c(Bc5RGUnorm, 4, 4, 1, 128) +_c(Bc5RGSnorm, 4, 4, 1, 128) + +/* https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#_bc6h */ +_c(Bc6hRGBUfloat, 4, 4, 1, 128) +_c(Bc6hRGBSfloat, 4, 4, 1, 128) + +/* https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#_bc7 */ +_c(Bc7RGBAUnorm, 4, 4, 1, 128) +_c(Bc7RGBASrgb, 4, 4, 1, 128) + +/* https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#Section-r11eac */ +_c(EacR11Unorm, 4, 4, 1, 64) +_c(EacR11Snorm, 4, 4, 1, 64) + +/* https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#_format_unsigned_rg11_eac */ +_c(EacRG11Unorm, 4, 4, 1, 128) +_c(EacRG11Snorm, 4, 4, 1, 128) + +/* https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#RGBETC2 */ +_c(Etc2RGB8Unorm, 4, 4, 1, 64) +_c(Etc2RGB8Srgb, 4, 4, 1, 64) + +/* https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#_format_rgb_etc2_with_punchthrough_alpha */ +_c(Etc2RGB8A1Unorm, 4, 4, 1, 64) +_c(Etc2RGB8A1Srgb, 4, 4, 1, 64) + +/* https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#_format_rgba_etc2 */ +_c(Etc2RGBA8Unorm, 4, 4, 1, 128) +_c(Etc2RGBA8Srgb, 4, 4, 1, 128) + +/* https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#_basic_concepts, + all blocks fit into 128 bits */ +_c(Astc4x4RGBAUnorm, 4, 4, 1, 128) +_c(Astc4x4RGBASrgb, 4, 4, 1, 128) +_c(Astc5x4RGBAUnorm, 5, 4, 1, 128) +_c(Astc5x4RGBASrgb, 5, 4, 1, 128) +_c(Astc5x5RGBAUnorm, 5, 5, 1, 128) +_c(Astc5x5RGBASrgb, 5, 5, 1, 128) +_c(Astc6x5RGBAUnorm, 6, 5, 1, 128) +_c(Astc6x5RGBASrgb, 6, 5, 1, 128) +_c(Astc6x6RGBAUnorm, 6, 6, 1, 128) +_c(Astc6x6RGBASrgb, 6, 6, 1, 128) +_c(Astc8x5RGBAUnorm, 8, 5, 1, 128) +_c(Astc8x5RGBASrgb, 8, 5, 1, 128) +_c(Astc8x6RGBAUnorm, 8, 6, 1, 128) +_c(Astc8x6RGBASrgb, 8, 6, 1, 128) +_c(Astc8x8RGBAUnorm, 8, 8, 1, 128) +_c(Astc8x8RGBASrgb, 8, 8, 1, 128) +_c(Astc10x5RGBAUnorm, 10, 5, 1, 128) +_c(Astc10x5RGBASrgb, 10, 5, 1, 128) +_c(Astc10x6RGBAUnorm, 10, 6, 1, 128) +_c(Astc10x6RGBASrgb, 10, 6, 1, 128) +_c(Astc10x8RGBAUnorm, 10, 8, 1, 128) +_c(Astc10x8RGBASrgb, 10, 8, 1, 128) +_c(Astc10x10RGBAUnorm, 10, 10, 1, 128) +_c(Astc10x10RGBASrgb, 10, 10, 1, 128) +_c(Astc12x10RGBAUnorm, 12, 10, 1, 128) +_c(Astc12x10RGBASrgb, 12, 10, 1, 128) +_c(Astc12x12RGBAUnorm, 12, 12, 1, 128) +_c(Astc12x12RGBASrgb, 12, 12, 1, 128) + +/* https://en.wikipedia.org/wiki/PVRTC */ +_c(PvrtcRGB2bppUnorm, 8, 4, 1, 64) +_c(PvrtcRGB2bppSrgb, 8, 4, 1, 64) +_c(PvrtcRGBA2bppUnorm, 8, 4, 1, 64) +_c(PvrtcRGBA2bppSrgb, 8, 4, 1, 64) +_c(PvrtcRGB4bppUnorm, 4, 4, 1, 64) +_c(PvrtcRGB4bppSrgb, 4, 4, 1, 64) +_c(PvrtcRGBA4bppUnorm, 4, 4, 1, 64) +_c(PvrtcRGBA4bppSrgb, 4, 4, 1, 64) +#endif diff --git a/src/Magnum/PixelFormat.cpp b/src/Magnum/PixelFormat.cpp index 8c7583b25..e467cc8cd 100644 --- a/src/Magnum/PixelFormat.cpp +++ b/src/Magnum/PixelFormat.cpp @@ -25,9 +25,12 @@ #include "PixelFormat.h" +#include #include #include +#include "Magnum/Math/Vector3.h" + namespace Magnum { UnsignedInt pixelSize(const PixelFormat format) { @@ -183,6 +186,51 @@ Debug& operator<<(Debug& debug, const PixelFormat value) { } #endif +namespace { + +#ifndef DOXYGEN_GENERATING_OUTPUT /* It gets *really* confused */ +constexpr UnsignedShort CompressedBlockData[] { + /* Assuming w/h/d/s is never larger than 16 (and never zero), each number + has 1 subtracted and packed into four bits, 16 bits in total. The size + is supplied in bits, so first divide by eight and then subtract 1. For + the currently ~60 supported format that makes this table to be about 128 + bytes.*/ + #define _c(format, width, height, depth, size) \ + ((width - 1) << 12) | \ + ((height - 1) << 8) | \ + ((depth - 1) << 4) | \ + ((size >> 3) - 1), + #include "Magnum/Implementation/compressedPixelFormatMapping.hpp" + #undef _s + #undef _c +}; +#endif + +} + +Vector3i compressedBlockSize(const CompressedPixelFormat format) { + CORRADE_ASSERT(!(UnsignedInt(format) & (1 << 31)), + "compressedBlockSize(): can't determine size of an implementation-specific format", {}); + + CORRADE_ASSERT(UnsignedInt(format) - 1 < Containers::arraySize(CompressedBlockData), + "compressedBlockSize(): invalid format" << format, {}); + const UnsignedInt data = CompressedBlockData[UnsignedInt(format) - 1]; + return { + (Int(data >> 12) & 0xf) + 1, + (Int(data >> 8) & 0xf) + 1, + (Int(data >> 4) & 0xf) + 1, + }; +} + +UnsignedInt compressedBlockDataSize(const CompressedPixelFormat format) { + CORRADE_ASSERT(!(UnsignedInt(format) & (1 << 31)), + "compressedBlockDataSize(): can't determine size of an implementation-specific format", {}); + + CORRADE_ASSERT(UnsignedInt(format) - 1 < Containers::arraySize(CompressedBlockData), + "compressedBlockDataSize(): invalid format" << format, {}); + return (CompressedBlockData[UnsignedInt(format) - 1] & 0xf) + 1; +} + #ifndef DOXYGEN_GENERATING_OUTPUT Debug& operator<<(Debug& debug, const CompressedPixelFormat value) { if(isCompressedPixelFormatImplementationSpecific(value)) { diff --git a/src/Magnum/PixelFormat.h b/src/Magnum/PixelFormat.h index 97c2bfdcb..4a031cd37 100644 --- a/src/Magnum/PixelFormat.h +++ b/src/Magnum/PixelFormat.h @@ -749,7 +749,8 @@ For D3D, corresponds to @m_class{m-doc-external} [DXGI_FORMAT](https://docs.micr and import is provided by the @ref Trade::DdsImporter "DdsImporter" plugin; for Metal, corresponds to @m_class{m-doc-external} [MTLPixelFormat](https://developer.apple.com/documentation/metal/mtlpixelformat?language=objc). See documentation of each value for more information about the mapping. -@see @ref PixelFormat, @ref CompressedImage, @ref CompressedImageView +@see @ref compressedBlockSize(), @ref compressedBlockDataSize(), + @ref PixelFormat, @ref CompressedImage, @ref CompressedImageView */ enum class CompressedPixelFormat: UnsignedInt { /* Zero reserved for an invalid format (but not being a named value) */ @@ -1591,6 +1592,26 @@ enum class CompressedPixelFormat: UnsignedInt { Metal doesn't support it and it doesn't have a WebGL equiv either. */ }; +/** +@brief Compressed block size + +For 2D formats the Z dimension is always 1. Expects that the pixel format is +* *not* implementation-specific. +@see @ref compressedBlockDataSize(), + @ref isCompressedPixelFormatImplementationSpecific() +*/ +MAGNUM_EXPORT Vector3i compressedBlockSize(CompressedPixelFormat format); + +/** +@brief Compressed block data size + +Byte size of each compressed block. Expects that the pixel format is *not* +implementation-specific. +@see @ref compressedBlockSize(), + @ref isCompressedPixelFormatImplementationSpecific() +*/ +MAGNUM_EXPORT UnsignedInt compressedBlockDataSize(CompressedPixelFormat format); + /** @debugoperatorenum{CompressedPixelFormat} */ MAGNUM_EXPORT Debug& operator<<(Debug& debug, CompressedPixelFormat value); diff --git a/src/Magnum/PixelStorage.h b/src/Magnum/PixelStorage.h index 958e238d8..a865ff754 100644 --- a/src/Magnum/PixelStorage.h +++ b/src/Magnum/PixelStorage.h @@ -185,6 +185,7 @@ class MAGNUM_EXPORT CompressedPixelStorage: public PixelStorage { * If set to @cpp 0 @ce for given dimension, size information from * particular compressed format is used. Default is @cpp 0 @ce in all * dimensions. + * @see @ref Magnum::compressedBlockSize() */ CompressedPixelStorage& setCompressedBlockSize(const Vector3i& size) { _blockSize = size; @@ -199,6 +200,7 @@ class MAGNUM_EXPORT CompressedPixelStorage: public PixelStorage { * * If set to @cpp 0 @ce, size information from particular compressed * format is used. Default is @cpp 0 @ce in all dimensions. + * @see @ref Magnum::compressedBlockDataSize() */ CompressedPixelStorage& setCompressedBlockDataSize(Int size) { _blockDataSize = size; diff --git a/src/Magnum/Test/PixelFormatTest.cpp b/src/Magnum/Test/PixelFormatTest.cpp index eeab64374..327836f5b 100644 --- a/src/Magnum/Test/PixelFormatTest.cpp +++ b/src/Magnum/Test/PixelFormatTest.cpp @@ -25,9 +25,11 @@ #include #include +#include #include #include "Magnum/PixelFormat.h" +#include "Magnum/Math/Vector3.h" namespace Magnum { namespace Test { namespace { @@ -38,6 +40,10 @@ struct PixelFormatTest: TestSuite::Tester { void sizeInvalid(); void sizeImplementationSpecific(); + void compressedBlockSize(); + void compressedBlockSizeInvalid(); + void compressedBlockSizeImplementationSpecific(); + void isImplementationSpecific(); void wrap(); void wrapInvalid(); @@ -62,6 +68,10 @@ PixelFormatTest::PixelFormatTest() { &PixelFormatTest::sizeInvalid, &PixelFormatTest::sizeImplementationSpecific, + &PixelFormatTest::compressedBlockSize, + &PixelFormatTest::compressedBlockSizeInvalid, + &PixelFormatTest::compressedBlockSizeImplementationSpecific, + &PixelFormatTest::isImplementationSpecific, &PixelFormatTest::wrap, &PixelFormatTest::wrapInvalid, @@ -113,6 +123,91 @@ void PixelFormatTest::sizeImplementationSpecific() { CORRADE_COMPARE(out.str(), "pixelSize(): can't determine pixel size of an implementation-specific format\n"); } +void PixelFormatTest::compressedBlockSize() { + /* Touchstone verification */ + CORRADE_COMPARE(Magnum::compressedBlockSize(CompressedPixelFormat::Etc2RGB8A1Srgb), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(compressedBlockDataSize(CompressedPixelFormat::Etc2RGB8A1Srgb), 8); + CORRADE_COMPARE(Magnum::compressedBlockSize(CompressedPixelFormat::Astc5x4RGBAUnorm), (Vector3i{5, 4, 1})); + CORRADE_COMPARE(compressedBlockDataSize(CompressedPixelFormat::Astc5x4RGBAUnorm), 16); + CORRADE_COMPARE(Magnum::compressedBlockSize(CompressedPixelFormat::Astc12x10RGBAUnorm), (Vector3i{12, 10, 1})); + CORRADE_COMPARE(compressedBlockDataSize(CompressedPixelFormat::Astc12x10RGBAUnorm), 16); + CORRADE_COMPARE(Magnum::compressedBlockSize(CompressedPixelFormat::PvrtcRGBA2bppUnorm), (Vector3i{8, 4, 1})); + CORRADE_COMPARE(compressedBlockDataSize(CompressedPixelFormat::PvrtcRGBA2bppUnorm), 8); + + /* This goes through the first 16 bits, which should be enough. Going + through 32 bits takes 8 seconds, too much. */ + UnsignedInt firstUnhandled = 0xffff; + UnsignedInt nextHandled = 1; /* 0 is an invalid format */ + for(UnsignedInt i = 1; i <= 0xffff; ++i) { + const auto format = CompressedPixelFormat(i); + /* Each case verifies: + - that the cases are ordered by number (so insertion here is done in + proper place) + - that there was no gap (unhandled value inside the range) + - that a particular pixel format maps to a particular GL format + - that a particular pixel type maps to a particular GL type */ + #ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic error "-Wswitch" + #endif + switch(format) { + #define _c(format, width, height, depth, size) \ + case CompressedPixelFormat::format: \ + CORRADE_COMPARE(nextHandled, i); \ + CORRADE_COMPARE(firstUnhandled, 0xffff); \ + CORRADE_COMPARE(Magnum::compressedBlockSize(CompressedPixelFormat::format), (Vector3i{width, height, depth})); \ + CORRADE_COMPARE(compressedBlockDataSize(CompressedPixelFormat::format), size/8); \ + CORRADE_COMPARE(size % 8, 0); \ + CORRADE_COMPARE_AS(width, 16, TestSuite::Compare::LessOrEqual); \ + CORRADE_COMPARE_AS(height, 16, TestSuite::Compare::LessOrEqual); \ + CORRADE_COMPARE_AS(depth, 16, TestSuite::Compare::LessOrEqual); \ + CORRADE_COMPARE_AS(size/8, 16, TestSuite::Compare::LessOrEqual); \ + ++nextHandled; \ + continue; + #include "Magnum/Implementation/compressedPixelFormatMapping.hpp" + #undef _c + } + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif + + /* Not handled by any value, remember -- we might either be at the end + of the enum range (which is okay) or some value might be unhandled + here */ + firstUnhandled = i; + } + + CORRADE_COMPARE(firstUnhandled, 0xffff); +} + +void PixelFormatTest::compressedBlockSizeInvalid() { + std::ostringstream out; + Error redirectError{&out}; + + Magnum::compressedBlockSize(CompressedPixelFormat{}); + Magnum::compressedBlockSize(CompressedPixelFormat(0xdead)); + compressedBlockDataSize(CompressedPixelFormat{}); + compressedBlockDataSize(CompressedPixelFormat(0xdead)); + + CORRADE_COMPARE(out.str(), + "compressedBlockSize(): invalid format CompressedPixelFormat(0x0)\n" + "compressedBlockSize(): invalid format CompressedPixelFormat(0xdead)\n" + "compressedBlockDataSize(): invalid format CompressedPixelFormat(0x0)\n" + "compressedBlockDataSize(): invalid format CompressedPixelFormat(0xdead)\n"); +} + +void PixelFormatTest::compressedBlockSizeImplementationSpecific() { + std::ostringstream out; + Error redirectError{&out}; + + Magnum::compressedBlockSize(compressedPixelFormatWrap(0xdead)); + compressedBlockDataSize(compressedPixelFormatWrap(0xdead)); + + CORRADE_COMPARE(out.str(), + "compressedBlockSize(): can't determine size of an implementation-specific format\n" + "compressedBlockDataSize(): can't determine size of an implementation-specific format\n"); +} + void PixelFormatTest::isImplementationSpecific() { constexpr bool a = isPixelFormatImplementationSpecific(PixelFormat::RGBA8Unorm); constexpr bool b = isPixelFormatImplementationSpecific(PixelFormat(0x8000dead));