diff --git a/src/Magnum/GL/AbstractTexture.cpp b/src/Magnum/GL/AbstractTexture.cpp index 630cfb2d2..3b2d5b79e 100644 --- a/src/Magnum/GL/AbstractTexture.cpp +++ b/src/Magnum/GL/AbstractTexture.cpp @@ -2174,7 +2174,7 @@ void AbstractTexture::DataHelper<1>::setCompressedImage(AbstractTexture& texture Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); texture.bindInternal(); - glCompressedTexImage1D(texture._target, level, GLenum(compressedPixelFormat(image.format())), image.size()[0], 0, Implementation::occupiedCompressedImageDataSize(image, image.data().size()), image.data()); + glCompressedTexImage1D(texture._target, level, GLenum(compressedPixelFormat(image.format())), image.size()[0], 0, Implementation::occupiedCompressedImageDataSize(image), image.data()); } void AbstractTexture::DataHelper<1>::setImage(AbstractTexture& texture, const GLint level, const TextureFormat internalFormat, BufferImage1D& image) { @@ -2188,7 +2188,7 @@ void AbstractTexture::DataHelper<1>::setCompressedImage(AbstractTexture& texture image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); texture.bindInternal(); - glCompressedTexImage1D(texture._target, level, GLenum(image.format()), image.size()[0], 0, Implementation::occupiedCompressedImageDataSize(image, image.dataSize()), nullptr); + glCompressedTexImage1D(texture._target, level, GLenum(image.format()), image.size()[0], 0, Implementation::occupiedCompressedImageDataSize(image), nullptr); } void AbstractTexture::DataHelper<1>::setSubImage(AbstractTexture& texture, const GLint level, const Math::Vector<1, GLint>& offset, const ImageView1D& image) { @@ -2200,7 +2200,7 @@ void AbstractTexture::DataHelper<1>::setSubImage(AbstractTexture& texture, const void AbstractTexture::DataHelper<1>::setCompressedSubImage(AbstractTexture& texture, const GLint level, const Math::Vector<1, GLint>& offset, const CompressedImageView1D& image) { Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); - Context::current().state().texture.compressedSubImage1DImplementation(texture, level, offset, image.size(), compressedPixelFormat(image.format()), image.data(), Implementation::occupiedCompressedImageDataSize(image, image.data().size())); + Context::current().state().texture.compressedSubImage1DImplementation(texture, level, offset, image.size(), compressedPixelFormat(image.format()), image.data(), Implementation::occupiedCompressedImageDataSize(image)); } void AbstractTexture::DataHelper<1>::setSubImage(AbstractTexture& texture, const GLint level, const Math::Vector<1, GLint>& offset, BufferImage1D& image) { @@ -2212,7 +2212,7 @@ void AbstractTexture::DataHelper<1>::setSubImage(AbstractTexture& texture, const void AbstractTexture::DataHelper<1>::setCompressedSubImage(AbstractTexture& texture, const GLint level, const Math::Vector<1, GLint>& offset, CompressedBufferImage1D& image) { image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); - Context::current().state().texture.compressedSubImage1DImplementation(texture, level, offset, image.size(), image.format(), nullptr, Implementation::occupiedCompressedImageDataSize(image, image.dataSize())); + Context::current().state().texture.compressedSubImage1DImplementation(texture, level, offset, image.size(), image.format(), nullptr, Implementation::occupiedCompressedImageDataSize(image)); } #endif @@ -2234,7 +2234,7 @@ void AbstractTexture::DataHelper<2>::setCompressedImage(AbstractTexture& texture #endif Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); texture.bindInternal(); - glCompressedTexImage2D(target, level, GLenum(compressedPixelFormat(image.format())), image.size().x(), image.size().y(), 0, Implementation::occupiedCompressedImageDataSize(image, image.data().size()), image.data()); + glCompressedTexImage2D(target, level, GLenum(compressedPixelFormat(image.format())), image.size().x(), image.size().y(), 0, Implementation::occupiedCompressedImageDataSize(image), image.data()); } #ifndef MAGNUM_TARGET_GLES2 @@ -2249,7 +2249,7 @@ void AbstractTexture::DataHelper<2>::setCompressedImage(AbstractTexture& texture image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); texture.bindInternal(); - glCompressedTexImage2D(target, level, GLenum(image.format()), image.size().x(), image.size().y(), 0, Implementation::occupiedCompressedImageDataSize(image, image.dataSize()), nullptr); + glCompressedTexImage2D(target, level, GLenum(image.format()), image.size().x(), image.size().y(), 0, Implementation::occupiedCompressedImageDataSize(image), nullptr); } #endif @@ -2270,7 +2270,7 @@ void AbstractTexture::DataHelper<2>::setCompressedSubImage(AbstractTexture& text Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); #endif Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); - Context::current().state().texture.compressedSubImage2DImplementation(texture, level, offset, image.size(), compressedPixelFormat(image.format()), image.data(), Implementation::occupiedCompressedImageDataSize(image, image.data().size())); + Context::current().state().texture.compressedSubImage2DImplementation(texture, level, offset, image.size(), compressedPixelFormat(image.format()), image.data(), Implementation::occupiedCompressedImageDataSize(image)); } #ifndef MAGNUM_TARGET_GLES2 @@ -2283,7 +2283,7 @@ void AbstractTexture::DataHelper<2>::setSubImage(AbstractTexture& texture, const void AbstractTexture::DataHelper<2>::setCompressedSubImage(AbstractTexture& texture, const GLint level, const Vector2i& offset, CompressedBufferImage2D& image) { image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); - Context::current().state().texture.compressedSubImage2DImplementation(texture, level, offset, image.size(), image.format(), nullptr, Implementation::occupiedCompressedImageDataSize(image, image.dataSize())); + Context::current().state().texture.compressedSubImage2DImplementation(texture, level, offset, image.size(), image.format(), nullptr, Implementation::occupiedCompressedImageDataSize(image)); } #endif @@ -2307,9 +2307,9 @@ void AbstractTexture::DataHelper<3>::setCompressedImage(AbstractTexture& texture Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); texture.bindInternal(); #ifndef MAGNUM_TARGET_GLES2 - glCompressedTexImage3D(texture._target, level, GLenum(compressedPixelFormat(image.format())), image.size().x(), image.size().y(), image.size().z(), 0, Implementation::occupiedCompressedImageDataSize(image, image.data().size()), image.data()); + glCompressedTexImage3D(texture._target, level, GLenum(compressedPixelFormat(image.format())), image.size().x(), image.size().y(), image.size().z(), 0, Implementation::occupiedCompressedImageDataSize(image), image.data()); #else - glCompressedTexImage3DOES(texture._target, level, GLenum(compressedPixelFormat(image.format())), image.size().x(), image.size().y(), image.size().z(), 0, Implementation::occupiedCompressedImageDataSize(image, image.data().size()), image.data()); + glCompressedTexImage3DOES(texture._target, level, GLenum(compressedPixelFormat(image.format())), image.size().x(), image.size().y(), image.size().z(), 0, Implementation::occupiedCompressedImageDataSize(image), image.data()); #endif } #endif @@ -2326,7 +2326,7 @@ void AbstractTexture::DataHelper<3>::setCompressedImage(AbstractTexture& texture image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); texture.bindInternal(); - glCompressedTexImage3D(texture._target, level, GLenum(image.format()), image.size().x(), image.size().y(), image.size().z(), 0, Implementation::occupiedCompressedImageDataSize(image, image.dataSize()), nullptr); + glCompressedTexImage3D(texture._target, level, GLenum(image.format()), image.size().x(), image.size().y(), image.size().z(), 0, Implementation::occupiedCompressedImageDataSize(image), nullptr); } #endif @@ -2348,7 +2348,7 @@ void AbstractTexture::DataHelper<3>::setCompressedSubImage(AbstractTexture& text Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); #endif Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); - Context::current().state().texture.compressedSubImage3DImplementation(texture, level, offset, image.size(), compressedPixelFormat(image.format()), image.data(), Implementation::occupiedCompressedImageDataSize(image, image.data().size())); + Context::current().state().texture.compressedSubImage3DImplementation(texture, level, offset, image.size(), compressedPixelFormat(image.format()), image.data(), Implementation::occupiedCompressedImageDataSize(image)); } #endif @@ -2362,7 +2362,7 @@ void AbstractTexture::DataHelper<3>::setSubImage(AbstractTexture& texture, const void AbstractTexture::DataHelper<3>::setCompressedSubImage(AbstractTexture& texture, const GLint level, const Vector3i& offset, CompressedBufferImage3D& image) { image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); - Context::current().state().texture.compressedSubImage3DImplementation(texture, level, offset, image.size(), image.format(), nullptr, Implementation::occupiedCompressedImageDataSize(image, image.dataSize())); + Context::current().state().texture.compressedSubImage3DImplementation(texture, level, offset, image.size(), image.format(), nullptr, Implementation::occupiedCompressedImageDataSize(image)); } #endif diff --git a/src/Magnum/GL/BufferImage.cpp b/src/Magnum/GL/BufferImage.cpp index 095ec09c9..e16acdfbf 100644 --- a/src/Magnum/GL/BufferImage.cpp +++ b/src/Magnum/GL/BufferImage.cpp @@ -95,23 +95,32 @@ template void BufferImage::setData(const Pix } template CompressedBufferImage::CompressedBufferImage(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor& size, const Containers::ArrayView data, const BufferUsage usage): CompressedBufferImage{storage, format, size, Buffer{Buffer::TargetHint::PixelPack}, data.size()} { + /* Size and block properties checks done in the delegated-to constructor + already */ _buffer.setData(data, usage); } template CompressedBufferImage::CompressedBufferImage(const CompressedPixelStorage storage, const Magnum::CompressedPixelFormat format, const VectorTypeFor& size, const Containers::ArrayView data, const BufferUsage usage): CompressedBufferImage{storage, compressedPixelFormat(format), size, data, usage} {} -template CompressedBufferImage::CompressedBufferImage(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor& size, Buffer&& buffer, const std::size_t dataSize) noexcept: _storage{storage}, _format{format}, _size{size}, _buffer{Utility::move(buffer)}, _dataSize{dataSize} {} +template CompressedBufferImage::CompressedBufferImage(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor& size, Buffer&& buffer, const std::size_t dataSize) noexcept: _storage{storage}, _format{format}, _blockSize{Vector3ub(compressedPixelFormatBlockSize(format))}, _blockDataSize{UnsignedByte(compressedPixelFormatBlockDataSize(format))}, _size{size}, _buffer{Utility::move(buffer)}, _dataSize{dataSize} { + #ifndef CORRADE_NO_ASSERT + Magnum::Implementation::checkBlockPropertiesForStorage("GL::CompressedBufferImage:", Vector3i{_blockSize}, _blockDataSize, storage); + CORRADE_ASSERT(Magnum::Implementation::compressedImageDataSize(*this) <= dataSize, "GL::CompressedBufferImage: data too small, got" << dataSize << "but expected at least" << Magnum::Implementation::compressedImageDataSize(*this) << "bytes", ); + #endif +} template CompressedBufferImage::CompressedBufferImage(const CompressedPixelStorage storage, const Magnum::CompressedPixelFormat format, const VectorTypeFor& size, Buffer&& buffer, const std::size_t dataSize) noexcept: CompressedBufferImage{storage, compressedPixelFormat(format), size, Utility::move(buffer), dataSize} {} -template CompressedBufferImage::CompressedBufferImage(const CompressedPixelStorage storage): _storage{storage}, _format{}, _buffer{Buffer::TargetHint::PixelPack}, _dataSize{} { - /* Not delegating to the (buffer&&, dataSize) constructor to avoid a size - assertion that'd happen with certain storage parameters */ +template CompressedBufferImage::CompressedBufferImage(const CompressedPixelStorage storage): _storage{storage}, _format{}, _blockDataSize{}, _buffer{Buffer::TargetHint::PixelPack}, _dataSize{} { + CORRADE_ASSERT(storage.compressedBlockSize() == Vector3i{}, + "GL::CompressedBufferImage: expected pixel storage block size to not be set at all but got" << Debug::packed << storage.compressedBlockSize(), ); + CORRADE_ASSERT(!storage.compressedBlockDataSize(), + "GL::CompressedBufferImage: expected pixel storage block data size to not be set at all but got" << storage.compressedBlockDataSize(), ); } -template CompressedBufferImage::CompressedBufferImage(NoCreateT) noexcept: _format{}, _buffer{NoCreate}, _dataSize{} {} +template CompressedBufferImage::CompressedBufferImage(NoCreateT) noexcept: _format{}, _blockDataSize{}, _buffer{NoCreate}, _dataSize{} {} -template CompressedBufferImage::CompressedBufferImage(CompressedBufferImage&& other) noexcept: _storage{Utility::move(other._storage)}, _format{Utility::move(other._format)}, _size{Utility::move(other._size)}, _buffer{Utility::move(other._buffer)}, _dataSize{Utility::move(other._dataSize)} { +template CompressedBufferImage::CompressedBufferImage(CompressedBufferImage&& other) noexcept: _storage{other._storage}, _format{other._format}, _blockSize{other._blockSize}, _blockDataSize{other._blockDataSize}, _size{other._size}, _buffer{Utility::move(other._buffer)}, _dataSize{other._dataSize} { other._size = {}; other._dataSize = {}; } @@ -120,6 +129,8 @@ template CompressedBufferImage& CompressedBu using Utility::swap; swap(_storage, other._storage); swap(_format, other._format); + swap(_blockSize, other._blockSize); + swap(_blockDataSize, other._blockDataSize); swap(_size, other._size); swap(_buffer, other._buffer); swap(_dataSize, other._dataSize); @@ -133,10 +144,18 @@ template std::pair void CompressedBufferImage::setData(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor& size, const Containers::ArrayView data, const BufferUsage usage) { _storage = storage; _format = format; + _blockSize = Vector3ub(compressedPixelFormatBlockSize(format)); + _blockDataSize = UnsignedByte(compressedPixelFormatBlockDataSize(format)); _size = size; + #ifndef CORRADE_NO_ASSERT + Magnum::Implementation::checkBlockPropertiesForStorage("GL::CompressedBufferImage::setData():", Vector3i{_blockSize}, _blockDataSize, storage); + #endif /* Keep the old storage if zero-sized nullptr buffer was passed */ - if(!(data.data() == nullptr && data.size() == 0)) { + if(data.data() == nullptr && data.size() == 0) { + CORRADE_ASSERT(Magnum::Implementation::compressedImageDataSize(*this) <= _dataSize, "GL::CompressedBufferImage::setData(): current storage too small, got" << _dataSize << "but expected at least" << Magnum::Implementation::compressedImageDataSize(*this) << "bytes", ); + } else { + CORRADE_ASSERT(Magnum::Implementation::compressedImageDataSize(*this) <= data.size(), "GL::CompressedBufferImage::setData(): data too small, got" << data.size() << "but expected at least" << Magnum::Implementation::compressedImageDataSize(*this) << "bytes", ); _buffer.setData(data, usage); _dataSize = data.size(); } diff --git a/src/Magnum/GL/BufferImage.h b/src/Magnum/GL/BufferImage.h index a0e23ecb7..992c4d4b9 100644 --- a/src/Magnum/GL/BufferImage.h +++ b/src/Magnum/GL/BufferImage.h @@ -454,6 +454,12 @@ template class CompressedBufferImage { * @param data Image data * @param usage Image buffer usage * + * The @p data array is expected to be of proper size for given + * parameters. @ref CompressedPixelStorage::compressedBlockSize() and + * @relativeref{CompressedPixelStorage,compressedBlockDataSize()} in + * @p storage are expected to be either both zero or exactly matching + * properties of given @p format. + * * @requires_gl42 Extension @gl_extension{ARB,compressed_texture_pixel_storage} * for non-default @ref CompressedPixelStorage * @requires_gl Non-default @ref CompressedPixelStorage is not @@ -507,6 +513,12 @@ template class CompressedBufferImage { * @param buffer Buffer * @param dataSize Buffer data size * + * The @p dataSize is expected to be of proper size for given + * parameters. @ref CompressedPixelStorage::compressedBlockSize() and + * @relativeref{CompressedPixelStorage,compressedBlockDataSize()} in + * @p storage are expected to be either both zero or exactly matching + * properties of given @p format. + * * If @p dataSize is @cpp 0 @ce, the buffer is unconditionally * reallocated on the first call to @ref setData(). * @requires_gl42 Extension @gl_extension{ARB,compressed_texture_pixel_storage} @@ -558,8 +570,12 @@ template class CompressedBufferImage { * @brief Construct an image placeholder * @param storage Storage of compressed pixel data * - * Format is undefined, size is zero and buffer is empty, call - * @ref setData() to fill the image with data. + * Format and block properties are undefined, size is zero and buffer + * is empty. Call @ref setData() to fill the image with data. + * + * @ref CompressedPixelStorage::compressedBlockSize() and + * @relativeref{CompressedPixelStorage,compressedBlockDataSize()} in + * @p storage are expected to be both zero. * @requires_gl42 Extension @gl_extension{ARB,compressed_texture_pixel_storage} * for non-default @ref CompressedPixelStorage * @requires_gl Non-default @ref CompressedPixelStorage is not @@ -608,6 +624,25 @@ template class CompressedBufferImage { /** @brief Format of compressed pixel data */ CompressedPixelFormat format() const { return _format; } + /** + * @brief Size of a compressed block in pixels + * @m_since_latest + * + * Note that the blocks can be 3D even for 2D images and 2D or 3D even + * for 1D images, in which case only the first slice in the extra + * dimensions is used. + * @see @ref blockDataSize(), @ref compressedPixelFormatBlockSize() + */ + Vector3i blockSize() const { return Vector3i{_blockSize}; } + + /** + * @brief Size of a compressed block in bytes + * @m_since_latest + * + * @see @ref blockSize(), @ref compressedPixelFormatBlockDataSize() + */ + UnsignedInt blockDataSize() const { return _blockDataSize; } + /** @brief Image size */ VectorTypeFor size() const { return _size; } @@ -706,6 +741,11 @@ template class CompressedBufferImage { private: CompressedPixelStorage _storage; CompressedPixelFormat _format; + /* Largest blocks are 12x12 in ASTC and at most 32 bytes, so an 8-bit + type should be more than enough. As even 1D images can have 3D + blocks, the member isn't dependent on dimension count. */ + Vector3ub _blockSize; + UnsignedByte _blockDataSize; Math::Vector _size; Buffer _buffer; std::size_t _dataSize; diff --git a/src/Magnum/GL/CubeMapTexture.cpp b/src/Magnum/GL/CubeMapTexture.cpp index 2084e7097..e1041c8a3 100644 --- a/src/Magnum/GL/CubeMapTexture.cpp +++ b/src/Magnum/GL/CubeMapTexture.cpp @@ -561,7 +561,7 @@ CubeMapTexture& CubeMapTexture::setCompressedSubImage(const Int level, const Vec Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); - glCompressedTextureSubImage3D(_id, level, offset.x(), offset.y(), offset.z(), image.size().x(), image.size().y(), image.size().z(), GLenum(compressedPixelFormat(image.format())), Implementation::occupiedCompressedImageDataSize(image, image.data().size()), image.data()); + glCompressedTextureSubImage3D(_id, level, offset.x(), offset.y(), offset.z(), image.size().x(), image.size().y(), image.size().z(), GLenum(compressedPixelFormat(image.format())), Implementation::occupiedCompressedImageDataSize(image), image.data()); return *this; } @@ -572,7 +572,7 @@ CubeMapTexture& CubeMapTexture::setCompressedSubImage(const Int level, const Vec image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); - glCompressedTextureSubImage3D(_id, level, offset.x(), offset.y(), offset.z(), image.size().x(), image.size().y(), image.size().z(), GLenum(image.format()), Implementation::occupiedCompressedImageDataSize(image, image.dataSize()), nullptr); + glCompressedTextureSubImage3D(_id, level, offset.x(), offset.y(), offset.z(), image.size().x(), image.size().y(), image.size().z(), GLenum(image.format()), Implementation::occupiedCompressedImageDataSize(image), nullptr); return *this; } #endif @@ -604,7 +604,7 @@ CubeMapTexture& CubeMapTexture::setCompressedSubImage(const CubeMapCoordinate co Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); #endif Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); - Context::current().state().texture.cubeCompressedSubImageImplementation(*this, coordinate, level, offset, image.size(), compressedPixelFormat(image.format()), image.data(), Implementation::occupiedCompressedImageDataSize(image, image.data().size())); + Context::current().state().texture.cubeCompressedSubImageImplementation(*this, coordinate, level, offset, image.size(), compressedPixelFormat(image.format()), image.data(), Implementation::occupiedCompressedImageDataSize(image)); return *this; } @@ -612,7 +612,7 @@ CubeMapTexture& CubeMapTexture::setCompressedSubImage(const CubeMapCoordinate co CubeMapTexture& CubeMapTexture::setCompressedSubImage(const CubeMapCoordinate coordinate, const Int level, const Vector2i& offset, CompressedBufferImage2D& image) { image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); - Context::current().state().texture.cubeCompressedSubImageImplementation(*this, coordinate, level, offset, image.size(), image.format(), nullptr, Implementation::occupiedCompressedImageDataSize(image, image.dataSize())); + Context::current().state().texture.cubeCompressedSubImageImplementation(*this, coordinate, level, offset, image.size(), image.format(), nullptr, Implementation::occupiedCompressedImageDataSize(image)); return *this; } #endif diff --git a/src/Magnum/GL/Implementation/imageProperties.h b/src/Magnum/GL/Implementation/imageProperties.h index a02ae0fe8..820970b9a 100644 --- a/src/Magnum/GL/Implementation/imageProperties.h +++ b/src/Magnum/GL/Implementation/imageProperties.h @@ -26,7 +26,7 @@ DEALINGS IN THE SOFTWARE. */ -#include "Magnum/PixelStorage.h" +#include "Magnum/Math/Vector3.h" namespace Magnum { namespace GL { namespace Implementation { @@ -40,18 +40,11 @@ namespace Magnum { namespace GL { namespace Implementation { ARB_compressed_texture_pixel_storage (which makes skip, row length etc. possible for compressed formats) didn't bother thinking about what the existing parameter is for, just left it unchanged, and nobody else in the - commitee bothered either. + commitee bothered either. */ +template std::size_t occupiedCompressedImageDataSize(const T& image) { + const auto realBlockCount = Math::Vector3{(Vector3i::pad(image.size(), 1) + image.blockSize() - Vector3i{1})/image.blockSize()}; - In case the block size properties aren't set, the actual image data size is - used as a backup, which might still be correct in most cases. */ -template std::size_t occupiedCompressedImageDataSize(const T& image, std::size_t dataSize) { - if(image.storage().compressedBlockSize().product() && image.storage().compressedBlockDataSize()) { - const auto realBlockCount = Math::Vector3{(Vector3i::pad(image.size(), 1) + image.storage().compressedBlockSize() - Vector3i{1})/image.storage().compressedBlockSize()}; - - return realBlockCount.product()*image.storage().compressedBlockDataSize(); - } - - return dataSize; + return realBlockCount.product()*image.blockDataSize(); } }}} diff --git a/src/Magnum/GL/Test/BufferImageGLTest.cpp b/src/Magnum/GL/Test/BufferImageGLTest.cpp index 145c5e772..14336ac66 100644 --- a/src/Magnum/GL/Test/BufferImageGLTest.cpp +++ b/src/Magnum/GL/Test/BufferImageGLTest.cpp @@ -52,6 +52,7 @@ struct BufferImageGLTest: OpenGLTester { void constructBufferCompressedGeneric(); void constructInvalidSize(); + void constructCompressedInvalidBlockSize(); void constructCompressedInvalidSize(); void constructMove(); @@ -67,6 +68,7 @@ struct BufferImageGLTest: OpenGLTester { void setDataCompressedGeneric(); void setDataCompressedKeepStorage(); void setDataInvalidSize(); + void setDataCompressedInvalidBlockSize(); void setDataCompressedInvalidSize(); void release(); @@ -86,6 +88,7 @@ BufferImageGLTest::BufferImageGLTest() { &BufferImageGLTest::constructBufferCompressedGeneric, &BufferImageGLTest::constructInvalidSize, + &BufferImageGLTest::constructCompressedInvalidBlockSize, &BufferImageGLTest::constructCompressedInvalidSize, &BufferImageGLTest::constructMove, @@ -101,6 +104,7 @@ BufferImageGLTest::BufferImageGLTest() { &BufferImageGLTest::setDataCompressedGeneric, &BufferImageGLTest::setDataCompressedKeepStorage, &BufferImageGLTest::setDataInvalidSize, + &BufferImageGLTest::setDataCompressedInvalidBlockSize, &BufferImageGLTest::setDataCompressedInvalidSize, &BufferImageGLTest::release, @@ -188,13 +192,16 @@ void BufferImageGLTest::constructPlaceholder() { } void BufferImageGLTest::constructCompressed() { - const char data[] = { 'a', 0, 0, 0, 'b', 0, 0, 0 }; + const char data[]{ + 'a', 0, 0, 0, 0, 0, 0, 0, 'b', 0, 0, 0, 0, 0, 0, 0, + 'c', 0, 0, 0, 0, 0, 0, 0, 'd', 0, 0, 0, 0, 0, 0, 0, + 'e', 0, 0, 0, 0, 0, 0, 0, 'f', 0, 0, 0, 0, 0, 0, 0, + 'g', 0, 0, 0, 0, 0, 0, 0, 'h', 0, 0, 0, 0, 0, 0, 0 + }; CompressedBufferImage2D a{ - #ifndef MAGNUM_TARGET_GLES - CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - #endif + CompressedPixelStorage{}.setRowLength(16), CompressedPixelFormat::RGBAS3tcDxt1, - {4, 4}, data, BufferUsage::StaticDraw}; + {12, 8}, data, BufferUsage::StaticDraw}; #ifndef MAGNUM_TARGET_GLES const auto imageData = a.buffer().data(); @@ -202,12 +209,12 @@ void BufferImageGLTest::constructCompressed() { MAGNUM_VERIFY_NO_GL_ERROR(); - #ifndef MAGNUM_TARGET_GLES - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); - #endif + CORRADE_COMPARE(a.storage().rowLength(), 16); CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt1); - CORRADE_COMPARE(a.size(), Vector2i(4, 4)); - CORRADE_COMPARE(a.dataSize(), 8); + CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(a.blockDataSize(), 8); + CORRADE_COMPARE(a.size(), (Vector2i{12, 8})); + CORRADE_COMPARE(a.dataSize(), 8*8); /** @todo How to verify the contents in ES? */ #ifndef MAGNUM_TARGET_GLES @@ -217,13 +224,16 @@ void BufferImageGLTest::constructCompressed() { } void BufferImageGLTest::constructCompressedGeneric() { - const char data[] = { 'a', 0, 0, 0, 'b', 0, 0, 0 }; + const char data[]{ + 'a', 0, 0, 0, 0, 0, 0, 0, 'b', 0, 0, 0, 0, 0, 0, 0, + 'c', 0, 0, 0, 0, 0, 0, 0, 'd', 0, 0, 0, 0, 0, 0, 0, + 'e', 0, 0, 0, 0, 0, 0, 0, 'f', 0, 0, 0, 0, 0, 0, 0, + 'g', 0, 0, 0, 0, 0, 0, 0, 'h', 0, 0, 0, 0, 0, 0, 0 + }; CompressedBufferImage2D a{ - #ifndef MAGNUM_TARGET_GLES - CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - #endif + CompressedPixelStorage{}.setRowLength(16), Magnum::CompressedPixelFormat::Bc1RGBAUnorm, - {4, 4}, data, BufferUsage::StaticDraw}; + {12, 8}, data, BufferUsage::StaticDraw}; #ifndef MAGNUM_TARGET_GLES const auto imageData = a.buffer().data(); @@ -231,12 +241,12 @@ void BufferImageGLTest::constructCompressedGeneric() { MAGNUM_VERIFY_NO_GL_ERROR(); - #ifndef MAGNUM_TARGET_GLES - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); - #endif + CORRADE_COMPARE(a.storage().rowLength(), 16); CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt1); - CORRADE_COMPARE(a.size(), Vector2i(4, 4)); - CORRADE_COMPARE(a.dataSize(), 8); + CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(a.blockDataSize(), 8); + CORRADE_COMPARE(a.size(), (Vector2i{12, 8})); + CORRADE_COMPARE(a.dataSize(), 8*8); /** @todo How to verify the contents in ES? */ #ifndef MAGNUM_TARGET_GLES @@ -251,6 +261,8 @@ void BufferImageGLTest::constructCompressedPlaceholder() { CORRADE_COMPARE(a.storage().rowLength(), 0); CORRADE_COMPARE(a.format(), CompressedPixelFormat{}); + CORRADE_COMPARE(a.blockSize(), Vector3i{}); + CORRADE_COMPARE(a.blockDataSize(), 0); CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(a.dataSize(), 0); CORRADE_VERIFY(a.buffer().id()); @@ -265,6 +277,8 @@ void BufferImageGLTest::constructCompressedPlaceholder() { CORRADE_COMPARE(a.storage().skip(), (Vector3i{1, 0, 0})); CORRADE_COMPARE(a.storage().rowLength(), 12); CORRADE_COMPARE(a.format(), CompressedPixelFormat{}); + CORRADE_COMPARE(a.blockSize(), Vector3i{}); + CORRADE_COMPARE(a.blockDataSize(), 0); CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(a.dataSize(), 0); CORRADE_VERIFY(a.buffer().id()); @@ -334,17 +348,20 @@ void BufferImageGLTest::constructBufferGeneric() { } void BufferImageGLTest::constructBufferCompressed() { - const char data[] = { 'a', 0, 0, 0, 'b', 0, 0, 0 }; + const char data[]{ + 'a', 0, 0, 0, 0, 0, 0, 0, 'b', 0, 0, 0, 0, 0, 0, 0, + 'c', 0, 0, 0, 0, 0, 0, 0, 'd', 0, 0, 0, 0, 0, 0, 0, + 'e', 0, 0, 0, 0, 0, 0, 0, 'f', 0, 0, 0, 0, 0, 0, 0, + 'g', 0, 0, 0, 0, 0, 0, 0, 'h', 0, 0, 0, 0, 0, 0, 0 + }; Buffer buffer; buffer.setData(data, BufferUsage::StaticDraw); const UnsignedInt id = buffer.id(); CompressedBufferImage2D a{ - #ifndef MAGNUM_TARGET_GLES - CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - #endif + CompressedPixelStorage{}.setRowLength(16), CompressedPixelFormat::RGBAS3tcDxt1, - {4, 4}, Utility::move(buffer), sizeof(data)}; + {12, 8}, Utility::move(buffer), sizeof(data)}; #ifndef MAGNUM_TARGET_GLES const auto imageData = a.buffer().data(); @@ -352,14 +369,14 @@ void BufferImageGLTest::constructBufferCompressed() { MAGNUM_VERIFY_NO_GL_ERROR(); - #ifndef MAGNUM_TARGET_GLES - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); - #endif + CORRADE_COMPARE(a.storage().rowLength(), 16); CORRADE_VERIFY(!buffer.id()); CORRADE_COMPARE(a.buffer().id(), id); CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt1); - CORRADE_COMPARE(a.size(), Vector2i(4, 4)); - CORRADE_COMPARE(a.dataSize(), 8); + CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(a.blockDataSize(), 8); + CORRADE_COMPARE(a.size(), (Vector2i{12, 8})); + CORRADE_COMPARE(a.dataSize(), 8*8); /** @todo How to verify the contents in ES? */ #ifndef MAGNUM_TARGET_GLES @@ -369,17 +386,20 @@ void BufferImageGLTest::constructBufferCompressed() { } void BufferImageGLTest::constructBufferCompressedGeneric() { - const char data[] = { 'a', 0, 0, 0, 'b', 0, 0, 0 }; + const char data[]{ + 'a', 0, 0, 0, 0, 0, 0, 0, 'b', 0, 0, 0, 0, 0, 0, 0, + 'c', 0, 0, 0, 0, 0, 0, 0, 'd', 0, 0, 0, 0, 0, 0, 0, + 'e', 0, 0, 0, 0, 0, 0, 0, 'f', 0, 0, 0, 0, 0, 0, 0, + 'g', 0, 0, 0, 0, 0, 0, 0, 'h', 0, 0, 0, 0, 0, 0, 0 + }; Buffer buffer; buffer.setData(data, BufferUsage::StaticDraw); const UnsignedInt id = buffer.id(); CompressedBufferImage2D a{ - #ifndef MAGNUM_TARGET_GLES - CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - #endif + CompressedPixelStorage{}.setRowLength(16), Magnum::CompressedPixelFormat::Bc1RGBAUnorm, - {4, 4}, Utility::move(buffer), sizeof(data)}; + {12, 8}, Utility::move(buffer), sizeof(data)}; #ifndef MAGNUM_TARGET_GLES const auto imageData = a.buffer().data(); @@ -387,14 +407,14 @@ void BufferImageGLTest::constructBufferCompressedGeneric() { MAGNUM_VERIFY_NO_GL_ERROR(); - #ifndef MAGNUM_TARGET_GLES - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); - #endif + CORRADE_COMPARE(a.storage().rowLength(), 16); CORRADE_VERIFY(!buffer.id()); CORRADE_COMPARE(a.buffer().id(), id); CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt1); - CORRADE_COMPARE(a.size(), Vector2i(4, 4)); - CORRADE_COMPARE(a.dataSize(), 8); + CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(a.blockDataSize(), 8); + CORRADE_COMPARE(a.size(), (Vector2i{12, 8})); + CORRADE_COMPARE(a.dataSize(), 8*8); /** @todo How to verify the contents in ES? */ #ifndef MAGNUM_TARGET_GLES @@ -412,10 +432,43 @@ void BufferImageGLTest::constructInvalidSize() { CORRADE_COMPARE(out, "GL::BufferImage: data too small, got 11 but expected at least 12 bytes\n"); } -void BufferImageGLTest::constructCompressedInvalidSize() { +void BufferImageGLTest::constructCompressedInvalidBlockSize() { CORRADE_SKIP_IF_NO_ASSERT(); - CORRADE_EXPECT_FAIL("Size checking for compressed image data is not implemented yet."); + /* This is okay */ + const char data[8]{}; + CompressedBufferImage2D{CompressedPixelStorage{} + .setCompressedBlockSize({4, 4, 1}) + .setCompressedBlockDataSize(8), + CompressedPixelFormat::SRGBS3tcDxt1, {1, 1}, data, BufferUsage::StaticDraw}; + + /* Tested mainly in ImageViewTest, here is just a subset to verify the same + helper is used internally and a proper prefix is printed. The block size + is picked up implicitly so it cannot be 0 or >= 256. */ + Containers::String out; + Error redirectError{&out}; + CompressedBufferImage2D{CompressedPixelStorage{} + .setCompressedBlockSize({5, 5, 5}) + .setCompressedBlockDataSize(8), + CompressedPixelFormat::SRGBS3tcDxt1, {1, 1}, data, BufferUsage::StaticDraw}; + CompressedBufferImage2D{CompressedPixelStorage{} + .setCompressedBlockSize({4, 4, 1}) + .setCompressedBlockDataSize(4), + CompressedPixelFormat::SRGBS3tcDxt1, {1, 1}, data, BufferUsage::StaticDraw}; + CompressedBufferImage2D{CompressedPixelStorage{} + .setCompressedBlockSize({0, 1, 0})}; + CompressedBufferImage2D{CompressedPixelStorage{} + .setCompressedBlockDataSize(1)}; + CORRADE_COMPARE_AS(out, + "GL::CompressedBufferImage: expected pixel storage block size to be either not set at all or equal to {4, 4, 1} but got {5, 5, 5}\n" + "GL::CompressedBufferImage: expected pixel storage block data size to be either not set at all or equal to 8 but got 4\n" + "GL::CompressedBufferImage: expected pixel storage block size to not be set at all but got {0, 1, 0}\n" + "GL::CompressedBufferImage: expected pixel storage block data size to not be set at all but got 1\n", + TestSuite::Compare::String); +} + +void BufferImageGLTest::constructCompressedInvalidSize() { + CORRADE_SKIP_IF_NO_ASSERT(); /* Too small for given format */ { @@ -488,8 +541,16 @@ void BufferImageGLTest::constructMove() { } void BufferImageGLTest::constructMoveCompressed() { - const char data[] = { 'a', 0, 0, 0, 'b', 0, 0, 0 }; - CompressedBufferImage2D a{CompressedPixelFormat::RGBAS3tcDxt1, {4, 4}, data, BufferUsage::StaticDraw}; + const char data[]{ + 'a', 0, 0, 0, 0, 0, 0, 0, 'b', 0, 0, 0, 0, 0, 0, 0, + 'c', 0, 0, 0, 0, 0, 0, 0, 'd', 0, 0, 0, 0, 0, 0, 0, + 'e', 0, 0, 0, 0, 0, 0, 0, 'f', 0, 0, 0, 0, 0, 0, 0, + 'g', 0, 0, 0, 0, 0, 0, 0, 'h', 0, 0, 0, 0, 0, 0, 0 + }; + CompressedBufferImage2D a{ + CompressedPixelStorage{}.setRowLength(16), + CompressedPixelFormat::RGBAS3tcDxt1, + {12, 8}, data, BufferUsage::StaticDraw}; const Int id = a.buffer().id(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -501,20 +562,20 @@ void BufferImageGLTest::constructMoveCompressed() { CORRADE_COMPARE(a.size(), Vector2i()); CORRADE_COMPARE(a.dataSize(), 0); - #ifndef MAGNUM_TARGET_GLES - CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{0}); - #endif + CORRADE_COMPARE(b.storage().rowLength(), 16); CORRADE_COMPARE(b.format(), CompressedPixelFormat::RGBAS3tcDxt1); - CORRADE_COMPARE(b.size(), Vector2i(4, 4)); - CORRADE_COMPARE(b.dataSize(), 8); + CORRADE_COMPARE(b.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(b.blockDataSize(), 8); + CORRADE_COMPARE(b.size(), (Vector2i{12, 8})); + CORRADE_COMPARE(b.dataSize(), 8*8); CORRADE_COMPARE(b.buffer().id(), id); - const unsigned char data2[] = { 'a', 0, 0, 0, 'b', 0, 0, 0, 'c', 0, 0, 0, 'd', 0, 0, 0 }; + const char data2[]{ + 'h', 0, 0, 0, 'i', 0, 0, 0, 'j', 0, 0, 0, 'k', 0, 0, 0 + }; CompressedBufferImage2D c{ - #ifndef MAGNUM_TARGET_GLES - CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - #endif - CompressedPixelFormat::RGBAS3tcDxt1, {8, 4}, data2, BufferUsage::StaticDraw}; + CompressedPixelFormat::RGBAAstc5x5, {5, 5}, + data2, BufferUsage::StaticDraw}; const Int cId = c.buffer().id(); c = Utility::move(b); @@ -522,15 +583,17 @@ void BufferImageGLTest::constructMoveCompressed() { CORRADE_VERIFY(cId > 0); CORRADE_COMPARE(b.buffer().id(), cId); - CORRADE_COMPARE(b.size(), Vector2i(8, 4)); + CORRADE_COMPARE(b.size(), (Vector2i{5, 5})); CORRADE_COMPARE(b.dataSize(), 16); #ifndef MAGNUM_TARGET_GLES CORRADE_COMPARE(c.storage().compressedBlockSize(), Vector3i{0}); #endif CORRADE_COMPARE(c.format(), CompressedPixelFormat::RGBAS3tcDxt1); - CORRADE_COMPARE(c.size(), Vector2i(4, 4)); - CORRADE_COMPARE(c.dataSize(), 8); + CORRADE_COMPARE(c.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(c.blockDataSize(), 8); + CORRADE_COMPARE(c.size(), (Vector2i{12, 8})); + CORRADE_COMPARE(c.dataSize(), 8*8); CORRADE_COMPARE(c.buffer().id(), id); CORRADE_VERIFY(std::is_nothrow_move_constructible::value); @@ -549,17 +612,16 @@ void BufferImageGLTest::dataProperties() { } void BufferImageGLTest::dataPropertiesCompressed() { - /* Yes, I know, this is totally bogus and doesn't match the BC1 format */ - const char data[1]{}; + const char data[336]{}; CompressedBufferImage3D image{ CompressedPixelStorage{} - .setCompressedBlockSize({3, 4, 5}) - .setCompressedBlockDataSize(16) - .setImageHeight(12) - .setSkip({5, 8, 11}), - Magnum::CompressedPixelFormat::Bc1RGBAUnorm, {2, 8, 11}, data, BufferUsage::StaticDraw}; + .setRowLength(12) + .setImageHeight(8) + .setSkip({8, 4, 4}), + CompressedPixelFormat::RGBAS3tcDxt1, {2, 3, 3}, + data, BufferUsage::StaticDraw}; CORRADE_COMPARE(image.dataProperties(), - (std::pair, Math::Vector3>{{2*16, 2*16, 9*16}, {1, 3, 3}})); + (std::pair, Math::Vector3>{{16, 24, 192}, {3, 2, 3}})); } void BufferImageGLTest::setData() { @@ -654,15 +716,21 @@ void BufferImageGLTest::setDataKeepStorage() { } void BufferImageGLTest::setDataCompressed() { - const char data[] = { 'a', 0, 0, 0, 'b', 0, 0, 0 }; - CompressedBufferImage2D a{CompressedPixelFormat::RGBAS3tcDxt1, {4, 4}, data, BufferUsage::StaticDraw}; + const char data[]{ + 'h', 0, 0, 0, 'i', 0, 0, 0, 'j', 0, 0, 0, 'k', 0, 0, 0 + }; + CompressedBufferImage2D a{CompressedPixelFormat::RGBAAstc5x5, {5, 5}, data, BufferUsage::StaticDraw}; - const char data2[] = { 'a', 0, 0, 0, 'b', 0, 0, 0, 'c', 0, 0, 0, 'd', 0, 0, 0 }; + const char data2[]{ + 'a', 0, 0, 0, 0, 0, 0, 0, 'b', 0, 0, 0, 0, 0, 0, 0, + 'c', 0, 0, 0, 0, 0, 0, 0, 'd', 0, 0, 0, 0, 0, 0, 0, + 'e', 0, 0, 0, 0, 0, 0, 0, 'f', 0, 0, 0, 0, 0, 0, 0, + 'g', 0, 0, 0, 0, 0, 0, 0, 'h', 0, 0, 0, 0, 0, 0, 0 + }; a.setData( - #ifndef MAGNUM_TARGET_GLES - CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - #endif - CompressedPixelFormat::RGBAS3tcDxt3, {8, 4}, data2, BufferUsage::StaticDraw); + CompressedPixelStorage{}.setRowLength(16), + CompressedPixelFormat::RGBAS3tcDxt1, + {12, 8}, data2, BufferUsage::StaticDraw); #ifndef MAGNUM_TARGET_GLES const auto imageData = a.buffer().data(); @@ -670,12 +738,12 @@ void BufferImageGLTest::setDataCompressed() { MAGNUM_VERIFY_NO_GL_ERROR(); - #ifndef MAGNUM_TARGET_GLES - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); - #endif - CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt3); - CORRADE_COMPARE(a.size(), Vector2i(8, 4)); - CORRADE_COMPARE(a.dataSize(), 16); + CORRADE_COMPARE(a.storage().rowLength(), 16); + CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt1); + CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(a.blockDataSize(), 8); + CORRADE_COMPARE(a.size(), (Vector2i{12, 8})); + CORRADE_COMPARE(a.dataSize(), 8*8); /** @todo How to verify the contents in ES? */ #ifndef MAGNUM_TARGET_GLES @@ -685,15 +753,21 @@ void BufferImageGLTest::setDataCompressed() { } void BufferImageGLTest::setDataCompressedGeneric() { - const char data[] = { 'a', 0, 0, 0, 'b', 0, 0, 0 }; - CompressedBufferImage2D a{CompressedPixelFormat::RGBAS3tcDxt1, {4, 4}, data, BufferUsage::StaticDraw}; + const char data[]{ + 'h', 0, 0, 0, 'i', 0, 0, 0, 'j', 0, 0, 0, 'k', 0, 0, 0 + }; + CompressedBufferImage2D a{CompressedPixelFormat::RGBAAstc5x5, {5, 5}, data, BufferUsage::StaticDraw}; - const char data2[] = { 'a', 0, 0, 0, 'b', 0, 0, 0, 'c', 0, 0, 0, 'd', 0, 0, 0 }; + const char data2[]{ + 'a', 0, 0, 0, 0, 0, 0, 0, 'b', 0, 0, 0, 0, 0, 0, 0, + 'c', 0, 0, 0, 0, 0, 0, 0, 'd', 0, 0, 0, 0, 0, 0, 0, + 'e', 0, 0, 0, 0, 0, 0, 0, 'f', 0, 0, 0, 0, 0, 0, 0, + 'g', 0, 0, 0, 0, 0, 0, 0, 'h', 0, 0, 0, 0, 0, 0, 0 + }; a.setData( - #ifndef MAGNUM_TARGET_GLES - CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - #endif - Magnum::CompressedPixelFormat::Bc2RGBAUnorm, {8, 4}, data2, BufferUsage::StaticDraw); + CompressedPixelStorage{}.setRowLength(16), + Magnum::CompressedPixelFormat::Bc1RGBAUnorm, + {12, 8}, data2, BufferUsage::StaticDraw); #ifndef MAGNUM_TARGET_GLES const auto imageData = a.buffer().data(); @@ -701,12 +775,12 @@ void BufferImageGLTest::setDataCompressedGeneric() { MAGNUM_VERIFY_NO_GL_ERROR(); - #ifndef MAGNUM_TARGET_GLES - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); - #endif - CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt3); - CORRADE_COMPARE(a.size(), Vector2i(8, 4)); - CORRADE_COMPARE(a.dataSize(), 16); + CORRADE_COMPARE(a.storage().rowLength(), 16); + CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt1); + CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(a.blockDataSize(), 8); + CORRADE_COMPARE(a.size(), (Vector2i{12, 8})); + CORRADE_COMPARE(a.dataSize(), 8*8); /** @todo How to verify the contents in ES? */ #ifndef MAGNUM_TARGET_GLES @@ -763,6 +837,39 @@ void BufferImageGLTest::setDataInvalidSize() { TestSuite::Compare::String); } +void BufferImageGLTest::setDataCompressedInvalidBlockSize() { + CORRADE_SKIP_IF_NO_ASSERT(); + + /* This is okay */ + const char data[8]{}; + CompressedBufferImage2D image; + image.setData( + CompressedPixelStorage{} + .setCompressedBlockSize({4, 4, 1}) + .setCompressedBlockDataSize(8), + CompressedPixelFormat::SRGBS3tcDxt1, {1, 1}, data, BufferUsage::StaticDraw); + + /* Tested mainly in ImageViewTest, here is just a subset to verify the same + helper is used internally and a proper prefix is printed. The block size + is picked up implicitly so it cannot be 0 or >= 256. */ + Containers::String out; + Error redirectError{&out}; + image.setData( + CompressedPixelStorage{} + .setCompressedBlockSize({5, 5, 5}) + .setCompressedBlockDataSize(8), + CompressedPixelFormat::SRGBS3tcDxt1, {1, 1}, data, BufferUsage::StaticDraw); + image.setData( + CompressedPixelStorage{} + .setCompressedBlockSize({4, 4, 1}) + .setCompressedBlockDataSize(4), + CompressedPixelFormat::SRGBS3tcDxt1, {1, 1}, data, BufferUsage::StaticDraw); + CORRADE_COMPARE_AS(out, + "GL::CompressedBufferImage::setData(): expected pixel storage block size to be either not set at all or equal to {4, 4, 1} but got {5, 5, 5}\n" + "GL::CompressedBufferImage::setData(): expected pixel storage block data size to be either not set at all or equal to 8 but got 4\n", + TestSuite::Compare::String); +} + void BufferImageGLTest::setDataCompressedInvalidSize() { CORRADE_SKIP_IF_NO_ASSERT(); @@ -770,8 +877,6 @@ void BufferImageGLTest::setDataCompressedInvalidSize() { CompressedBufferImage2D a{CompressedPixelFormat::RGBAS3tcDxt1, {4, 4}, "helloheyhellhe", BufferUsage::StaticDraw}; CORRADE_COMPARE(a.dataSize(), 15); - CORRADE_EXPECT_FAIL("Size checking for compressed image data is not implemented yet."); - /* Too small for given format */ { Containers::String out; diff --git a/src/Magnum/GL/Test/BufferImageTest.cpp b/src/Magnum/GL/Test/BufferImageTest.cpp index f362e4462..abfd6d113 100644 --- a/src/Magnum/GL/Test/BufferImageTest.cpp +++ b/src/Magnum/GL/Test/BufferImageTest.cpp @@ -70,6 +70,8 @@ void BufferImageTest::constructNoCreateCompressed() { CORRADE_COMPARE(image.buffer().id(), 0); CORRADE_COMPARE(image.size(), Vector2i{}); CORRADE_COMPARE(image.format(), CompressedPixelFormat{}); + CORRADE_COMPARE(image.blockSize(), Vector3i{}); + CORRADE_COMPARE(image.blockDataSize(), 0); CORRADE_COMPARE(image.dataSize(), 0); } diff --git a/src/Magnum/GL/Test/PixelStorageTest.cpp b/src/Magnum/GL/Test/PixelStorageTest.cpp index 736bf20ad..edb30986c 100644 --- a/src/Magnum/GL/Test/PixelStorageTest.cpp +++ b/src/Magnum/GL/Test/PixelStorageTest.cpp @@ -48,51 +48,37 @@ void PixelStorageTest::occupiedCompressedImageDataSize() { being 55x28x12 */ const char data[(55/5)*(28/4)*(12/2)*16]{}; - /* If we have no block properties, the passed data size is taken, assuming - it's the best bet. Image size or row length and image height isn't taken - into account in any way. */ - { - CompressedImageView3D image{ - CompressedPixelStorage{} - .setRowLength(55) - .setImageHeight(28) - .setSkip({10, 8, 4}), - 42069, /* custom format */ - {35, 20, 6}, - data}; - CORRADE_COMPARE(Implementation::occupiedCompressedImageDataSize(image, 1337), 1337); - - /* If we have block properties, the size is calculated from those and the - *image* size, not the supplied row length / image height. This is what - GL wants, it has no relation to anything useful. For comparison see the PixelStorageTest::dataOffsetSizeCompressed() test in the core + /* The size is calculated from block properties and the *image* size, not + the supplied row length / image height. This is what GL wants, it has no + relation to anything useful. For comparison see the PixelStorageTest::dataOffsetSizeCompressed() test in the core library. */ - } { + { CompressedImageView3D image{ CompressedPixelStorage{} - .setCompressedBlockSize({5, 4, 2}) - .setCompressedBlockDataSize(16) .setRowLength(55) .setImageHeight(28) .setSkip({10, 8, 4}), 42069, /* custom format */ + {5, 4, 2}, + 16, {35, 20, 6}, data}; - CORRADE_COMPARE(Implementation::occupiedCompressedImageDataSize(image, 1337), + CORRADE_COMPARE(Implementation::occupiedCompressedImageDataSize(image), (35/5)*(20/4)*(6/2)*16); /* Same result if the size isn't whole blocks */ } { CompressedImageView3D image{ CompressedPixelStorage{} - .setCompressedBlockSize({5, 4, 2}) - .setCompressedBlockDataSize(16) .setRowLength(55) .setImageHeight(28) .setSkip({10, 8, 4}), 42069, /* custom format */ + {5, 4, 2}, + 16, {31, 19, 5}, data}; - CORRADE_COMPARE(Implementation::occupiedCompressedImageDataSize(image, 1337), + CORRADE_COMPARE(Implementation::occupiedCompressedImageDataSize(image), (35/5)*(20/4)*(6/2)*16); } } diff --git a/src/Magnum/Image.cpp b/src/Magnum/Image.cpp index 99b0e477b..43326771b 100644 --- a/src/Magnum/Image.cpp +++ b/src/Magnum/Image.cpp @@ -96,18 +96,45 @@ template Containers::Array Image::rele return data; } -template CompressedImage::CompressedImage(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor& size, Containers::Array&& data, const ImageFlags flags) noexcept: _storage{storage}, _format{format}, _flags{flags}, _size{size}, _data{Utility::move(data)} { +template CompressedImage::CompressedImage(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor& size, Containers::Array&& data, const ImageFlags flags) noexcept: CompressedImage{storage, format, (CORRADE_CONSTEXPR_ASSERT(!isCompressedPixelFormatImplementationSpecific(format), "CompressedImage: can't determine block size of an implementation-specific pixel format" << Debug::hex << compressedPixelFormatUnwrap(format) << Debug::nospace << ", pass it explicitly"), compressedPixelFormatBlockSize(format)), ( + /* Have to do it like this to ensure the above assertion is printed before + the subsequent one from compressedPixelFormatBlockDataSize(), as + compilers are free to reorder these as they want */ #ifndef CORRADE_NO_ASSERT + isCompressedPixelFormatImplementationSpecific(format) ? 0 : + #endif + compressedPixelFormatBlockDataSize(format) +), size, Utility::move(data), flags} {} + +template CompressedImage::CompressedImage(const CompressedPixelStorage storage, const CompressedPixelFormat format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor& size, Containers::Array&& data, const ImageFlags flags) noexcept: _storage{storage}, _format{format}, _flags{flags}, _blockSize{Vector3ub{blockSize}}, _blockDataSize{UnsignedByte(blockDataSize)}, _size{size}, _data{Utility::move(data)} { + #ifndef CORRADE_NO_ASSERT + const bool passed = + Implementation::checkBlockProperties("CompressedImage:", blockSize, blockDataSize); + #ifdef CORRADE_GRACEFUL_ASSERT + /* If the above check fails on a build with graceful assertions, the data + size check below could then die on division by zero. Exit early in that + case. */ + /** @todo any better idea to handle this? ugh */ + if(!passed) return; + #else + static_cast(passed); + #endif + Implementation::checkBlockPropertiesForStorage("CompressedImage:", blockSize, blockDataSize, storage); + CORRADE_ASSERT(Implementation::compressedImageDataSize(*this) <= _data.size(), "CompressedImage: data too small, got" << _data.size() << "but expected at least" << Implementation::compressedImageDataSize(*this) << "bytes", ); Implementation::checkImageFlagsForSize("CompressedImage:", flags, size); #endif } -template CompressedImage::CompressedImage(const CompressedPixelStorage storage, const UnsignedInt format, const VectorTypeFor& size, Containers::Array&& data, const ImageFlags flags) noexcept: CompressedImage{storage, compressedPixelFormatWrap(format), size, Utility::move(data), flags} {} +template CompressedImage::CompressedImage(const CompressedPixelStorage storage, const UnsignedInt format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor& size, Containers::Array&& data, const ImageFlags flags) noexcept: CompressedImage{storage, compressedPixelFormatWrap(format), blockSize, blockDataSize, size, Utility::move(data), flags} {} -template CompressedImage::CompressedImage(const CompressedPixelStorage storage) noexcept: _storage{storage}, _format{} {} +template CompressedImage::CompressedImage(const CompressedPixelStorage storage) noexcept: _storage{storage}, _format{}, _blockDataSize{} { + CORRADE_ASSERT(storage.compressedBlockSize() == Vector3i{}, + "CompressedImage: expected pixel storage block size to not be set at all but got" << Debug::packed << storage.compressedBlockSize(), ); + CORRADE_ASSERT(!storage.compressedBlockDataSize(), + "CompressedImage: expected pixel storage block data size to not be set at all but got" << storage.compressedBlockDataSize(), ); +} -template CompressedImage::CompressedImage(CompressedImage&& other) noexcept: _storage{Utility::move(other._storage)}, _format{Utility::move(other._format)}, _flags{other._flags}, _size{Utility::move(other._size)}, _data{Utility::move(other._data)} -{ +template CompressedImage::CompressedImage(CompressedImage&& other) noexcept: _storage{other._storage}, _format{other._format}, _flags{other._flags}, _blockSize{other._blockSize}, _blockDataSize{other._blockDataSize}, _size{other._size}, _data{Utility::move(other._data)} { other._size = {}; } @@ -116,17 +143,19 @@ template CompressedImage& CompressedImage CompressedImage::operator BasicMutableCompressedImageView() { - return BasicMutableCompressedImageView{_storage, _format, _size, _data, _flags}; + return BasicMutableCompressedImageView{_storage, _format, Vector3i{_blockSize}, _blockDataSize, _size, _data, _flags}; } template CompressedImage::operator BasicCompressedImageView() const { - return BasicCompressedImageView{_storage, _format, _size, _data, _flags}; + return BasicCompressedImageView{_storage, _format, Vector3i{_blockSize}, _blockDataSize, _size, _data, _flags}; } template std::pair, VectorTypeFor> CompressedImage::dataProperties() const { diff --git a/src/Magnum/Image.h b/src/Magnum/Image.h index f362ed090..f43742786 100644 --- a/src/Magnum/Image.h +++ b/src/Magnum/Image.h @@ -573,8 +573,23 @@ template class CompressedImage { * @param data Image data * @param flags Image layout flags * - * For a 3D image, if @p flags contain @ref ImageFlag3D::CubeMap, the - * @p size is expected to match its restrictions. + * The @p data array is expected to be of proper size for given + * parameters. For a 3D image, if @p flags contain + * @ref ImageFlag3D::CubeMap, the @p size is expected to match its + * restrictions. + * + * The @p format is expected to not be implementation-specific, use the + * @ref CompressedImage(CompressedPixelStorage, CompressedPixelFormat, const Vector3i&, UnsignedInt, const VectorTypeFor&, Containers::Array&&, ImageFlags) + * overload to explicitly pass pass an implementation-specific + * @ref CompressedPixelFormat along with its block properties, or the + * @ref CompressedImage(CompressedPixelStorage, T, const VectorTypeFor&, Containers::Array&&, ImageFlags) + * overload with the original implementation-specific enum type to have + * the pixel size determined implicitly. + * + * @ref CompressedPixelStorage::compressedBlockSize() and + * @relativeref{CompressedPixelStorage,compressedBlockDataSize()} in + * @p storage are expected to be either both zero or exactly matching + * properties of given @p format. */ explicit CompressedImage(CompressedPixelStorage storage, CompressedPixelFormat format, const VectorTypeFor& size, Containers::Array&& data, ImageFlags flags = {}) noexcept; @@ -590,6 +605,51 @@ template class CompressedImage { */ explicit CompressedImage(CompressedPixelFormat format, const VectorTypeFor& size, Containers::Array&& data, ImageFlags flags = {}) noexcept: CompressedImage{{}, format, size, Utility::move(data), flags} {} + /** + * @brief Construct a compressed image with an implementation-specific pixel format + * @param storage Storage of compressed pixel data + * @param format Format of compressed pixel data + * @param blockSize Size of a compressed block in given format, + * in pixels + * @param blockDataSize Size of a compressed block in given format, + * in bytes + * @param size Image size, in pixels + * @param data Image data + * @param flags Image layout flags + * @m_since_latest + * + * Unlike with @ref CompressedImage(CompressedPixelStorage, CompressedPixelFormat, const VectorTypeFor&, Containers::Array&&, ImageFlags), + * where block size is determined automatically + * @ref compressedPixelFormatBlockSize() and + * @ref compressedPixelFormatBlockDataSize(), this allows you to + * specify an implementation-specific pixel format and block properties + * directly. Uses @ref compressedPixelFormatWrap() internally to wrap + * @p format in @ref CompressedPixelFormat. The @p blockSize and + * @p blockDataSize is expected to be greater than @cpp 0 @ce and less + * than @cpp 256 @ce. Note that the blocks can be 3D even for 2D images + * and 2D or 3D even for 1D images, in which case only the first slice + * in the extra dimensions is used. + * + * @ref CompressedPixelStorage::compressedBlockSize() and + * @relativeref{CompressedPixelStorage,compressedBlockDataSize()} in + * @p storage are expected to be either both zero or exactly matching + * @p blockSize and @p blockDataSize. + * + * The @p data array is expected to be of proper size for given + * parameters. For a 3D image, if @p flags contain + * @ref ImageFlag3D::CubeMap, the @p size is expected to match its + * restrictions. + */ + explicit CompressedImage(CompressedPixelStorage storage, UnsignedInt format, const Vector3i& blockSize, UnsignedInt blockDataSize, const VectorTypeFor& size, Containers::Array&& data, ImageFlags flags = {}) noexcept; + + /** @overload + * @m_since_latest + * + * Equivalent to the above for @p format already wrapped with + * @ref compressedPixelFormatWrap(). + */ + explicit CompressedImage(CompressedPixelStorage storage, CompressedPixelFormat format, const Vector3i& blockSize, UnsignedInt blockDataSize, const VectorTypeFor& size, Containers::Array&& data, ImageFlags flags = {}) noexcept; + /** * @brief Construct a compressed image with implementation-specific format * @param storage Storage of compressed pixel data @@ -598,11 +658,10 @@ template class CompressedImage { * @param data Image data * @param flags Image layout flags * - * Uses @ref compressedPixelFormatWrap() internally to convert - * @p format to @ref CompressedPixelFormat. - * - * For a 3D image, if @p flags contain @ref ImageFlag3D::CubeMap, the - * @p size is expected to match its restrictions. + * Uses ADL to find a corresponding @cpp compressedPixelFormatBlockSize(T) @ce + * and @cpp compressedPixelFormatBlockDataSize(T) @ce overloads, then + * calls @ref CompressedImage(CompressedPixelStorage, UnsignedInt, const Vector3i&, UnsignedInt, const VectorTypeFor&, Containers::Array&&, ImageFlags) + * with determined block size properties. */ template explicit CompressedImage(CompressedPixelStorage storage, T format, const VectorTypeFor& size, Containers::Array&& data, ImageFlags flags = {}) noexcept; @@ -622,9 +681,13 @@ template class CompressedImage { * @brief Construct an image placeholder * @param storage Storage of compressed pixel data * - * Format is undefined, size is zero, data is @cpp nullptr @ce and - * data layout flags are empty. Move over a non-empty instance to make - * it useful. + * Format and block properties are undefined, size is zero, data is + * @cpp nullptr @ce and data layout flags are empty. Move over a + * non-empty instance to make it useful. + * + * @ref CompressedPixelStorage::compressedBlockSize() and + * @relativeref{CompressedPixelStorage,compressedBlockDataSize()} in + * @p storage are expected to be both zero. */ /* No ImageFlags parameter here as this constructor is mainly used to query GL textures, and there the flags are forcibly reset */ @@ -681,6 +744,25 @@ template class CompressedImage { */ CompressedPixelFormat format() const { return _format; } + /** + * @brief Size of a compressed block in pixels + * @m_since_latest + * + * Note that the blocks can be 3D even for 2D images and 2D or 3D even + * for 1D images, in which case only the first slice in the extra + * dimensions is used. + * @see @ref blockDataSize(), @ref compressedPixelFormatBlockSize() + */ + Vector3i blockSize() const { return Vector3i{_blockSize}; } + + /** + * @brief Size of a compressed block in bytes + * @m_since_latest + * + * @see @ref blockSize(), @ref compressedPixelFormatBlockDataSize() + */ + UnsignedInt blockDataSize() const { return _blockDataSize; } + /** @brief Image size in pixels */ /* Unlike other getters this one is a const& so it's possible to slice to the sizes when all images are in an array, for example for use @@ -732,13 +814,14 @@ template class CompressedImage { Containers::Array release(); private: - /* To be made public once block size and block data size are stored - together with the image */ - explicit CompressedImage(CompressedPixelStorage storage, UnsignedInt format, const VectorTypeFor& size, Containers::Array&& data, ImageFlags flags) noexcept; - CompressedPixelStorage _storage; CompressedPixelFormat _format; ImageFlags _flags; + /* Largest blocks are 12x12 in ASTC and at most 32 bytes, so an 8-bit + type should be more than enough. As even 1D images can have 3D + blocks, the member isn't dependent on dimension count. */ + Vector3ub _blockSize; + UnsignedByte _blockDataSize; VectorTypeFor _size; Containers::Array _data; }; @@ -772,7 +855,7 @@ template template inline Image::Ima "format types larger than 32bits are not supported"); } -template template inline CompressedImage::CompressedImage(const CompressedPixelStorage storage, const T format, const VectorTypeFor& size, Containers::Array&& data, const ImageFlags flags) noexcept: CompressedImage{storage, UnsignedInt(format), size, Utility::move(data), flags} { +template template inline CompressedImage::CompressedImage(const CompressedPixelStorage storage, const T format, const VectorTypeFor& size, Containers::Array&& data, const ImageFlags flags) noexcept: CompressedImage{storage, UnsignedInt(format), compressedPixelFormatBlockSize(format), compressedPixelFormatBlockDataSize(format), size, Utility::move(data), flags} { static_assert(sizeof(T) <= 4, "format types larger than 32bits are not supported"); } diff --git a/src/Magnum/ImageView.cpp b/src/Magnum/ImageView.cpp index dd2b861d0..f1306713f 100644 --- a/src/Magnum/ImageView.cpp +++ b/src/Magnum/ImageView.cpp @@ -68,26 +68,66 @@ template auto ImageView::pixels( return Implementation::imagePixelView(*this, data()); } -template CompressedImageView::CompressedImageView(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor& size, const Containers::ArrayView data, const ImageFlags flags) noexcept: _storage{storage}, _format{format}, _flags{flags}, _size{size}, _data{reinterpret_cast(data.data()), data.size()} { +template CompressedImageView::CompressedImageView(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor& size, const Containers::ArrayView data, const ImageFlags flags) noexcept: CompressedImageView{storage, format, (CORRADE_CONSTEXPR_ASSERT(!isCompressedPixelFormatImplementationSpecific(format), "CompressedImageView: can't determine block size of an implementation-specific pixel format" << Debug::hex << compressedPixelFormatUnwrap(format) << Debug::nospace << ", pass it explicitly"), compressedPixelFormatBlockSize(format)), ( + /* Have to do it like this to ensure the above assertion is printed before + the subsequent one from compressedPixelFormatBlockDataSize(), as + compilers are free to reorder these as they want */ #ifndef CORRADE_NO_ASSERT + isCompressedPixelFormatImplementationSpecific(format) ? 0 : + #endif + compressedPixelFormatBlockDataSize(format) +), size, data, flags} {} + +template CompressedImageView::CompressedImageView(const CompressedPixelStorage storage, const CompressedPixelFormat format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor& size, const Containers::ArrayView data, const ImageFlags flags) noexcept: _storage{storage}, _format{format}, _flags{flags}, _blockSize{Vector3ub{blockSize}}, _blockDataSize{UnsignedByte(blockDataSize)}, _size{size}, _data{reinterpret_cast(data.data()), data.size()} { + #ifndef CORRADE_NO_ASSERT + const bool passed = + Implementation::checkBlockProperties("CompressedImageView:", blockSize, blockDataSize); + #ifdef CORRADE_GRACEFUL_ASSERT + /* If the above check fails on a build with graceful assertions, the data + size check below could then die on division by zero. Exit early in that + case. */ + /** @todo any better idea to handle this? ugh */ + if(!passed) return; + #else + static_cast(passed); + #endif + Implementation::checkBlockPropertiesForStorage("CompressedImageView:", blockSize, blockDataSize, storage); + CORRADE_ASSERT(Implementation::compressedImageDataSize(*this) <= data.size(), "CompressedImageView: data too small, got" << data.size() << "but expected at least" << Implementation::compressedImageDataSize(*this) << "bytes", ); Implementation::checkImageFlagsForSize("CompressedImageView:", flags, size); #endif } -template CompressedImageView::CompressedImageView(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor& size, const ImageFlags flags) noexcept: _storage{storage}, _format{format}, _flags{flags}, _size{size} { +template CompressedImageView::CompressedImageView(const CompressedPixelStorage storage, const UnsignedInt format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor& size, const Containers::ArrayView data, const ImageFlags flags) noexcept: CompressedImageView{storage, compressedPixelFormatWrap(format), blockSize, blockDataSize, size, data, flags} {} + +template CompressedImageView::CompressedImageView(const CompressedPixelStorage storage, const CompressedPixelFormat format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor& size, const ImageFlags flags) noexcept: _storage{storage}, _format{format}, _flags{flags}, _blockSize{Vector3ub{blockSize}}, _blockDataSize{UnsignedByte(blockDataSize)}, _size{size} { #ifndef CORRADE_NO_ASSERT + Implementation::checkBlockProperties("CompressedImageView:", blockSize, blockDataSize); + Implementation::checkBlockPropertiesForStorage("CompressedImageView:", blockSize, blockDataSize, storage); Implementation::checkImageFlagsForSize("CompressedImageView:", flags, size); #endif } -template CompressedImageView::CompressedImageView(const CompressedPixelStorage storage, const UnsignedInt format, const VectorTypeFor& size, const Containers::ArrayView data, const ImageFlags flags) noexcept: CompressedImageView{storage, compressedPixelFormatWrap(format), size, data, flags} {} +template CompressedImageView::CompressedImageView(const CompressedPixelStorage storage, const UnsignedInt format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor& size, const ImageFlags flags) noexcept: CompressedImageView{storage, compressedPixelFormatWrap(format), blockSize, blockDataSize, size, flags} {} -template CompressedImageView::CompressedImageView(const CompressedPixelStorage storage, const UnsignedInt format, const VectorTypeFor& size, const ImageFlags flags) noexcept: CompressedImageView{storage, compressedPixelFormatWrap(format), size, flags} {} +template CompressedImageView::CompressedImageView(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor& size, const ImageFlags flags) noexcept: CompressedImageView{storage, format, (CORRADE_CONSTEXPR_ASSERT(!isCompressedPixelFormatImplementationSpecific(format), "CompressedImageView: can't determine block size of an implementation-specific pixel format" << Debug::hex << compressedPixelFormatUnwrap(format) << Debug::nospace << ", pass it explicitly"), compressedPixelFormatBlockSize(format)), ( + /* Have to do it like this to ensure the above assertion is printed before + the subsequent one from compressedPixelFormatBlockDataSize(), as + compilers are free to reorder these as they want */ + #ifndef CORRADE_NO_ASSERT + isCompressedPixelFormatImplementationSpecific(format) ? 0 : + #endif + compressedPixelFormatBlockDataSize(format) +), size, flags} {} template std::pair, VectorTypeFor> CompressedImageView::dataProperties() const { return Implementation::compressedImageDataProperties(*this); } +template void CompressedImageView::setData(const Containers::ArrayView data) { + CORRADE_ASSERT(Implementation::compressedImageDataSize(*this) <= data.size(), "CompressedImageView::setData(): data too small, got" << data.size() << "but expected at least" << Implementation::compressedImageDataSize(*this) << "bytes", ); + _data = {reinterpret_cast(data.data()), data.size()}; +} + template class MAGNUM_EXPORT ImageView<1, const char>; template class MAGNUM_EXPORT ImageView<2, const char>; template class MAGNUM_EXPORT ImageView<3, const char>; diff --git a/src/Magnum/ImageView.h b/src/Magnum/ImageView.h index 7df1b75a5..8a509152d 100644 --- a/src/Magnum/ImageView.h +++ b/src/Magnum/ImageView.h @@ -730,8 +730,23 @@ template class CompressedImageView { * @param data Image data * @param flags Image layout flags * - * For a 3D image, if @p flags contain @ref ImageFlag3D::CubeMap, the - * @p size is expected to match its restrictions. + * The @p data array is expected to be of proper size for given + * parameters. For a 3D image, if @p flags contain + * @ref ImageFlag3D::CubeMap, the @p size is expected to match its + * restrictions. + * + * The @p format is expected to not be implementation-specific, use the + * @ref CompressedImageView(CompressedPixelStorage, CompressedPixelFormat, const Vector3i&, UnsignedInt, const VectorTypeFor&, Containers::ArrayView, ImageFlags) + * overload to explicitly pass pass an implementation-specific + * @ref CompressedPixelFormat along with its block properties, or the + * @ref CompressedImageView(CompressedPixelStorage, U, const VectorTypeFor&, Containers::ArrayView, ImageFlags) + * overload with the original implementation-specific enum type to have + * the pixel size determined implicitly. + * + * @ref CompressedPixelStorage::compressedBlockSize() and + * @relativeref{CompressedPixelStorage,compressedBlockDataSize()} in + * @p storage are expected to be either both zero or exactly matching + * properties of given @p format. */ explicit CompressedImageView(CompressedPixelStorage storage, CompressedPixelFormat format, const VectorTypeFor& size, Containers::ArrayView data, ImageFlags flags = {}) noexcept; @@ -758,6 +773,19 @@ template class CompressedImageView { * assign a memory view to the image. For a 3D image, if @p flags * contain @ref ImageFlag3D::CubeMap, the @p size is expected to match * its restrictions. + * + * The @p format is expected to not be implementation-specific, use the + * @ref CompressedImageView(CompressedPixelStorage, CompressedPixelFormat, const Vector3i&, UnsignedInt, const VectorTypeFor&, ImageFlags) + * overload to explicitly pass pass an implementation-specific + * @ref CompressedPixelFormat along with its block properties, or the + * @ref CompressedImageView(CompressedPixelStorage, U, const VectorTypeFor&, ImageFlags) + * overload with the original implementation-specific enum type to have + * the pixel size determined implicitly. + * + * @ref CompressedPixelStorage::compressedBlockSize() and + * @relativeref{CompressedPixelStorage,compressedBlockDataSize()} in + * @p storage are expected to be either both zero or exactly matching + * properties of given @p format. */ explicit CompressedImageView(CompressedPixelStorage storage, CompressedPixelFormat format, const VectorTypeFor& size, ImageFlags flags = {}) noexcept; @@ -772,6 +800,94 @@ template class CompressedImageView { */ explicit CompressedImageView(CompressedPixelFormat format, const VectorTypeFor& size, ImageFlags flags = {}) noexcept: CompressedImageView{{}, format, size, flags} {} + /** + * @brief Construct a view with an implementation-specific pixel format + * @param storage Storage of compressed pixel data + * @param format Format of compressed pixel data + * @param blockSize Size of a compressed block in given format, + * in pixels + * @param blockDataSize Size of a compressed block in given format, + * in bytes + * @param size Image size, in pixels + * @param data Image data + * @param flags Image layout flags + * @m_since_latest + * + * Unlike with @ref CompressedImageView(CompressedPixelStorage, CompressedPixelFormat, const VectorTypeFor&, Containers::ArrayView, ImageFlags), + * where block size is determined automatically + * @ref compressedPixelFormatBlockSize() and + * @ref compressedPixelFormatBlockDataSize(), this allows you to + * specify an implementation-specific pixel format and block properties + * directly. Uses @ref compressedPixelFormatWrap() internally to wrap + * @p format in @ref CompressedPixelFormat. The @p blockSize and + * @p blockDataSize is expected to be greater than @cpp 0 @ce and less + * than @cpp 256 @ce. Note that the blocks can be 3D even for 2D images + * and 2D or 3D even for 1D images, in which case only the first slice + * in the extra dimensions is used. + * + * @ref CompressedPixelStorage::compressedBlockSize() and + * @relativeref{CompressedPixelStorage,compressedBlockDataSize()} in + * @p storage are expected to be either both zero or exactly matching + * @p blockSize and @p blockDataSize. + * + * The @p data array is expected to be of proper size for given + * parameters. For a 3D image, if @p flags contain + * @ref ImageFlag3D::CubeMap, the @p size is expected to match its + * restrictions. + */ + explicit CompressedImageView(CompressedPixelStorage storage, UnsignedInt format, const Vector3i& blockSize, UnsignedInt blockDataSize, const VectorTypeFor& size, Containers::ArrayView data, ImageFlags flags = {}) noexcept; + + /** @overload + * + * Equivalent to the above for @p format already wrapped with + * @ref compressedPixelFormatWrap(). + */ + explicit CompressedImageView(CompressedPixelStorage storage, CompressedPixelFormat format, const Vector3i& blockSize, UnsignedInt blockDataSize, const VectorTypeFor& size, Containers::ArrayView data, ImageFlags flags = {}) noexcept; + + /** + * @brief Construct an empty view with an implementation-specific pixel format + * @param storage Storage of compressed pixel data + * @param format Format of compressed pixel data + * @param blockSize Size of a compressed block in given format, + * in pixels + * @param blockDataSize Size of a compressed block in given format, + * in bytes + * @param size Image size, in pixels + * @param flags Image layout flags + * @m_since_latest + * + * Unlike with @ref CompressedImageView(CompressedPixelStorage, CompressedPixelFormat, const VectorTypeFor&, ImageFlags), + * where block size is determined automatically + * @ref compressedPixelFormatBlockSize() and + * @ref compressedPixelFormatBlockDataSize(), this allows you to + * specify an implementation-specific pixel format and block properties + * directly. Uses @ref compressedPixelFormatWrap() internally to wrap + * @p format in @ref CompressedPixelFormat. The @p blockSize and + * @p blockDataSize is expected to be greater than @cpp 0 @ce and less + * than @cpp 256 @ce. Note that the blocks can be 3D even for 2D images + * and 2D or 3D even for 1D images, in that case only the first slice + * in the extra dimensions is used. + * + * @ref CompressedPixelStorage::compressedBlockSize() and + * @relativeref{CompressedPixelStorage,compressedBlockDataSize()} in + * @p storage are expected to be either both zero or exactly matching + * @p blockSize and @p blockDataSize. + * + * Data pointer is set to @cpp nullptr @ce, call @ref setData() to + * assign a memory view to the image. For a 3D image, if @p flags + * contain @ref ImageFlag3D::CubeMap, the @p size is expected to match + * its restrictions. + */ + explicit CompressedImageView(CompressedPixelStorage storage, UnsignedInt format, const Vector3i& blockSize, UnsignedInt blockDataSize, const VectorTypeFor& size, ImageFlags flags = {}) noexcept; + + /** @overload + * @m_since_latest + * + * Equivalent to the above for @p format already wrapped with + * @ref compressedPixelFormatWrap(). + */ + explicit CompressedImageView(CompressedPixelStorage storage, CompressedPixelFormat format, const Vector3i& blockSize, UnsignedInt blockDataSize, const VectorTypeFor& size, ImageFlags flags = {}) noexcept; + /** * @brief Construct an image view with implementation-specific format * @param storage Storage of compressed pixel data @@ -780,11 +896,10 @@ template class CompressedImageView { * @param data Image data * @param flags Image layout flags * - * Uses @ref compressedPixelFormatWrap() internally to convert - * @p format to @ref CompressedPixelFormat. - * - * For a 3D image, if @p flags contain @ref ImageFlag3D::CubeMap, the - * @p size is expected to match its restrictions. + * Uses ADL to find a corresponding @cpp compressedPixelFormatBlockSize(U) @ce + * and @cpp compressedPixelFormatBlockDataSize(U) @ce overloads, then + * calls @ref CompressedImageView(CompressedPixelStorage, UnsignedInt, const Vector3i&, UnsignedInt, const VectorTypeFor&, Containers::ArrayView, ImageFlags) + * with determined block size properties. */ template explicit CompressedImageView(CompressedPixelStorage storage, U format, const VectorTypeFor& size, Containers::ArrayView data, ImageFlags flags = {}) noexcept; @@ -807,13 +922,10 @@ template class CompressedImageView { * @param size Image size * @param flags Image layout flags * - * Uses @ref compressedPixelFormatWrap() internally to convert - * @p format to @ref CompressedPixelFormat. - * - * Data pointer is set to @cpp nullptr @ce, call @ref setData() to - * assign a memory view to the image. For a 3D image, if @p flags - * contain @ref ImageFlag3D::CubeMap, the @p size is expected to match - * its restrictions. + * Uses ADL to find a corresponding @cpp compressedPixelFormatBlockSize(U) @ce + * and @cpp compressedPixelFormatBlockDataSize(U) @ce overloads, then + * calls @ref CompressedImageView(CompressedPixelStorage, UnsignedInt, const Vector3i&, UnsignedInt, const VectorTypeFor&, ImageFlags) + * with determined block size properties. */ template explicit CompressedImageView(CompressedPixelStorage storage, U format, const VectorTypeFor& size, ImageFlags flags = {}) noexcept; @@ -874,6 +986,25 @@ template class CompressedImageView { */ CompressedPixelFormat format() const { return _format; } + /** + * @brief Size of a compressed block in pixels + * @m_since_latest + * + * Note that the blocks can be 3D even for 2D images and 2D or 3D even + * for 1D images, in which case only the first slice in the extra + * dimensions is used. + * @see @ref blockDataSize(), @ref compressedPixelFormatBlockSize() + */ + Vector3i blockSize() const { return Vector3i{_blockSize}; } + + /** + * @brief Size of a compressed block in bytes + * @m_since_latest + * + * @see @ref blockSize(), @ref compressedPixelFormatBlockDataSize() + */ + UnsignedInt blockDataSize() const { return _blockDataSize; } + /** @brief Image size in pixels */ /* Unlike other getters this one is a const& so it's possible to slice to the sizes when all images are in an array, for example for use @@ -897,22 +1028,20 @@ template class CompressedImageView { * The data array is expected to be of proper size for parameters * specified in the constructor. */ - void setData(Containers::ArrayView data) { - _data = {reinterpret_cast(data.data()), data.size()}; - } + void setData(Containers::ArrayView data); private: /* Needed for mutable->const conversion */ template friend class CompressedImageView; - /* To be made public once block size and block data size are stored - together with the image */ - explicit CompressedImageView(CompressedPixelStorage storage, UnsignedInt format, const VectorTypeFor& size, Containers::ArrayView data, ImageFlags flags) noexcept; - explicit CompressedImageView(CompressedPixelStorage storage, UnsignedInt format, const VectorTypeFor& size, ImageFlags flags) noexcept; - CompressedPixelStorage _storage; CompressedPixelFormat _format; ImageFlags _flags; + /* Largest blocks are 12x12 in ASTC and at most 32 bytes, so an 8-bit + type should be more than enough. As even 1D images can have 3D + blocks, the member isn't dependent on dimension count. */ + Vector3ub _blockSize; + UnsignedByte _blockDataSize; VectorTypeFor _size; Containers::ArrayView _data; }; @@ -1024,12 +1153,12 @@ template template template::value && !std::is_const::value, int>::type> ImageView::ImageView(const ImageView& other) noexcept: _storage{other._storage}, _format{other._format}, _formatExtra{other._formatExtra}, _pixelSize{other._pixelSize}, _flags{other._flags}, _size{other._size}, _data{other._data} {} #endif -template template inline CompressedImageView::CompressedImageView(const CompressedPixelStorage storage, const U format, const VectorTypeFor& size, const Containers::ArrayView data, const ImageFlags flags) noexcept: CompressedImageView{storage, UnsignedInt(format), size, data, flags} { +template template inline CompressedImageView::CompressedImageView(const CompressedPixelStorage storage, const U format, const VectorTypeFor& size, const Containers::ArrayView data, const ImageFlags flags) noexcept: CompressedImageView{storage, UnsignedInt(format), compressedPixelFormatBlockSize(format), compressedPixelFormatBlockDataSize(format), size, data, flags} { static_assert(sizeof(U) <= 4, "format types larger than 32bits are not supported"); } -template template inline CompressedImageView::CompressedImageView(const CompressedPixelStorage storage, const U format, const VectorTypeFor& size, const ImageFlags flags) noexcept: CompressedImageView{storage, UnsignedInt(format), size, flags} { +template template inline CompressedImageView::CompressedImageView(const CompressedPixelStorage storage, const U format, const VectorTypeFor& size, const ImageFlags flags) noexcept: CompressedImageView{storage, UnsignedInt(format), compressedPixelFormatBlockSize(format), compressedPixelFormatBlockDataSize(format), size, flags} { static_assert(sizeof(U) <= 4, "format types larger than 32bits are not supported"); } @@ -1040,10 +1169,12 @@ template template(UnsignedShort(other._flags)&~UnsignedShort(ImageFlag2D::Array))|flags}, + _blockSize{other._blockSize}, + _blockDataSize{other._blockDataSize}, _size{Math::Vector::pad(other._size, 1)}, _data{other._data} {} -template template::value && !std::is_const::value, int>::type> CompressedImageView::CompressedImageView(const CompressedImageView& other) noexcept: _storage{other._storage}, _format{other._format}, _flags{other._flags}, _size{other._size}, _data{other._data} {} +template template::value && !std::is_const::value, int>::type> CompressedImageView::CompressedImageView(const CompressedImageView& other) noexcept: _storage{other._storage}, _format{other._format}, _flags{other._flags}, _blockSize{other._blockSize}, _blockDataSize{other._blockDataSize}, _size{other._size}, _data{other._data} {} #endif } diff --git a/src/Magnum/Implementation/ImageProperties.h b/src/Magnum/Implementation/ImageProperties.h index 3a1fdc26c..0a3755bf3 100644 --- a/src/Magnum/Implementation/ImageProperties.h +++ b/src/Magnum/Implementation/ImageProperties.h @@ -49,6 +49,33 @@ inline void checkPixelSize(const char* prefix << "expected pixel size to be non-zero and less than 256 but got" << pixelSize, ); } +inline bool checkBlockProperties(const char* + #ifndef CORRADE_STANDARD_ASSERT + const prefix + #endif + , const Vector3i& blockSize, const UnsignedInt blockDataSize) +{ + CORRADE_ASSERT((blockSize > Vector3i{}).all() && + (blockSize < Vector3i{256}).all(), + prefix << "expected block size to be greater than zero and less than 256 but got" << Debug::packed << blockSize, {}); + CORRADE_ASSERT(blockDataSize && blockDataSize < 256, + prefix << "expected block data size to be non-zero and less than 256 but got" << blockDataSize, {}); + return true; +} +/* GL::BufferImage has block size statically defined for all known formats so + it doesn't need the above, only this */ +inline void checkBlockPropertiesForStorage(const char* + #ifndef CORRADE_STANDARD_ASSERT + const prefix + #endif + , const Vector3i& blockSize, const UnsignedInt blockDataSize, const CompressedPixelStorage& storage) +{ + CORRADE_ASSERT(storage.compressedBlockSize() == Vector3i{} || storage.compressedBlockSize() == blockSize, + prefix << "expected pixel storage block size to be either not set at all or equal to" << Debug::packed << blockSize << "but got" << Debug::packed << storage.compressedBlockSize(), ); + CORRADE_ASSERT(!storage.compressedBlockDataSize() || UnsignedInt(storage.compressedBlockDataSize()) == blockDataSize, + prefix << "expected pixel storage block data size to be either not set at all or equal to" << blockDataSize << "but got" << storage.compressedBlockDataSize(), ); +} + inline void checkImageFlagsForSize(const char*, const ImageFlags1D, const Math::Vector<1, Int>&) {} inline void checkImageFlagsForSize(const char*, const ImageFlags2D, const Vector2i&) {} inline void checkImageFlagsForSize(const char* @@ -73,7 +100,8 @@ template std::pair, Math::Vector3> compressedDataProperties(const CompressedPixelStorage& storage, const Vector3i& blockSize, const UnsignedInt blockDataSize, const Vector3i& size) { const Vector3i blockCount = (size + blockSize - Vector3i{1})/blockSize; const Math::Vector3 dataSize{ @@ -87,9 +115,17 @@ inline std::pair, Math::Vector3> compres return std::make_pair(offset, size.product() ? dataSize : Math::Vector3{}); } +template struct CompressedImageTraits; +template struct CompressedImageTraits().storage()), CompressedPixelStorage>::value>::type> { + static CompressedPixelStorage storage(const T& image) { return image.storage(); } +}; +template struct CompressedImageTraits().storage()), PixelStorage>::value>::type> { + static CompressedPixelStorage storage(const T& image) { return image.compressedStorage(); } +}; + /* Used in Compressed*Image::dataProperties() */ template std::pair, Math::Vector> compressedImageDataProperties(const T& image) { - std::pair, Math::Vector3> dataProperties = image.storage().dataProperties(Vector3i::pad(image.size(), 1)); + std::pair, Math::Vector3> dataProperties = compressedDataProperties(CompressedImageTraits::storage(image), image.blockSize(), image.blockDataSize(), Vector3i::pad(image.size(), 1)); return std::make_pair(Math::Vector::pad(dataProperties.first), Math::Vector::pad(dataProperties.second)); } @@ -126,10 +162,8 @@ template inline std::size_t imageDataSize(const T& image) { where the nv-cubemap-broken-full-compressed-image-query workaround needs to go slice by slice, taking offset and incrementing it by size divided by the Z dimension. */ -template std::pair compressedImageDataOffsetSizeFor(const T& image, const Math::Vector& size) { - CORRADE_INTERNAL_ASSERT(image.storage().compressedBlockSize().product() && image.storage().compressedBlockDataSize()); - - std::pair, Math::Vector3> dataProperties = image.storage().dataProperties(Vector3i::pad(size, 1)); +template std::pair compressedImageDataOffsetSizeFor(const CompressedPixelStorage& storage, const Vector3i& blockSize, const UnsignedInt blockDataSize, const Math::Vector& size) { + std::pair, Math::Vector3> dataProperties = compressedDataProperties(storage, blockSize, blockDataSize, Vector3i::pad(size, 1)); /* Smallest line/rectangle/cube that covers the area. Same logic as in imageDataSizeFor() above. */ @@ -137,13 +171,17 @@ template std::pair co if(dataProperties.first.z()) dataOffset += dataProperties.first.z(); else if(dataProperties.first.y()) { - if(!image.storage().imageHeight()) + if(!storage.imageHeight()) dataOffset += dataProperties.first.y(); } else if(dataProperties.first.x()) { - if(!image.storage().rowLength()) + if(!storage.rowLength()) dataOffset += dataProperties.first.x(); } - return {dataOffset, dataProperties.second.product()*image.storage().compressedBlockDataSize()}; + return {dataOffset, dataProperties.second.product()*blockDataSize}; +} + +template std::pair compressedImageDataOffsetSizeFor(const T& image, const Math::Vector& size) { + return compressedImageDataOffsetSizeFor(CompressedImageTraits::storage(image), image.blockSize(), image.blockDataSize(), size); } /* Used in image query functions */ @@ -152,6 +190,12 @@ template std::size_t compressedImageDataSizeFor return r.first + r.second; } +/* Used in data size assertions */ +template inline std::size_t compressedImageDataSize(const T& image) { + auto r = compressedImageDataOffsetSizeFor(image, image.size()); + return r.first + r.second; +} + template std::ptrdiff_t pixelStorageSkipOffsetFor(const T& image, const Math::Vector& size) { return image.storage().dataProperties(image.pixelSize(), Vector3i::pad(size, 1)).first.sum(); } diff --git a/src/Magnum/PixelStorage.h b/src/Magnum/PixelStorage.h index 1ede8fde4..fe9b36b7d 100644 --- a/src/Magnum/PixelStorage.h +++ b/src/Magnum/PixelStorage.h @@ -177,31 +177,40 @@ class MAGNUM_EXPORT CompressedPixelStorage: public PixelStorage { return !operator==(other); } - /** @brief Compressed block size */ + /** @brief Compressed block size in pixels */ constexpr Vector3i compressedBlockSize() const { return _blockSize; } /** - * @brief Set compressed block size + * @brief Set compressed block size in pixels * - * 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::compressedPixelFormatBlockSize() + * Expected to either match size information for a particular + * compressed format of image the storage is used with or be set to + * @cpp 0 @ce. Default is @cpp 0 @ce in all dimensions. + * @see @ref compressedPixelFormatBlockSize(), + * @ref CompressedImageView::blockSize(), + * @ref CompressedImage::blockSize(), + * @ref Trade::ImageData::compressedBlockSize(), + * @ref GL::CompressedBufferImage::blockSize(), */ CompressedPixelStorage& setCompressedBlockSize(const Vector3i& size) { _blockSize = size; return *this; } - /** @brief Compressed block data size (in bytes) */ + /** @brief Compressed block data size in bytes */ constexpr Int compressedBlockDataSize() const { return _blockDataSize; } /** - * @brief Set compressed block data size (in bytes) + * @brief Set compressed block data size in bytes * - * 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::compressedPixelFormatBlockDataSize() + * Expected to either match size information for a particular + * compressed format of image the storage is used with or be set to + * @cpp 0 @ce. Default is @cpp 0 @ce. + * @see @ref compressedPixelFormatBlockDataSize(), + * @ref CompressedImageView::blockDataSize(), + * @ref CompressedImage::blockDataSize(), + * @ref Trade::ImageData::compressedBlockDataSize(), + * @ref GL::CompressedBufferImage::blockSize(), */ CompressedPixelStorage& setCompressedBlockDataSize(Int size) { _blockDataSize = size; diff --git a/src/Magnum/Test/ImageTest.cpp b/src/Magnum/Test/ImageTest.cpp index 0784b5dd0..9544bb620 100644 --- a/src/Magnum/Test/ImageTest.cpp +++ b/src/Magnum/Test/ImageTest.cpp @@ -56,6 +56,8 @@ struct ImageTest: TestSuite::Tester { void constructInvalidPixelSize(); void constructInvalidSize(); void constructInvalidCubeMap(); + void constructCompressedUnknownImplementationSpecificBlockSize(); + void constructCompressedInvalidBlockSize(); void constructCompressedInvalidSize(); void constructCompressedInvalidCubeMap(); @@ -115,6 +117,8 @@ ImageTest::ImageTest() { &ImageTest::constructInvalidPixelSize, &ImageTest::constructInvalidSize, &ImageTest::constructInvalidCubeMap, + &ImageTest::constructCompressedUnknownImplementationSpecificBlockSize, + &ImageTest::constructCompressedInvalidBlockSize, &ImageTest::constructCompressedInvalidSize, &ImageTest::constructCompressedInvalidCubeMap, @@ -168,6 +172,20 @@ namespace GL { } enum class CompressedPixelFormat { RGBS3tcDxt1 = 21 }; + Vector3i compressedPixelFormatBlockSize(CompressedPixelFormat format) { + #ifdef CORRADE_NO_ASSERT + static_cast(format); + #endif + CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::RGBS3tcDxt1); + return {4, 4, 1}; + } + UnsignedInt compressedPixelFormatBlockDataSize(CompressedPixelFormat format) { + #ifdef CORRADE_NO_ASSERT + static_cast(format); + #endif + CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::RGBS3tcDxt1); + return 8; + } } namespace Vk { @@ -182,6 +200,22 @@ namespace Vk { #endif return 12; } + + enum class CompressedPixelFormat { Astc5x5x4RGBAF = 111 }; + Vector3i compressedPixelFormatBlockSize(CompressedPixelFormat format) { + #ifdef CORRADE_NO_ASSERT + static_cast(format); + #endif + CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::Astc5x5x4RGBAF); + return {5, 5, 4}; + } + UnsignedInt compressedPixelFormatBlockDataSize(CompressedPixelFormat format) { + #ifdef CORRADE_NO_ASSERT + static_cast(format); + #endif + CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::Astc5x5x4RGBAF); + return 16; + } } void ImageTest::constructGeneric() { @@ -394,28 +428,34 @@ void ImageTest::constructImplementationSpecificPlaceholder() { void ImageTest::constructCompressedGeneric() { { - auto data = new char[8]; - CompressedImage2D a{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, - Containers::Array{data, 8}, ImageFlag2D::Array}; + auto data = new char[6*8]; + CompressedImage2D a{ + CompressedPixelFormat::Bc1RGBAUnorm, {12, 8}, + Containers::Array{data, 6*8}, ImageFlag2D::Array}; CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{0}); + CORRADE_COMPARE(a.storage().rowLength(), 0); CORRADE_COMPARE(a.format(), CompressedPixelFormat::Bc1RGBAUnorm); - CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(a.blockDataSize(), 8); + CORRADE_COMPARE(a.size(), (Vector2i{12, 8})); CORRADE_COMPARE(a.data(), static_cast(data)); - CORRADE_COMPARE(a.data().size(), 8); + CORRADE_COMPARE(a.data().size(), 6*8); } { - auto data = new char[8]; - CompressedImage2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, - Containers::Array{data, 8}, ImageFlag2D::Array}; + auto data = new char[8*16]; + CompressedImage2D a{ + CompressedPixelStorage{}.setRowLength(20), + CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, + Containers::Array{data, 8*16}, ImageFlag2D::Array}; CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(a.format(), CompressedPixelFormat::Bc1RGBAUnorm); - CORRADE_COMPARE(a.size(), Vector2i(4, 4)); + CORRADE_COMPARE(a.storage().rowLength(), 20); + CORRADE_COMPARE(a.format(), CompressedPixelFormat::Astc5x5x4RGBAF); + CORRADE_COMPARE(a.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(a.blockDataSize(), 16); + CORRADE_COMPARE(a.size(), (Vector2i{15, 10})); CORRADE_COMPARE(a.data(), static_cast(data)); - CORRADE_COMPARE(a.data().size(), 8); + CORRADE_COMPARE(a.data().size(), 8*16); } } @@ -424,8 +464,10 @@ void ImageTest::constructCompressedGenericPlaceholder() { CompressedImage2D a; CORRADE_COMPARE(a.flags(), ImageFlags2D{}); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{0}); + CORRADE_COMPARE(a.storage().rowLength(), 0); CORRADE_COMPARE(a.format(), CompressedPixelFormat{}); + CORRADE_COMPARE(a.blockSize(), (Vector3i{})); + CORRADE_COMPARE(a.blockDataSize(), 0); CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(a.data(), static_cast(nullptr)); } { @@ -433,12 +475,14 @@ void ImageTest::constructCompressedGenericPlaceholder() { CompressedPixelStorage{} /* Even with skip it shouldn't assert on data size */ .setSkip({1, 0, 0}) - .setCompressedBlockSize(Vector3i{4})}; + .setRowLength(20)}; CORRADE_COMPARE(a.flags(), ImageFlags2D{}); CORRADE_COMPARE(a.storage().skip(), (Vector3i{1, 0, 0})); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); + CORRADE_COMPARE(a.storage().rowLength(), 20); CORRADE_COMPARE(a.format(), CompressedPixelFormat{}); + CORRADE_COMPARE(a.blockSize(), (Vector3i{})); + CORRADE_COMPARE(a.blockDataSize(), 0); CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(a.data(), static_cast(nullptr)); } @@ -447,31 +491,54 @@ void ImageTest::constructCompressedGenericPlaceholder() { void ImageTest::constructCompressedImplementationSpecific() { /* Format with autodetection */ { - auto data = new char[8]; - CompressedImage2D a{GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, - Containers::Array{data, 8}, ImageFlag2D::Array}; + auto data = new char[6*8]; + CompressedImage2D a{ + GL::CompressedPixelFormat::RGBS3tcDxt1, {12, 8}, + Containers::Array{data, 6*8}, ImageFlag2D::Array}; CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{0}); + CORRADE_COMPARE(a.storage().rowLength(), 0); CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(a.blockDataSize(), 8); + CORRADE_COMPARE(a.size(), (Vector2i{12, 8})); CORRADE_COMPARE(a.data(), static_cast(data)); - CORRADE_COMPARE(a.data().size(), 8); + CORRADE_COMPARE(a.data().size(), 6*8); } { - auto data = new char[8]; - CompressedImage2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, - Containers::Array{data, 8}, ImageFlag2D::Array}; + auto data = new char[8*16]; + CompressedImage2D a{ + CompressedPixelStorage{}.setRowLength(20), + Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, + Containers::Array{data, 8*16}, ImageFlag2D::Array}; CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(a.storage().rowLength(), 20); + CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + CORRADE_COMPARE(a.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(a.blockDataSize(), 16); + CORRADE_COMPARE(a.size(), (Vector2i{15, 10})); CORRADE_COMPARE(a.data(), static_cast(data)); - CORRADE_COMPARE(a.data().size(), 8); + CORRADE_COMPARE(a.data().size(), 8*16); } - /* Manual properties not implemented yet */ + /* Manual block properties */ + { + auto data = new char[6*12]; + CompressedImage2D a{ + CompressedPixelStorage{}.setRowLength(6), + 111, {3, 4, 5}, 12, {3, 8}, Containers::Array{data, 6*12}, ImageFlag2D::Array}; + + CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); + CORRADE_COMPARE(a.storage().rowLength(), 6); + CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + /* These deliberately don't match what compressedPixelFormatBlockSize() + above returns */ + CORRADE_COMPARE(a.blockSize(), (Vector3i{3, 4, 5})); + CORRADE_COMPARE(a.blockDataSize(), 12); + CORRADE_COMPARE(a.size(), (Vector2i{3, 8})); + CORRADE_COMPARE(a.data(), static_cast(data)); + CORRADE_COMPARE(a.data().size(), 6*12); + } } void ImageTest::constructUnknownImplementationSpecificPixelSize() { @@ -539,10 +606,62 @@ void ImageTest::constructInvalidCubeMap() { "Image: expected square faces for a cube map, got {4, 3}\n"); } -void ImageTest::constructCompressedInvalidSize() { +void ImageTest::constructCompressedUnknownImplementationSpecificBlockSize() { CORRADE_SKIP_IF_NO_ASSERT(); - CORRADE_EXPECT_FAIL("Size checking for compressed image data is not implemented yet."); + Containers::String out; + Error redirectError{&out}; + CompressedImage2D{compressedPixelFormatWrap(0x666), {1, 1}, Containers::Array{NoInit, 1}}; + CORRADE_COMPARE_AS(out, + "CompressedImage: can't determine block size of an implementation-specific pixel format 0x666, pass it explicitly\n" + /* The next messages are printed because it cannot exit the + construction from the middle of the member initializer list. With + non-graceful asserts just one message is printed tho. */ + "compressedPixelFormatBlockSize(): can't determine size of an implementation-specific format 0x666\n" + "CompressedImage: expected block size to be greater than zero and less than 256 but got {0, 0, 0}\n", + TestSuite::Compare::String); +} + +void ImageTest::constructCompressedInvalidBlockSize() { + CORRADE_SKIP_IF_NO_ASSERT(); + + /* This is all okay */ + CompressedImage2D{CompressedPixelStorage{}, 666, {4, 5, 6}, 8, {1, 1}, Containers::Array{NoInit, 8}}; + CompressedImage2D{CompressedPixelStorage{} + .setCompressedBlockSize({4, 5, 6}) + .setCompressedBlockDataSize(8), + 666, {4, 5, 6}, 8, {1, 1}, Containers::Array{NoInit, 8}}; + + /* Tested mainly in ImageViewTest, here is just a subset to verify the same + helper is used internally and a proper prefix is printed */ + Containers::String out; + Error redirectError{&out}; + CompressedImage2D{CompressedPixelStorage{}, 666, {0, 0, 0}, 4, {1, 1}, nullptr}; + CompressedImage2D{CompressedPixelStorage{}, 666, {4, 4, 4}, 0, {1, 1}, nullptr}; + CompressedImage2D{CompressedPixelStorage{} + .setCompressedBlockSize({5, 5, 5}) + .setCompressedBlockDataSize(8), + 666, {4, 4, 1}, 8, {1, 1}, Containers::Array{NoInit, 8}}; + CompressedImage2D{CompressedPixelStorage{} + .setCompressedBlockSize({4, 4, 1}) + .setCompressedBlockDataSize(4), + 666, {4, 4, 1}, 8, {1, 1}, Containers::Array{NoInit, 8}}; + CompressedImage2D{CompressedPixelStorage{} + .setCompressedBlockSize({0, 1, 0})}; + CompressedImage2D{CompressedPixelStorage{} + .setCompressedBlockDataSize(1)}; + CORRADE_COMPARE_AS(out, + "CompressedImage: expected block size to be greater than zero and less than 256 but got {0, 0, 0}\n" + "CompressedImage: expected block data size to be non-zero and less than 256 but got 0\n" + "CompressedImage: expected pixel storage block size to be either not set at all or equal to {4, 4, 1} but got {5, 5, 5}\n" + "CompressedImage: expected pixel storage block data size to be either not set at all or equal to 8 but got 4\n" + "CompressedImage: expected pixel storage block size to not be set at all but got {0, 1, 0}\n" + "CompressedImage: expected pixel storage block data size to not be set at all but got 1\n", + TestSuite::Compare::String); +} + +void ImageTest::constructCompressedInvalidSize() { + CORRADE_SKIP_IF_NO_ASSERT(); /* Too small for given format */ { @@ -661,70 +780,80 @@ void ImageTest::constructMoveImplementationSpecific() { } void ImageTest::constructMoveCompressedGeneric() { - auto data = new char[8]; + auto data = new char[8*16]; CompressedImage2D a{ - CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - CompressedPixelFormat::Bc3RGBAUnorm, {4, 4}, Containers::Array{data, 8}, ImageFlag2D::Array}; + CompressedPixelStorage{}.setRowLength(20), + CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, + Containers::Array{data, 8*16}, ImageFlag2D::Array}; CompressedImage2D b{Utility::move(a)}; CORRADE_COMPARE(a.data(), static_cast(nullptr)); CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(b.format(), CompressedPixelFormat::Bc3RGBAUnorm); - CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(b.storage().rowLength(), 20); + CORRADE_COMPARE(b.format(), CompressedPixelFormat::Astc5x5x4RGBAF); + CORRADE_COMPARE(b.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(b.blockDataSize(), 16); + CORRADE_COMPARE(b.size(), (Vector2i{15, 10})); CORRADE_COMPARE(b.data(), static_cast(data)); - CORRADE_COMPARE(b.data().size(), 8); + CORRADE_COMPARE(b.data().size(), 8*16); - auto data2 = new char[16]; - CompressedImage2D c{CompressedPixelFormat::Bc1RGBAUnorm, {8, 4}, Containers::Array{data2, 16}}; + auto data2 = new char[2*8]; + CompressedImage2D c{CompressedPixelFormat::Bc1RGBAUnorm, {8, 4}, Containers::Array{data2, 2*8}}; c = Utility::move(b); CORRADE_COMPARE(b.data(), static_cast(data2)); CORRADE_COMPARE(b.size(), (Vector2i{8, 4})); CORRADE_COMPARE(c.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(c.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(c.format(), CompressedPixelFormat::Bc3RGBAUnorm); - CORRADE_COMPARE(c.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(c.storage().rowLength(), 20); + CORRADE_COMPARE(c.format(), CompressedPixelFormat::Astc5x5x4RGBAF); + CORRADE_COMPARE(c.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(c.blockDataSize(), 16); + CORRADE_COMPARE(c.size(), (Vector2i{15, 10})); CORRADE_COMPARE(c.data(), static_cast(data)); - CORRADE_COMPARE(c.data().size(), 8); + CORRADE_COMPARE(c.data().size(), 8*16); CORRADE_VERIFY(std::is_nothrow_move_constructible::value); CORRADE_VERIFY(std::is_nothrow_move_assignable::value); } void ImageTest::constructMoveCompressedImplementationSpecific() { - auto data = new char[8]; + auto data = new char[8*16]; CompressedImage2D a{ - CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, Containers::Array{data, 8}, ImageFlag2D::Array}; + CompressedPixelStorage{}.setRowLength(20), + Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, + Containers::Array{data, 8*16}, ImageFlag2D::Array}; CompressedImage2D b{Utility::move(a)}; CORRADE_COMPARE(a.data(), static_cast(nullptr)); CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(b.storage().rowLength(), 20); + CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + CORRADE_COMPARE(b.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(b.blockDataSize(), 16); + CORRADE_COMPARE(b.size(), (Vector2i{15, 10})); CORRADE_COMPARE(b.data(), static_cast(data)); - CORRADE_COMPARE(b.data().size(), 8); + CORRADE_COMPARE(b.data().size(), 8*16); - auto data2 = new char[16]; - CompressedImage2D c{CompressedPixelFormat::Bc2RGBAUnorm, {8, 4}, Containers::Array{data2, 16}}; + auto data2 = new char[2*8]; + CompressedImage2D c{CompressedPixelFormat::Bc1RGBAUnorm, {8, 4}, Containers::Array{data2, 2*8}}; c = Utility::move(b); CORRADE_COMPARE(b.data(), static_cast(data2)); CORRADE_COMPARE(b.size(), (Vector2i{8, 4})); CORRADE_COMPARE(c.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(c.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(c.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(c.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(c.storage().rowLength(), 20); + CORRADE_COMPARE(c.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + CORRADE_COMPARE(c.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(c.blockDataSize(), 16); + CORRADE_COMPARE(c.size(), (Vector2i{15, 10})); CORRADE_COMPARE(c.data(), static_cast(data)); - CORRADE_COMPARE(c.data().size(), 8); + CORRADE_COMPARE(c.data().size(), 8*16); } template void ImageTest::toViewGeneric() { @@ -764,35 +893,41 @@ template void ImageTest::toViewImplementationSpecific() { template void ImageTest::toViewCompressedGeneric() { setTestCaseTemplateName(MutabilityTraits::name()); - auto data = new char[8]; + auto data = new char[8*16]; typename MutabilityTraits::CompressedImageType a{ - CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - CompressedPixelFormat::Bc1RGBUnorm, {4, 4}, Containers::Array{data, 8}, ImageFlag2D::Array}; + CompressedPixelStorage{}.setRowLength(20), + CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, + Containers::Array{data, 8*16}, ImageFlag2D::Array}; CompressedImageView<2, T> b = a; CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(b.format(), CompressedPixelFormat::Bc1RGBUnorm); - CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(b.storage().rowLength(), 20); + CORRADE_COMPARE(b.format(), CompressedPixelFormat::Astc5x5x4RGBAF); + CORRADE_COMPARE(b.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(b.blockDataSize(), 16); + CORRADE_COMPARE(b.size(), (Vector2i{15, 10})); CORRADE_COMPARE(b.data(), static_cast(data)); - CORRADE_COMPARE(b.data().size(), 8); + CORRADE_COMPARE(b.data().size(), 8*16); } template void ImageTest::toViewCompressedImplementationSpecific() { setTestCaseTemplateName(MutabilityTraits::name()); - auto data = new char[8]; + auto data = new char[8*16]; typename MutabilityTraits::CompressedImageType a{ - CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, Containers::Array{data, 8}, ImageFlag2D::Array}; + CompressedPixelStorage{}.setRowLength(20), + Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, + Containers::Array{data, 8*16}, ImageFlag2D::Array}; CompressedImageView<2, T> b = a; CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(b.storage().rowLength(), 20); + CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + CORRADE_COMPARE(b.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(b.blockDataSize(), 16); + CORRADE_COMPARE(b.size(), (Vector2i{15, 10})); CORRADE_COMPARE(b.data(), static_cast(data)); - CORRADE_COMPARE(b.data().size(), 8); + CORRADE_COMPARE(b.data().size(), 8*16); } void ImageTest::data() { @@ -804,9 +939,10 @@ void ImageTest::data() { } void ImageTest::dataCompressed() { - auto data = new char[8]; - CompressedImage2D a{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, - Containers::Array{data, 8}}; + auto data = new char[6*8]; + CompressedImage2D a{ + CompressedPixelFormat::Bc1RGBAUnorm, {12, 8}, + Containers::Array{data, 6*8}, ImageFlag2D::Array}; const CompressedImage2D& ca = a; CORRADE_COMPARE(a.data(), static_cast(data)); CORRADE_COMPARE(ca.data(), static_cast(data)); @@ -820,10 +956,10 @@ void ImageTest::dataRvalue() { } void ImageTest::dataRvalueCompressed() { - auto data = new char[8]; + auto data = new char[6*8]; Containers::Array released = CompressedImage2D{ - CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, - Containers::Array{data, 8}}.data(); + CompressedPixelFormat::Bc1RGBAUnorm, {12, 8}, + Containers::Array{data, 6*8}, ImageFlag2D::Array}.data(); CORRADE_COMPARE(released.data(), static_cast(data)); } @@ -839,17 +975,15 @@ void ImageTest::dataProperties() { } void ImageTest::dataPropertiesCompressed() { - /* Yes, I know, this is totally bogus and doesn't match the BC1 format */ CompressedImage3D image{ CompressedPixelStorage{} - .setCompressedBlockSize({3, 4, 5}) - .setCompressedBlockDataSize(16) - .setImageHeight(12) - .setSkip({5, 8, 11}), - CompressedPixelFormat::Bc1RGBAUnorm, {2, 8, 11}, - Containers::Array{1}}; + .setRowLength(12) + .setImageHeight(8) + .setSkip({8, 4, 4}), + CompressedPixelFormat::Bc1RGBAUnorm, {2, 3, 3}, + Containers::Array{NoInit, 336}}; CORRADE_COMPARE(image.dataProperties(), - (std::pair, Math::Vector3>{{2*16, 2*16, 9*16}, {1, 3, 3}})); + (std::pair, Math::Vector3>{{16, 24, 192}, {3, 2, 3}})); } void ImageTest::release() { @@ -863,8 +997,10 @@ void ImageTest::release() { } void ImageTest::releaseCompressed() { - char data[8]; - CompressedImage2D a{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, Containers::Array{data, 8}}; + char data[6*8]; + CompressedImage2D a{ + CompressedPixelFormat::Bc1RGBAUnorm, {12, 8}, + Containers::Array{data, 6*8}, ImageFlag2D::Array}; const char* const pointer = a.release().release(); CORRADE_COMPARE(pointer, data); diff --git a/src/Magnum/Test/ImageViewTest.cpp b/src/Magnum/Test/ImageViewTest.cpp index a20d5f059..45b90128e 100644 --- a/src/Magnum/Test/ImageViewTest.cpp +++ b/src/Magnum/Test/ImageViewTest.cpp @@ -64,6 +64,8 @@ struct ImageViewTest: TestSuite::Tester { void constructInvalidPixelSize(); void constructInvalidSize(); void constructInvalidCubeMap(); + void constructCompressedUnknownImplementationSpecificBlockSize(); + void constructCompressedInvalidBlockSize(); void constructCompressedInvalidSize(); void constructCompressedInvalidCubeMap(); @@ -120,6 +122,8 @@ ImageViewTest::ImageViewTest() { &ImageViewTest::constructInvalidPixelSize, &ImageViewTest::constructInvalidSize, &ImageViewTest::constructInvalidCubeMap, + &ImageViewTest::constructCompressedUnknownImplementationSpecificBlockSize, + &ImageViewTest::constructCompressedInvalidBlockSize, &ImageViewTest::constructCompressedInvalidSize, &ImageViewTest::constructCompressedInvalidCubeMap, @@ -160,6 +164,20 @@ namespace GL { } enum class CompressedPixelFormat { RGBS3tcDxt1 = 21 }; + Vector3i compressedPixelFormatBlockSize(CompressedPixelFormat format) { + #ifdef CORRADE_NO_ASSERT + static_cast(format); + #endif + CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::RGBS3tcDxt1); + return {4, 4, 1}; + } + UnsignedInt compressedPixelFormatBlockDataSize(CompressedPixelFormat format) { + #ifdef CORRADE_NO_ASSERT + static_cast(format); + #endif + CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::RGBS3tcDxt1); + return 8; + } } namespace Vk { @@ -174,6 +192,22 @@ namespace Vk { #endif return 12; } + + enum class CompressedPixelFormat { Astc5x5x4RGBAF = 111 }; + Vector3i compressedPixelFormatBlockSize(CompressedPixelFormat format) { + #ifdef CORRADE_NO_ASSERT + static_cast(format); + #endif + CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::Astc5x5x4RGBAF); + return {5, 5, 4}; + } + UnsignedInt compressedPixelFormatBlockDataSize(CompressedPixelFormat format) { + #ifdef CORRADE_NO_ASSERT + static_cast(format); + #endif + CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::Astc5x5x4RGBAF); + return 16; + } } template void ImageViewTest::constructGeneric() { @@ -376,27 +410,34 @@ template void ImageViewTest::constructCompressedGeneric() { setTestCaseTemplateName(MutabilityTraits::name()); { - T data[8]{}; - CompressedImageView<2, T> a{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, data, ImageFlag2D::Array}; + T data[6*8]{}; + CompressedImageView<2, T> a{ + CompressedPixelFormat::Bc1RGBAUnorm, {12, 8}, + data, ImageFlag2D::Array}; CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{0}); + CORRADE_COMPARE(a.storage().rowLength(), 0); CORRADE_COMPARE(a.format(), CompressedPixelFormat::Bc1RGBAUnorm); - CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(a.blockDataSize(), 8); + CORRADE_COMPARE(a.size(), (Vector2i{12, 8})); CORRADE_COMPARE(a.data(), static_cast(data)); - CORRADE_COMPARE(a.data().size(), 8); + CORRADE_COMPARE(a.data().size(), 6*8); } { - T data[8]{}; - CompressedImageView<2, T> a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, + T data[8*16]{}; + CompressedImageView<2, T> a{ + CompressedPixelStorage{}.setRowLength(20), + CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, data, ImageFlag2D::Array}; CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(a.format(), CompressedPixelFormat::Bc1RGBAUnorm); - CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(a.storage().rowLength(), 20); + CORRADE_COMPARE(a.format(), CompressedPixelFormat::Astc5x5x4RGBAF); + CORRADE_COMPARE(a.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(a.blockDataSize(), 16); + CORRADE_COMPARE(a.size(), (Vector2i{15, 10})); CORRADE_COMPARE(a.data(), static_cast(data)); - CORRADE_COMPARE(a.data().size(), 8); + CORRADE_COMPARE(a.data().size(), 8*16); } } @@ -404,21 +445,27 @@ template void ImageViewTest::constructCompressedGenericEmpty() { setTestCaseTemplateName(MutabilityTraits::name()); { - CompressedImageView<2, T> a{CompressedPixelFormat::Bc1RGBAUnorm, {8, 16}, ImageFlag2D::Array}; + CompressedImageView<2, T> a{CompressedPixelFormat::Bc1RGBAUnorm, {12, 8}, ImageFlag2D::Array}; CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{0}); + CORRADE_COMPARE(a.storage().rowLength(), 0); CORRADE_COMPARE(a.format(), CompressedPixelFormat::Bc1RGBAUnorm); - CORRADE_COMPARE(a.size(), (Vector2i{8, 16})); + CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(a.blockDataSize(), 8); + CORRADE_COMPARE(a.size(), (Vector2i{12, 8})); CORRADE_COMPARE(a.data(), static_cast(nullptr)); } { - CompressedImageView<2, T> a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - CompressedPixelFormat::Bc1RGBAUnorm, {8, 16}, ImageFlag2D::Array}; + CompressedImageView<2, T> a{ + CompressedPixelStorage{}.setRowLength(20), + CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, + ImageFlag2D::Array}; CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(a.format(), CompressedPixelFormat::Bc1RGBAUnorm); - CORRADE_COMPARE(a.size(), (Vector2i{8, 16})); + CORRADE_COMPARE(a.storage().rowLength(), 20); + CORRADE_COMPARE(a.format(), CompressedPixelFormat::Astc5x5x4RGBAF); + CORRADE_COMPARE(a.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(a.blockDataSize(), 16); + CORRADE_COMPARE(a.size(), (Vector2i{15, 10})); CORRADE_COMPARE(a.data(), static_cast(nullptr)); } } @@ -428,30 +475,54 @@ template void ImageViewTest::constructCompressedImplementationSpecific( /* Format with autodetection */ { - T data[8]{}; - CompressedImageView<2, T> a{GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, + T data[6*8]{}; + CompressedImageView<2, T> a{ + GL::CompressedPixelFormat::RGBS3tcDxt1, {12, 8}, data, ImageFlag2D::Array}; CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{0}); + CORRADE_COMPARE(a.storage().rowLength(), 0); CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(a.blockDataSize(), 8); + CORRADE_COMPARE(a.size(), (Vector2i{12, 8})); CORRADE_COMPARE(a.data(), static_cast(data)); - CORRADE_COMPARE(a.data().size(), 8); + CORRADE_COMPARE(a.data().size(), 6*8); } { - T data[8]{}; - CompressedImageView<2, T> a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, data, ImageFlag2D::Array}; + T data[8*16]{}; + CompressedImageView<2, T> a{ + CompressedPixelStorage{}.setRowLength(20), + Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, + data, ImageFlag2D::Array}; CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(a.storage().rowLength(), 20); + CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + CORRADE_COMPARE(a.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(a.blockDataSize(), 16); + CORRADE_COMPARE(a.size(), (Vector2i{15, 10})); CORRADE_COMPARE(a.data(), static_cast(data)); - CORRADE_COMPARE(a.data().size(), 8); + CORRADE_COMPARE(a.data().size(), 8*16); } - /* Manual properties not implemented yet */ + /* Manual block properties */ + { + T data[6*12]{}; + CompressedImageView<2, T> a{ + CompressedPixelStorage{}.setRowLength(6), + 111, {3, 4, 5}, 12, {3, 8}, data, ImageFlag2D::Array}; + + CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); + CORRADE_COMPARE(a.storage().rowLength(), 6); + CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + /* These deliberately don't match what compressedPixelFormatBlockSize() + above returns */ + CORRADE_COMPARE(a.blockSize(), (Vector3i{3, 4, 5})); + CORRADE_COMPARE(a.blockDataSize(), 12); + CORRADE_COMPARE(a.size(), (Vector2i{3, 8})); + CORRADE_COMPARE(a.data(), static_cast(data)); + CORRADE_COMPARE(a.data().size(), 6*12); + } } template void ImageViewTest::constructCompressedImplementationSpecificEmpty() { @@ -459,25 +530,47 @@ template void ImageViewTest::constructCompressedImplementationSpecificE /* Format with autodetection */ { - CompressedImageView<2, T> a{GL::CompressedPixelFormat::RGBS3tcDxt1, {8, 16}, ImageFlag2D::Array}; + CompressedImageView<2, T> a{ + GL::CompressedPixelFormat::RGBS3tcDxt1, {12, 8}, + ImageFlag2D::Array}; CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{0}); + CORRADE_COMPARE(a.storage().rowLength(), 0); CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(a.size(), (Vector2i{8, 16})); + CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(a.blockDataSize(), 8); + CORRADE_COMPARE(a.size(), (Vector2i{12, 8})); CORRADE_COMPARE(a.data(), static_cast(nullptr)); } { - CompressedImageView<2, T> a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 8}, ImageFlag2D::Array}; + CompressedImageView<2, T> a{ + CompressedPixelStorage{}.setRowLength(20), + Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, ImageFlag2D::Array}; CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(a.size(), (Vector2i{4, 8})); + CORRADE_COMPARE(a.storage().rowLength(), 20); + CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + CORRADE_COMPARE(a.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(a.blockDataSize(), 16); + CORRADE_COMPARE(a.size(), (Vector2i{15, 10})); CORRADE_COMPARE(a.data(), static_cast(nullptr)); } - /* Manual properties not implemented yet */ + /* Manual block properties */ + { + CompressedImageView<2, T> a{ + CompressedPixelStorage{}.setRowLength(6), + 111, {3, 4, 5}, 12, {3, 8}, ImageFlag2D::Array}; + + CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); + CORRADE_COMPARE(a.storage().rowLength(), 6); + CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + /* These deliberately don't match what compressedPixelFormatBlockSize() + above returns */ + CORRADE_COMPARE(a.blockSize(), (Vector3i{3, 4, 5})); + CORRADE_COMPARE(a.blockDataSize(), 12); + CORRADE_COMPARE(a.size(), (Vector2i{3, 8})); + CORRADE_COMPARE(a.data(), static_cast(nullptr)); + } } void ImageViewTest::construct3DFrom1D() { @@ -549,29 +642,35 @@ void ImageViewTest::construct3DFrom2D() { void ImageViewTest::constructCompressed3DFrom1D() { /* Copied from constructCompressedImplementationSpecific(), as that exposes - most fields */ - /** @todo S3TC doesn't have 1D compression so this might blow up once we - check for block sizes */ - const char data[8]{}; - /** @todo use a real flag once it exists */ - CompressedImageView1D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - GL::CompressedPixelFormat::RGBS3tcDxt1, 4, data, ImageFlag1D(0xdea0)}; + most fields. It's okay to use a 2D format for a 1D image, only the first + row of each block gets used. */ + char data[4*16]{}; + CompressedImageView1D a{ + CompressedPixelStorage{}.setRowLength(20), + Vk::CompressedPixelFormat::Astc5x5x4RGBAF, 15, + data, ImageFlag1D(0xdea0)}; + CORRADE_COMPARE(a.flags(), ImageFlag1D(0xdea0)); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(a.size(), 4); + CORRADE_COMPARE(a.storage().rowLength(), 20); + CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + CORRADE_COMPARE(a.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(a.blockDataSize(), 16); + CORRADE_COMPARE(a.size(), 15); CORRADE_COMPARE(a.data(), static_cast(data)); - CORRADE_COMPARE(a.data().size(), 8); + CORRADE_COMPARE(a.data().size(), 4*16); /* Not testing the flags parameter here to be sure implicit conversion works as well, it's tested in constructCompressed3DFrom2D() below */ CompressedImageView3D b = a; CORRADE_COMPARE(b.flags(), ImageFlag3D(0xdea0)); - CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(b.size(), (Vector3i{4, 1, 1})); + CORRADE_COMPARE(b.storage().rowLength(), 20); + CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + CORRADE_COMPARE(b.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(b.blockDataSize(), 16); + /* The size doesn't get expanded */ + CORRADE_COMPARE(b.size(), (Vector3i{15, 1, 1})); CORRADE_COMPARE(b.data(), static_cast(data)); - CORRADE_COMPARE(b.data().size(), 8); + CORRADE_COMPARE(b.data().size(), 4*16); /* Conversion the other way is not allowed (will be later, but explicitly via a slice<1>() like with StridedArrayView); conversion from const to @@ -584,25 +683,32 @@ void ImageViewTest::constructCompressed3DFrom1D() { void ImageViewTest::constructCompressed3DFrom2D() { /* Copied from constructCompressedImplementationSpecific(), as that exposes most fields */ - char data[8*2]{}; - MutableCompressedImageView2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 8}, data, ImageFlag2D::Array|ImageFlag2D(0xde00)}; + char data[8*16]{}; + MutableCompressedImageView2D a{ + CompressedPixelStorage{}.setRowLength(20), + Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, + data, ImageFlag2D::Array|ImageFlag2D(0xde00)}; + CORRADE_COMPARE(a.flags(), ImageFlag2D::Array|ImageFlag2D(0xde00)); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(a.size(), (Vector2i{4, 8})); + CORRADE_COMPARE(a.storage().rowLength(), 20); + CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + CORRADE_COMPARE(a.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(a.blockDataSize(), 16); + CORRADE_COMPARE(a.size(), (Vector2i{15, 10})); CORRADE_COMPARE(a.data(), static_cast(data)); - CORRADE_COMPARE(a.data().size(), 8*2); + CORRADE_COMPARE(a.data().size(), 8*16); MutableCompressedImageView3D b = {a, ImageFlag3D(0x00a0)}; /* The Array flag got implicitly stripped away, the rest got passed through and combined with the flags argument */ CORRADE_COMPARE(b.flags(), ImageFlag3D(0xdea0)); - CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(b.size(), (Vector3i{4, 8, 1})); + CORRADE_COMPARE(b.storage().rowLength(), 20); + CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + CORRADE_COMPARE(b.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(b.blockDataSize(), 16); + CORRADE_COMPARE(b.size(), (Vector3i{15, 10, 1})); CORRADE_COMPARE(b.data(), static_cast(data)); - CORRADE_COMPARE(b.data().size(), 8*2); + CORRADE_COMPARE(b.data().size(), 8*16); /* Conversion the other way is not allowed (will be later, but explicitly via a slice<1>() like with StridedArrayView) */ @@ -638,23 +744,30 @@ void ImageViewTest::constructFromMutable() { void ImageViewTest::constructCompressedFromMutable() { /* Copied from constructCompressedImplementationSpecific(), as that exposes most fields */ - char data[8]{}; - MutableCompressedImageView2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, data, ImageFlag2D::Array}; + char data[8*16]{}; + MutableCompressedImageView2D a{ + CompressedPixelStorage{}.setRowLength(20), + Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, + data, ImageFlag2D::Array}; + CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(a.storage().rowLength(), 20); + CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + CORRADE_COMPARE(a.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(a.blockDataSize(), 16); + CORRADE_COMPARE(a.size(), (Vector2i{15, 10})); CORRADE_COMPARE(a.data(), static_cast(data)); - CORRADE_COMPARE(a.data().size(), 8); + CORRADE_COMPARE(a.data().size(), 8*16); CompressedImageView2D b = a; CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(b.storage().rowLength(), 20); + CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + CORRADE_COMPARE(b.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(b.blockDataSize(), 16); + CORRADE_COMPARE(b.size(), (Vector2i{15, 10})); CORRADE_COMPARE(b.data(), static_cast(data)); - CORRADE_COMPARE(b.data().size(), 8); + CORRADE_COMPARE(b.data().size(), 8*16); } void ImageViewTest::constructUnknownImplementationSpecificPixelSize() { @@ -729,10 +842,126 @@ void ImageViewTest::constructInvalidCubeMap() { "ImageView: expected exactly 6 faces for a cube map, got 5\n"); } -void ImageViewTest::constructCompressedInvalidSize() { +void ImageViewTest::constructCompressedUnknownImplementationSpecificBlockSize() { + CORRADE_SKIP_IF_NO_ASSERT(); + + char data[1]; + + Containers::String out; + Error redirectError{&out}; + CompressedImageView2D{compressedPixelFormatWrap(0x666), {1, 1}, data}; + CompressedImageView2D{compressedPixelFormatWrap(0x777), {1, 1}}; + CORRADE_COMPARE_AS(out, + "CompressedImageView: can't determine block size of an implementation-specific pixel format 0x666, pass it explicitly\n" + /* The next messages are printed because it cannot exit the + construction from the middle of the member initializer list. With + non-graceful asserts just one message is printed tho. */ + "compressedPixelFormatBlockSize(): can't determine size of an implementation-specific format 0x666\n" + "CompressedImageView: expected block size to be greater than zero and less than 256 but got {0, 0, 0}\n" + + "CompressedImageView: can't determine block size of an implementation-specific pixel format 0x777, pass it explicitly\n" + /* Same */ + "compressedPixelFormatBlockSize(): can't determine size of an implementation-specific format 0x777\n" + "CompressedImageView: expected block size to be greater than zero and less than 256 but got {0, 0, 0}\n", + TestSuite::Compare::String); +} + +void ImageViewTest::constructCompressedInvalidBlockSize() { CORRADE_SKIP_IF_NO_ASSERT(); - CORRADE_EXPECT_FAIL("Size checking for compressed image data is not implemented yet."); + /* This is all okay */ + char data[8]; + CompressedImageView2D{CompressedPixelStorage{}, 666, {4, 5, 6}, 8, {1, 1}, data}; + CompressedImageView2D{CompressedPixelStorage{} + .setCompressedBlockSize({4, 5, 6}) + .setCompressedBlockDataSize(8), + 666, {4, 5, 6}, 8, {1, 1}, data}; + + Containers::String out; + Error redirectError{&out}; + /* Zeros */ + CompressedImageView2D{CompressedPixelStorage{}, 666, {4, 4, 0}, 4, {1, 1}, nullptr}; + CompressedImageView2D{CompressedPixelStorage{}, 666, {4, 4, 0}, 4, {1, 1}}; + CompressedImageView2D{CompressedPixelStorage{}, 666, {4, 0, 4}, 4, {1, 1}, nullptr}; + CompressedImageView2D{CompressedPixelStorage{}, 666, {4, 0, 4}, 4, {1, 1}}; + CompressedImageView2D{CompressedPixelStorage{}, 666, {0, 4, 4}, 4, {1, 1}, nullptr}; + CompressedImageView2D{CompressedPixelStorage{}, 666, {0, 4, 4}, 4, {1, 1}}; + CompressedImageView2D{CompressedPixelStorage{}, 666, {4, 4, 4}, 0, {1, 1}, nullptr}; + CompressedImageView2D{CompressedPixelStorage{}, 666, {4, 4, 4}, 0, {1, 1}}; + /* Too large */ + CompressedImageView2D{CompressedPixelStorage{}, 666, {1, 1, 256}, 4, {1, 1}, nullptr}; + CompressedImageView2D{CompressedPixelStorage{}, 666, {1, 1, 256}, 4, {1, 1}}; + CompressedImageView2D{CompressedPixelStorage{}, 666, {1, 256, 1}, 4, {1, 1}, nullptr}; + CompressedImageView2D{CompressedPixelStorage{}, 666, {1, 256, 1}, 4, {1, 1}}; + CompressedImageView2D{CompressedPixelStorage{}, 666, {256, 1, 1}, 4, {1, 1}, nullptr}; + CompressedImageView2D{CompressedPixelStorage{}, 666, {256, 1, 1}, 4, {1, 1}}; + CompressedImageView2D{CompressedPixelStorage{}, 666, {1, 1, 1}, 256, {1, 1}, nullptr}; + CompressedImageView2D{CompressedPixelStorage{}, 666, {1, 1, 1}, 256, {1, 1}}; + /* CompressedPixelStorage doesn't match what is passed */ + CompressedImageView2D{CompressedPixelStorage{} + .setCompressedBlockSize({4, 5, 5}) + .setCompressedBlockDataSize(4), + 666, {4, 5, 6}, 4, {1, 1}, data}; + CompressedImageView2D{CompressedPixelStorage{} + .setCompressedBlockSize({4, 5, 5}) + .setCompressedBlockDataSize(4), + 666, {4, 5, 6}, 4, {1, 1}}; + CompressedImageView2D{CompressedPixelStorage{} + .setCompressedBlockSize({4, 6, 6}) + .setCompressedBlockDataSize(4), + 666, {4, 5, 6}, 4, {1, 1}, data}; + CompressedImageView2D{CompressedPixelStorage{} + .setCompressedBlockSize({4, 6, 6}) + .setCompressedBlockDataSize(4), + 666, {4, 5, 6}, 4, {1, 1}}; + CompressedImageView2D{CompressedPixelStorage{} + .setCompressedBlockSize({5, 5, 6}) + .setCompressedBlockDataSize(4), + 666, {4, 5, 6}, 4, {1, 1}, data}; + CompressedImageView2D{CompressedPixelStorage{} + .setCompressedBlockSize({5, 5, 6}) + .setCompressedBlockDataSize(4), + 666, {4, 5, 6}, 4, {1, 1}}; + CompressedImageView2D{CompressedPixelStorage{} + .setCompressedBlockSize({4, 5, 6}) + .setCompressedBlockDataSize(8), + 666, {4, 5, 6}, 4, {1, 1}, data}; + CompressedImageView2D{CompressedPixelStorage{} + .setCompressedBlockSize({4, 5, 6}) + .setCompressedBlockDataSize(8), + 666, {4, 5, 6}, 4, {1, 1}}; + CORRADE_COMPARE_AS(out, + "CompressedImageView: expected block size to be greater than zero and less than 256 but got {4, 4, 0}\n" + "CompressedImageView: expected block size to be greater than zero and less than 256 but got {4, 4, 0}\n" + "CompressedImageView: expected block size to be greater than zero and less than 256 but got {4, 0, 4}\n" + "CompressedImageView: expected block size to be greater than zero and less than 256 but got {4, 0, 4}\n" + "CompressedImageView: expected block size to be greater than zero and less than 256 but got {0, 4, 4}\n" + "CompressedImageView: expected block size to be greater than zero and less than 256 but got {0, 4, 4}\n" + "CompressedImageView: expected block data size to be non-zero and less than 256 but got 0\n" + "CompressedImageView: expected block data size to be non-zero and less than 256 but got 0\n" + + "CompressedImageView: expected block size to be greater than zero and less than 256 but got {1, 1, 256}\n" + "CompressedImageView: expected block size to be greater than zero and less than 256 but got {1, 1, 256}\n" + "CompressedImageView: expected block size to be greater than zero and less than 256 but got {1, 256, 1}\n" + "CompressedImageView: expected block size to be greater than zero and less than 256 but got {1, 256, 1}\n" + "CompressedImageView: expected block size to be greater than zero and less than 256 but got {256, 1, 1}\n" + "CompressedImageView: expected block size to be greater than zero and less than 256 but got {256, 1, 1}\n" + "CompressedImageView: expected block data size to be non-zero and less than 256 but got 256\n" + "CompressedImageView: expected block data size to be non-zero and less than 256 but got 256\n" + + "CompressedImageView: expected pixel storage block size to be either not set at all or equal to {4, 5, 6} but got {4, 5, 5}\n" + "CompressedImageView: expected pixel storage block size to be either not set at all or equal to {4, 5, 6} but got {4, 5, 5}\n" + "CompressedImageView: expected pixel storage block size to be either not set at all or equal to {4, 5, 6} but got {4, 6, 6}\n" + "CompressedImageView: expected pixel storage block size to be either not set at all or equal to {4, 5, 6} but got {4, 6, 6}\n" + "CompressedImageView: expected pixel storage block size to be either not set at all or equal to {4, 5, 6} but got {5, 5, 6}\n" + "CompressedImageView: expected pixel storage block size to be either not set at all or equal to {4, 5, 6} but got {5, 5, 6}\n" + "CompressedImageView: expected pixel storage block data size to be either not set at all or equal to 4 but got 8\n" + "CompressedImageView: expected pixel storage block data size to be either not set at all or equal to 4 but got 8\n", + TestSuite::Compare::String); +} + +void ImageViewTest::constructCompressedInvalidSize() { + CORRADE_SKIP_IF_NO_ASSERT(); const char data[15]{}; @@ -785,18 +1014,16 @@ void ImageViewTest::dataProperties() { } void ImageViewTest::dataPropertiesCompressed() { - /* Yes, I know, this is totally bogus and doesn't match the BC1 format */ - const char data[1]{}; + const char data[336]{}; CompressedImageView3D image{ CompressedPixelStorage{} - .setCompressedBlockSize({3, 4, 5}) - .setCompressedBlockDataSize(16) - .setImageHeight(12) - .setSkip({5, 8, 11}), - CompressedPixelFormat::Bc1RGBAUnorm, {2, 8, 11}, + .setRowLength(12) + .setImageHeight(8) + .setSkip({8, 4, 4}), + CompressedPixelFormat::Bc1RGBAUnorm, {2, 3, 3}, data}; CORRADE_COMPARE(image.dataProperties(), - (std::pair, Math::Vector3>{{2*16, 2*16, 9*16}, {1, 3, 3}})); + (std::pair, Math::Vector3>{{16, 24, 192}, {3, 2, 3}})); } template void ImageViewTest::setData() { @@ -811,23 +1038,23 @@ template void ImageViewTest::setData() { CORRADE_COMPARE(a.storage().alignment(), 1); CORRADE_COMPARE(a.format(), PixelFormat::RGB8Snorm); CORRADE_COMPARE(a.size(), Vector2i(1, 3)); - CORRADE_COMPARE(a.data(), &data2[0]); + CORRADE_COMPARE(a.data(), static_cast(data2)); } template void ImageViewTest::setDataCompressed() { setTestCaseTemplateName(MutabilityTraits::name()); - T data[8]{}; + T data[2*8]{}; CompressedImageView<2, T> a{ - CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), + CompressedPixelStorage{}.setRowLength(8), CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, data}; - T data2[16]{}; + T data2[2*8]{}; a.setData(data2); - CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); + CORRADE_COMPARE(a.storage().rowLength(), 8); CORRADE_COMPARE(a.format(), CompressedPixelFormat::Bc1RGBAUnorm); CORRADE_COMPARE(a.size(), Vector2i(4, 4)); - CORRADE_COMPARE(a.data(), &data2[0]); + CORRADE_COMPARE(a.data(), static_cast(data2)); } void ImageViewTest::setDataInvalidSize() { @@ -845,8 +1072,6 @@ void ImageViewTest::setDataInvalidSize() { void ImageViewTest::setDataCompressedInvalidSize() { CORRADE_SKIP_IF_NO_ASSERT(); - CORRADE_EXPECT_FAIL("Size checking for compressed image data is not implemented yet."); - const char data[11]{}; /* Too small for given format */ diff --git a/src/Magnum/Test/PixelStorageTest.cpp b/src/Magnum/Test/PixelStorageTest.cpp index 8a099581f..5eec37604 100644 --- a/src/Magnum/Test/PixelStorageTest.cpp +++ b/src/Magnum/Test/PixelStorageTest.cpp @@ -302,10 +302,10 @@ void PixelStorageTest::dataOffsetSizeCompressed1D() { /* Image size in whole blocks, no skip */ { CompressedImageView1D image{ - CompressedPixelStorage{} - .setCompressedBlockSize({5, 1, 1}) - .setCompressedBlockDataSize(8), + CompressedPixelStorage{}, 42069, /* custom format */ + {5, 1, 1}, + 8, 1, /* this is ignored, the passed size is used instead */ data}; CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, (Math::Vector<1, Int>{55})), (std::pair{ @@ -316,10 +316,10 @@ void PixelStorageTest::dataOffsetSizeCompressed1D() { } { CompressedImageView1D image{ CompressedPixelStorage{} - .setCompressedBlockSize({5, 1, 1}) - .setCompressedBlockDataSize(8) .setSkip({10, 0, 0}), 42069, + {5, 1, 1}, + 8, 1, data}; std::pair out = Implementation::compressedImageDataOffsetSizeFor(image, Math::Vector<1, Int>{35}); @@ -336,10 +336,10 @@ void PixelStorageTest::dataOffsetSizeCompressed1D() { } { CompressedImageView1D image{ CompressedPixelStorage{} - .setCompressedBlockSize({5, 1, 1}) - .setCompressedBlockDataSize(8) .setSkip({10, 0, 0}), 42069, + {5, 1, 1}, + 8, 1, data}; CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, (Math::Vector<1, Int>{35})), (std::pair{ @@ -356,10 +356,10 @@ void PixelStorageTest::dataOffsetSizeCompressed2D() { /* Image size in whole blocks, no skip */ { CompressedImageView2D image{ - CompressedPixelStorage{} - .setCompressedBlockSize({5, 4, 1}) - .setCompressedBlockDataSize(8), + CompressedPixelStorage{}, 42069, /* custom format */ + {5, 4, 1}, + 8, {1, 1}, /* this is ignored, the passed size is used instead */ data}; CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{55, 28}), (std::pair{ @@ -370,11 +370,11 @@ void PixelStorageTest::dataOffsetSizeCompressed2D() { } { CompressedImageView2D image{ CompressedPixelStorage{} - .setCompressedBlockSize({5, 4, 1}) - .setCompressedBlockDataSize(8) .setRowLength(45) .setSkip({10, 0, 0}), 42069, + {5, 4, 1}, + 8, {1, 1}, data}; std::pair out = Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{35, 20}); @@ -392,10 +392,10 @@ void PixelStorageTest::dataOffsetSizeCompressed2D() { } { CompressedImageView2D image{ CompressedPixelStorage{} - .setCompressedBlockSize({5, 4, 1}) - .setCompressedBlockDataSize(8) .setSkip({0, 8, 0}), 42069, + {5, 4, 1}, + 8, {1, 1}, data}; std::pair out = Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{35, 20}); @@ -412,11 +412,11 @@ void PixelStorageTest::dataOffsetSizeCompressed2D() { } { CompressedImageView2D image{ CompressedPixelStorage{} - .setCompressedBlockSize({5, 4, 1}) - .setCompressedBlockDataSize(8) .setRowLength(45) .setSkip({0, 8, 0}), 42069, + {5, 4, 1}, + 8, {1, 1}, data}; std::pair out = Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{35, 20}); @@ -433,11 +433,11 @@ void PixelStorageTest::dataOffsetSizeCompressed2D() { } { CompressedImageView2D image{ CompressedPixelStorage{} - .setCompressedBlockSize({5, 4, 1}) - .setCompressedBlockDataSize(8) .setRowLength(45) .setSkip({10, 8, 0}), 42069, + {5, 4, 1}, + 8, {1, 1}, data}; std::pair out = Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{35, 20}); @@ -454,11 +454,11 @@ void PixelStorageTest::dataOffsetSizeCompressed2D() { } { CompressedImageView2D image{ CompressedPixelStorage{} - .setCompressedBlockSize({5, 4, 1}) - .setCompressedBlockDataSize(8) .setRowLength(41) .setSkip({10, 8, 0}), 42069, + {5, 4, 1}, + 8, {1, 1}, data}; CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{35, 20}), (std::pair{ @@ -475,10 +475,10 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() { /* Image size in whole blocks, no offset */ { CompressedImageView3D image{ - CompressedPixelStorage{} - .setCompressedBlockSize({5, 4, 2}) - .setCompressedBlockDataSize(16), + CompressedPixelStorage{}, 42069, /* custom format */ + {5, 4, 2}, + 16, {1, 1, 1}, /* this is ignored, the passed size is used instead */ data}; CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{45, 28, 6}), (std::pair{ @@ -489,11 +489,11 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() { } { CompressedImageView3D image{ CompressedPixelStorage{} - .setCompressedBlockSize({5, 4, 2}) - .setCompressedBlockDataSize(16) .setRowLength(45) .setSkip({10, 0, 0}), 42069, + {5, 4, 2}, + 16, {1, 1, 1}, data}; std::pair out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6}); @@ -511,12 +511,12 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() { } { CompressedImageView3D image{ CompressedPixelStorage{} - .setCompressedBlockSize({5, 4, 2}) - .setCompressedBlockDataSize(16) .setRowLength(45) .setImageHeight(28) .setSkip({20, 0, 0}), 42069, + {5, 4, 2}, + 16, {1, 1, 1}, data}; std::pair out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6}); @@ -534,11 +534,11 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() { } { CompressedImageView3D image{ CompressedPixelStorage{} - .setCompressedBlockSize({5, 4, 2}) - .setCompressedBlockDataSize(16) .setImageHeight(28) .setSkip({0, 8, 0}), 42069, + {5, 4, 2}, + 16, {1, 1, 1}, data}; std::pair out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6}); @@ -556,12 +556,12 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() { } { CompressedImageView3D image{ CompressedPixelStorage{} - .setCompressedBlockSize({5, 4, 2}) - .setCompressedBlockDataSize(16) .setRowLength(45) .setImageHeight(28) .setSkip({0, 8, 0}), 42069, + {5, 4, 2}, + 16, {1, 1, 1}, data}; std::pair out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6}); @@ -579,12 +579,12 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() { } { CompressedImageView3D image{ CompressedPixelStorage{} - .setCompressedBlockSize({5, 4, 2}) - .setCompressedBlockDataSize(16) .setRowLength(45) .setImageHeight(28) .setSkip({0, 0, 4}), 42069, + {5, 4, 2}, + 16, {1, 1, 1}, data}; std::pair out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6}); @@ -601,12 +601,12 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() { } { CompressedImageView3D image{ CompressedPixelStorage{} - .setCompressedBlockSize({5, 4, 2}) - .setCompressedBlockDataSize(16) .setRowLength(45) .setImageHeight(28) .setSkip({10, 8, 4}), 42069, + {5, 4, 2}, + 16, {1, 1, 1}, data}; std::pair out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6}); @@ -624,12 +624,12 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() { } { CompressedImageView3D image{ CompressedPixelStorage{} - .setCompressedBlockSize({5, 4, 2}) - .setCompressedBlockDataSize(16) .setRowLength(41) .setImageHeight(27) .setSkip({10, 8, 4}), 42069, + {5, 4, 2}, + 16, {1, 1, 1}, data}; CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6}), (std::pair{ @@ -639,9 +639,14 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() { } void PixelStorageTest::dataOffsetSizeCompressedZeroSize() { - const CompressedImage3D image{CompressedPixelStorage{} - .setCompressedBlockSize({4, 4, 1}) - .setCompressedBlockDataSize(16)}; + char data[16]; + const CompressedImageView3D image{ + CompressedPixelStorage{}, + 42069, /* custom format */ + {4, 4, 1}, + 16, + {1, 1, 1}, /* this is ignored, the passed size is used instead */ + data}; CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{0, 4, 4}), (std::pair{})); CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{4, 0, 4}), diff --git a/src/Magnum/Trade/ImageData.cpp b/src/Magnum/Trade/ImageData.cpp index 87a62fce1..919c4f4d2 100644 --- a/src/Magnum/Trade/ImageData.cpp +++ b/src/Magnum/Trade/ImageData.cpp @@ -72,32 +72,68 @@ template ImageData::ImageData(const PixelSto _dataFlags = dataFlags; } -template ImageData::ImageData(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor& size, Containers::Array&& data, const ImageFlags flags, const void* const importerState) noexcept: _dataFlags{DataFlag::Owned|DataFlag::Mutable}, _compressed{true}, _flags{flags}, _compressedStorage{storage}, _compressedFormat{format}, _size{size}, _data{Utility::move(data)}, _importerState{importerState} { +template ImageData::ImageData(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor& size, Containers::Array&& data, const ImageFlags flags, const void* const importerState) noexcept: ImageData{storage, format, ( + /* Have to do it like this to avoid an ungraceful assertion from + compressedPixelFormatBlockDataSize() / compressedPixelFormatBlockSize() + afterwards */ #ifndef CORRADE_NO_ASSERT + isCompressedPixelFormatImplementationSpecific(format) ? + (CORRADE_CONSTEXPR_ASSERT(false, "Trade::ImageData: can't determine block size of an implementation-specific pixel format" << Debug::hex << compressedPixelFormatUnwrap(format) << Debug::nospace << ", pass it explicitly"), Vector3i{}) : + #endif + compressedPixelFormatBlockSize(format) + ), ( + #ifndef CORRADE_NO_ASSERT + isCompressedPixelFormatImplementationSpecific(format) ? 0 : + #endif + compressedPixelFormatBlockDataSize(format) + ), size, Utility::move(data), flags, importerState} {} + +template ImageData::ImageData(const CompressedPixelStorage storage, const CompressedPixelFormat format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor& size, Containers::Array&& data, const ImageFlags flags, const void* const importerState) noexcept: _dataFlags{DataFlag::Owned|DataFlag::Mutable}, _compressed{true}, _flags{flags}, _compressedStorage{storage}, _compressedFormat{format}, _blockSize{Vector3ub{blockSize}}, _blockDataSize{UnsignedByte(blockDataSize)}, _size{size}, _data{Utility::move(data)}, _importerState{importerState} { + #ifndef CORRADE_NO_ASSERT + const bool passed = + Magnum::Implementation::checkBlockProperties("Trade::ImageData:", blockSize, blockDataSize); + #ifdef CORRADE_GRACEFUL_ASSERT + /* If the above check fails on a build with graceful assertions, the data + size check below could then die on division by zero. Exit early in that + case. */ + /** @todo any better idea to handle this? ugh */ + if(!passed) return; + #else + static_cast(passed); + #endif + Magnum::Implementation::checkBlockPropertiesForStorage("Trade::ImageData:", blockSize, blockDataSize, storage); + CORRADE_ASSERT(Magnum::Implementation::compressedImageDataSize(*this) <= _data.size(), "Trade::ImageData: data too small, got" << _data.size() << "but expected at least" << Magnum::Implementation::compressedImageDataSize(*this) << "bytes", ); Magnum::Implementation::checkImageFlagsForSize("Trade::ImageData:", flags, size); #endif } +template ImageData::ImageData(const CompressedPixelStorage storage, const UnsignedInt format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor& size, Containers::Array&& data, const ImageFlags flags, const void* const importerState) noexcept: ImageData{storage, compressedPixelFormatWrap(format), blockSize, blockDataSize, size, Utility::move(data), flags, importerState} {} + template ImageData::ImageData(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor& size, const DataFlags dataFlags, const Containers::ArrayView data, const ImageFlags flags, const void* const importerState) noexcept: ImageData{storage, format, size, Containers::Array{const_cast(static_cast(data.data())), data.size(), Implementation::nonOwnedArrayDeleter}, flags, importerState} { CORRADE_ASSERT(!(dataFlags & DataFlag::Owned), "Trade::ImageData: can't construct a non-owned instance with" << dataFlags, ); _dataFlags = dataFlags; } -template ImageData::ImageData(const CompressedPixelFormat format, const VectorTypeFor& size, Containers::Array&& data, const ImageFlags flags, const void* const importerState) noexcept: ImageData{{}, format, size, Utility::move(data), flags, importerState} {} +template ImageData::ImageData(const CompressedPixelStorage storage, const CompressedPixelFormat format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor& size, const DataFlags dataFlags, const Containers::ArrayView data, const ImageFlags flags, const void* const importerState) noexcept: ImageData{storage, format, blockSize, blockDataSize, size, Containers::Array{const_cast(static_cast(data.data())), data.size(), Implementation::nonOwnedArrayDeleter}, flags, importerState} { + CORRADE_ASSERT(!(dataFlags & DataFlag::Owned), + "Trade::ImageData: can't construct a non-owned instance with" << dataFlags, ); + _dataFlags = dataFlags; +} -template ImageData::ImageData(const CompressedPixelFormat format, const VectorTypeFor& size, const DataFlags dataFlags, const Containers::ArrayView data, const ImageFlags flags, const void* const importerState) noexcept: ImageData{{}, format, size, dataFlags, data, flags, importerState} {} +template ImageData::ImageData(const CompressedPixelStorage storage, const UnsignedInt format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor& size, const DataFlags dataFlags, const Containers::ArrayView data, const ImageFlags flags, const void* const importerState) noexcept: ImageData{storage, compressedPixelFormatWrap(format), blockSize, blockDataSize, size, dataFlags, data, flags, importerState} {} -template ImageData::ImageData(const CompressedPixelStorage storage, const UnsignedInt format, const VectorTypeFor& size, Containers::Array&& data, const ImageFlags flags, const void* const importerState) noexcept: ImageData{storage, compressedPixelFormatWrap(format), size, Utility::move(data), flags, importerState} {} +template ImageData::ImageData(const CompressedPixelFormat format, const VectorTypeFor& size, Containers::Array&& data, const ImageFlags flags, const void* const importerState) noexcept: ImageData{{}, format, size, Utility::move(data), flags, importerState} {} -template ImageData::ImageData(const CompressedPixelStorage storage, const UnsignedInt format, const VectorTypeFor& size, const DataFlags dataFlags, const Containers::ArrayView data, const ImageFlags flags, const void* const importerState) noexcept: ImageData{storage, compressedPixelFormatWrap(format), size, dataFlags, data, flags, importerState} {} +template ImageData::ImageData(const CompressedPixelFormat format, const VectorTypeFor& size, const DataFlags dataFlags, const Containers::ArrayView data, const ImageFlags flags, const void* const importerState) noexcept: ImageData{{}, format, size, dataFlags, data, flags, importerState} {} template ImageData::ImageData(ImageData&& other) noexcept: _dataFlags{other._dataFlags}, _compressed{Utility::move(other._compressed)}, _flags{Utility::move(other._flags)}, _size{Utility::move(other._size)}, _data{Utility::move(other._data)}, _importerState{Utility::move(other._importerState)} { if(_compressed) { - new(&_compressedStorage) CompressedPixelStorage{Utility::move(other._compressedStorage)}; - _compressedFormat = Utility::move(other._compressedFormat); - } - else { + new(&_compressedStorage) CompressedPixelStorage{other._compressedStorage}; + _compressedFormat = other._compressedFormat; + _blockSize = other._blockSize; + _blockDataSize = other._blockDataSize; + } else { new(&_storage) PixelStorage{Utility::move(other._storage)}; _format = Utility::move(other._format); _formatExtra = Utility::move(other._formatExtra); @@ -116,20 +152,26 @@ template ImageData& ImageData::o swap(_dataFlags, other._dataFlags); swap(_compressed, other._compressed); swap(_flags, other._flags); + /* Because the CompressedPixelStorage is larger than the uncompressed, copy it if either of the sides is compressed to ensure no compressed properties are lost. The _storage / _compressedStorage and _format / _compressedFormat are unions of trivially copyable contents so - copying a type that's not there should be fine. */ + copying a type that's not there should be fine. Then, + _compressedBlockDataSize and _pixelSize have compatible layout, so + swapping one with the other is fine, but _compressedBlockSize is smaller + than _formatExtra, so swapping _formatExtra always. */ if(_compressed || other._compressed) { swap(_compressedStorage, other._compressedStorage); swap(_compressedFormat, other._compressedFormat); + swap(_blockDataSize, other._blockDataSize); } else { swap(_storage, other._storage); swap(_format, other._format); + swap(_pixelSize, other._pixelSize); } swap(_formatExtra, other._formatExtra); - swap(_pixelSize, other._pixelSize); + swap(_size, other._size); swap(_data, other._data); swap(_importerState, other._importerState); @@ -166,11 +208,26 @@ template UnsignedInt ImageData::pixelSize() return _pixelSize; } +template Vector3i ImageData::blockSize() const { + CORRADE_ASSERT(_compressed, "Trade::ImageData::blockSize(): the image is not compressed", {}); + return Vector3i{_blockSize}; +} + +template UnsignedInt ImageData::blockDataSize() const { + CORRADE_ASSERT(_compressed, "Trade::ImageData::blockDataSize(): the image is not compressed", {}); + return _blockDataSize; +} + template std::pair, VectorTypeFor> ImageData::dataProperties() const { CORRADE_ASSERT(!_compressed, "Trade::ImageData::dataProperties(): the image is compressed", {}); return Magnum::Implementation::imageDataProperties(*this); } +template std::pair, VectorTypeFor> ImageData::compressedDataProperties() const { + CORRADE_ASSERT(_compressed, "Trade::ImageData::compressedDataProperties(): the image is not compressed", {}); + return Magnum::Implementation::compressedImageDataProperties(*this); +} + template Containers::ArrayView ImageData::mutableData() & { CORRADE_ASSERT(_dataFlags & DataFlag::Mutable, "Trade::ImageData::mutableData(): the image is not mutable", {}); @@ -202,20 +259,16 @@ template ImageData::operator BasicMutableIma } template ImageData::operator BasicCompressedImageView() const { - CORRADE_ASSERT(_compressed, "Trade::ImageData: the image is not compressed", (BasicCompressedImageView{_compressedStorage, _compressedFormat, _size})); - return BasicCompressedImageView{ - _compressedStorage, - _compressedFormat, _size, _data, _flags}; + CORRADE_ASSERT(_compressed, "Trade::ImageData: the image is not compressed", (BasicCompressedImageView{_compressedStorage, _compressedFormat, Vector3i{_blockSize}, _blockDataSize, _size})); + return BasicCompressedImageView{_compressedStorage, _compressedFormat, Vector3i{_blockSize}, _blockDataSize, _size, _data, _flags}; } template ImageData::operator BasicMutableCompressedImageView() { CORRADE_ASSERT(_dataFlags & DataFlag::Mutable, "Trade::ImageData: the image is not mutable", - (BasicMutableCompressedImageView{_compressedStorage, _compressedFormat, _size})); - CORRADE_ASSERT(_compressed, "Trade::ImageData: the image is not compressed", (BasicMutableCompressedImageView{_compressedStorage, _compressedFormat, _size})); - return BasicMutableCompressedImageView{ - _compressedStorage, - _compressedFormat, _size, _data, _flags}; + (BasicMutableCompressedImageView{_compressedStorage, _compressedFormat, Vector3i{_blockSize}, _blockDataSize, _size})); + CORRADE_ASSERT(_compressed, "Trade::ImageData: the image is not compressed", (BasicMutableCompressedImageView{_compressedStorage, _compressedFormat, Vector3i{_blockSize}, _blockDataSize, _size})); + return BasicMutableCompressedImageView{_compressedStorage, _compressedFormat, Vector3i{_blockSize}, _blockDataSize, _size, _data, _flags}; } template Containers::Array ImageData::release() { diff --git a/src/Magnum/Trade/ImageData.h b/src/Magnum/Trade/ImageData.h index e1e7d5123..d6a0e6297 100644 --- a/src/Magnum/Trade/ImageData.h +++ b/src/Magnum/Trade/ImageData.h @@ -476,6 +476,24 @@ template class ImageData { * @param flags Image layout flags * @param importerState Importer-specific state * @m_since_latest + * + * The @p data array is expected to be of proper size for given + * parameters. For a 3D image, if @p flags contain + * @ref ImageFlag3D::CubeMap, the @p size is expected to match its + * restrictions. + * + * The @p format is expected to not be implementation-specific, use the + * @ref ImageData(CompressedPixelStorage, CompressedPixelFormat, const Vector3i&, UnsignedInt, const VectorTypeFor&, Containers::Array&&, ImageFlags, const void*) + * overload to explicitly pass pass an implementation-specific + * @ref CompressedPixelFormat along with its block properties, or the + * @ref ImageData(CompressedPixelStorage, T, const VectorTypeFor&, Containers::Array&&, ImageFlags, const void*) + * overload with the original implementation-specific enum type to have + * the pixel size determined implicitly. + * + * @ref CompressedPixelStorage::compressedBlockSize() and + * @relativeref{CompressedPixelStorage,compressedBlockDataSize()} in + * @p storage are expected to be either both zero or exactly matching + * properties of given @p format. */ explicit ImageData(CompressedPixelStorage storage, CompressedPixelFormat format, const VectorTypeFor& size, Containers::Array&& data, ImageFlags flags = {}, const void* importerState = nullptr) noexcept; @@ -564,20 +582,65 @@ template class ImageData { #endif /** - * @brief Construct a compressed image data + * @brief Construct a compressed image data with an implementation-specific pixel format * @param storage Storage of compressed pixel data * @param format Format of compressed pixel data - * @param size Image size + * @param blockSize Size of a compressed block in given format, + * in pixels + * @param blockDataSize Size of a compressed block in given format, + * in bytes + * @param size Image size, in pixels * @param data Image data * @param flags Image layout flags * @param importerState Importer-specific state * @m_since_latest * - * Uses @ref compressedPixelFormatWrap() internally to convert - * @p format to @ref CompressedPixelFormat. + * Unlike with @ref ImageData(CompressedPixelStorage, CompressedPixelFormat, const VectorTypeFor&, Containers::Array&&, ImageFlags, const void*), + * where block size is determined automatically + * @ref compressedPixelFormatBlockSize() and + * @ref compressedPixelFormatBlockDataSize(), this allows you to + * specify an implementation-specific pixel format and block properties + * directly. Uses @ref compressedPixelFormatWrap() internally to wrap + * @p format in @ref CompressedPixelFormat. The @p blockSize and + * @p blockDataSize is expected to be greater than @cpp 0 @ce and less + * than @cpp 256 @ce. Note that the blocks can be 3D even for 2D images + * and 2D or 3D even for 1D images, in which case only the first slice + * in the extra dimensions is used. + * + * @ref CompressedPixelStorage::compressedBlockSize() and + * @relativeref{CompressedPixelStorage,compressedBlockDataSize()} in + * @p storage are expected to be either both zero or exactly matching + * @p blockSize and @p blockDataSize. + * + * The @p data array is expected to be of proper size for given + * parameters. For a 3D image, if @p flags contain + * @ref ImageFlag3D::CubeMap, the @p size is expected to match its + * restrictions. + */ + explicit ImageData(CompressedPixelStorage storage, UnsignedInt format, const Vector3i& blockSize, UnsignedInt blockDataSize, const VectorTypeFor& size, Containers::Array&& data, ImageFlags flags = {}, const void* importerState = nullptr) noexcept; + + /** @overload + * @m_since_latest + * + * Equivalent to the above for @p format already wrapped with + * @ref compressedPixelFormatWrap(). + */ + explicit ImageData(CompressedPixelStorage storage, CompressedPixelFormat format, const Vector3i& blockSize, UnsignedInt blockDataSize, const VectorTypeFor& size, Containers::Array&& data, ImageFlags flags = {}, const void* importerState = nullptr) noexcept; + + /** + * @brief Construct a compressed image data with an implementation-specific format + * @param storage Storage of compressed pixel data + * @param format Format of compressed pixel data + * @param size Image size + * @param data Image data + * @param flags Image layout flags + * @param importerState Importer-specific state + * @m_since_latest * - * For a 3D image, if @p flags contain @ref ImageFlag3D::CubeMap, the - * @p size is expected to match its restrictions. + * Uses ADL to find a corresponding @cpp compressedPixelFormatBlockSize(T) @ce + * and @cpp compressedPixelFormatBlockDataSize(T) @ce overloads, then + * calls @ref ImageData(CompressedPixelStorage, UnsignedInt, const Vector3i&, UnsignedInt, const VectorTypeFor&, Containers::Array&&, ImageFlags, const void*) + * with determined block size properties. */ template explicit ImageData(CompressedPixelStorage storage, T format, const VectorTypeFor& size, Containers::Array&& data, ImageFlags flags = {}, const void* importerState = nullptr) noexcept; @@ -591,22 +654,52 @@ template class ImageData { #endif /** - * @brief Construct a non-owned compressed image data + * @brief Construct a non-owned compressed image data with an implementation-specific pixel format * @param storage Storage of compressed pixel data * @param format Format of compressed pixel data - * @param size Image size + * @param blockSize Size of a compressed block in given format, + * in pixels + * @param blockDataSize Size of a compressed block in given format, + * in bytes + * @param size Image size, in pixels * @param dataFlags Data flags - * @param data View on image data + * @param data Image data * @param flags Image layout flags * @param importerState Importer-specific state * @m_since_latest * - * Compared to @ref ImageData(CompressedPixelStorage, T, const VectorTypeFor&, Containers::Array&&, ImageFlags, const void*) + * Compared to @ref ImageData(CompressedPixelStorage, UnsignedInt, const Vector3i&, UnsignedInt, const VectorTypeFor&, Containers::Array&&, ImageFlags, const void*) * creates an instance that doesn't own the passed data. The * @p dataFlags parameter can contain @ref DataFlag::Mutable to * indicate the external data can be modified, and is expected to *not* * have @ref DataFlag::Owned set. */ + explicit ImageData(CompressedPixelStorage storage, UnsignedInt format, const Vector3i& blockSize, UnsignedInt blockDataSize, const VectorTypeFor& size, DataFlags dataFlags, Containers::ArrayView data, ImageFlags flags = {}, const void* importerState = nullptr) noexcept; + + /** @overload + * @m_since_latest + * + * Equivalent to the above for @p format already wrapped with + * @ref compressedPixelFormatWrap(). + */ + explicit ImageData(CompressedPixelStorage storage, CompressedPixelFormat format, const Vector3i& blockSize, UnsignedInt blockDataSize, const VectorTypeFor& size, DataFlags dataFlags, Containers::ArrayView data, ImageFlags flags = {}, const void* importerState = nullptr) noexcept; + + /** + * @brief Construct a non-owned compressed image data with implementation-specific format + * @param storage Storage of compressed pixel data + * @param format Format of compressed pixel data + * @param size Image size + * @param dataFlags Data flags + * @param data View on image data + * @param flags Image layout flags + * @param importerState Importer-specific state + * @m_since_latest + * + * Uses ADL to find a corresponding @cpp compressedPixelFormatBlockSize(T) @ce + * and @cpp compressedPixelFormatBlockDataSize(T) @ce overloads, then + * calls @ref ImageData(CompressedPixelStorage, UnsignedInt, const Vector3i&, UnsignedInt, const VectorTypeFor&, DataFlags, Containers::ArrayView, ImageFlags, const void*) + * with determined block size properties. + */ template explicit ImageData(CompressedPixelStorage storage, T format, const VectorTypeFor& size, DataFlags dataFlags, Containers::ArrayView data, ImageFlags flags = {}, const void* importerState = nullptr) noexcept; #ifdef MAGNUM_BUILD_DEPRECATED @@ -759,6 +852,28 @@ template class ImageData { */ UnsignedInt pixelSize() const; + /** + * @brief Size of a block in pixels + * @m_since_latest + * + * The image is expected to be compressed. Note that the blocks can be + * 3D even for 2D images and 2D or 3D even for 1D images, in which case + * only the first slice in the extra dimensions is used. + * @see @ref isCompressed(), @ref blockDataSize(), + * @ref compressedPixelFormatBlockSize() + */ + Vector3i blockSize() const; + + /** + * @brief Size of a block in bytes + * @m_since_latest + * + * The image is expected to be compressed. + * @see @ref isCompressed(), @ref blockSize(), + * @ref compressedPixelFormatBlockDataSize() + */ + UnsignedInt blockDataSize() const; + /** @brief Image size in pixels */ /* Unlike other getters this one is a const& so it's possible to slice to the sizes when all images are in an array, for example for use @@ -774,9 +889,15 @@ template class ImageData { */ std::pair, VectorTypeFor> dataProperties() const; - /* compressed data properties are not available because the importers - are not setting any block size pixel storage properties to avoid - needless state changes -- thus the calculation can't be done */ + /** + * @brief Compressed image data properties + * @m_since_latest + * + * The image is expected to be compressed. See + * @ref CompressedPixelStorage::dataProperties() for more information. + * @see @ref isCompressed() + */ + std::pair, VectorTypeFor> compressedDataProperties() const; /** * @brief Raw image data @@ -896,10 +1017,6 @@ template class ImageData { friend AbstractImporter; friend AbstractImageConverter; - explicit ImageData(CompressedPixelStorage storage, UnsignedInt format, const VectorTypeFor& size, Containers::Array&& data, ImageFlags flags, const void* importerState = nullptr) noexcept; - - explicit ImageData(CompressedPixelStorage storage, UnsignedInt format, const VectorTypeFor& size, DataFlags dataFlags, Containers::ArrayView data, ImageFlags flags, const void* importerState = nullptr) noexcept; - DataFlags _dataFlags; bool _compressed; ImageFlags _flags; @@ -911,8 +1028,14 @@ template class ImageData { PixelFormat _format; CompressedPixelFormat _compressedFormat; }; - UnsignedInt _formatExtra; - UnsignedByte _pixelSize; + union { + UnsignedInt _formatExtra; + Vector3ub _blockSize; + }; + union { + UnsignedByte _pixelSize; + UnsignedByte _blockDataSize; + }; /* 3 bytes free */ VectorTypeFor _size; Containers::Array _data; @@ -948,12 +1071,12 @@ template template ImageData::ImageD "format types larger than 32bits are not supported"); } -template template ImageData::ImageData(const CompressedPixelStorage storage, const T format, const VectorTypeFor& size, Containers::Array&& data, const ImageFlags flags, const void* const importerState) noexcept: ImageData{storage, UnsignedInt(format), size, Utility::move(data), flags, importerState} { +template template ImageData::ImageData(const CompressedPixelStorage storage, const T format, const VectorTypeFor& size, Containers::Array&& data, const ImageFlags flags, const void* const importerState) noexcept: ImageData{storage, UnsignedInt(format), compressedPixelFormatBlockSize(format), compressedPixelFormatBlockDataSize(format), size, Utility::move(data), flags, importerState} { static_assert(sizeof(T) <= 4, "format types larger than 32bits are not supported"); } -template template ImageData::ImageData(const CompressedPixelStorage storage, const T format, const VectorTypeFor& size, const DataFlags dataFlags, const Containers::ArrayView data, const ImageFlags flags, const void* const importerState) noexcept: ImageData{storage, UnsignedInt(format), size, dataFlags, data, flags, importerState} { +template template ImageData::ImageData(const CompressedPixelStorage storage, const T format, const VectorTypeFor& size, const DataFlags dataFlags, const Containers::ArrayView data, const ImageFlags flags, const void* const importerState) noexcept: ImageData{storage, UnsignedInt(format), compressedPixelFormatBlockSize(format), compressedPixelFormatBlockDataSize(format), size, dataFlags, data, flags, importerState} { static_assert(sizeof(T) <= 4, "format types larger than 32bits are not supported"); } diff --git a/src/Magnum/Trade/Test/ImageDataTest.cpp b/src/Magnum/Trade/Test/ImageDataTest.cpp index f7bc84510..fc6aef471 100644 --- a/src/Magnum/Trade/Test/ImageDataTest.cpp +++ b/src/Magnum/Trade/Test/ImageDataTest.cpp @@ -62,6 +62,8 @@ struct ImageDataTest: TestSuite::Tester { void constructInvalidPixelSize(); void constructInvalidSize(); void constructInvalidCubeMap(); + void constructCompressedUnknownImplementationSpecificBlockSize(); + void constructCompressedInvalidBlockSize(); void constructCompressedInvalidSize(); void constructCompressedInvalidCubeMap(); @@ -90,6 +92,7 @@ struct ImageDataTest: TestSuite::Tester { void mutableAccessNotAllowed(); void dataProperties(); + void dataPropertiesCompressed(); void release(); void releaseCompressed(); @@ -140,6 +143,8 @@ ImageDataTest::ImageDataTest() { &ImageDataTest::constructInvalidPixelSize, &ImageDataTest::constructInvalidSize, &ImageDataTest::constructInvalidCubeMap, + &ImageDataTest::constructCompressedUnknownImplementationSpecificBlockSize, + &ImageDataTest::constructCompressedInvalidBlockSize, &ImageDataTest::constructCompressedInvalidSize, &ImageDataTest::constructCompressedInvalidCubeMap, @@ -172,6 +177,7 @@ ImageDataTest::ImageDataTest() { &ImageDataTest::mutableAccessNotAllowed, &ImageDataTest::dataProperties, + &ImageDataTest::dataPropertiesCompressed, &ImageDataTest::release, &ImageDataTest::releaseCompressed, @@ -198,6 +204,20 @@ namespace GL { } enum class CompressedPixelFormat { RGBS3tcDxt1 = 21 }; + Vector3i compressedPixelFormatBlockSize(CompressedPixelFormat format) { + CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::RGBS3tcDxt1); + #ifdef CORRADE_NO_ASSERT + static_cast(format); + #endif + return {4, 4, 1}; + } + UnsignedInt compressedPixelFormatBlockDataSize(CompressedPixelFormat format) { + CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::RGBS3tcDxt1); + #ifdef CORRADE_NO_ASSERT + static_cast(format); + #endif + return 8; + } } namespace Vk { @@ -212,6 +232,22 @@ namespace Vk { #endif return 12; } + + enum class CompressedPixelFormat { Astc5x5x4RGBAF = 111 }; + Vector3i compressedPixelFormatBlockSize(CompressedPixelFormat format) { + #ifdef CORRADE_NO_ASSERT + static_cast(format); + #endif + CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::Astc5x5x4RGBAF); + return {5, 5, 4}; + } + UnsignedInt compressedPixelFormatBlockDataSize(CompressedPixelFormat format) { + #ifdef CORRADE_NO_ASSERT + static_cast(format); + #endif + CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::Astc5x5x4RGBAF); + return 16; + } } void ImageDataTest::constructGeneric() { @@ -333,39 +369,45 @@ void ImageDataTest::constructImplementationSpecific() { void ImageDataTest::constructCompressedGeneric() { { - auto data = new char[8]; + auto data = new char[7*8]; int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ - ImageData2D a{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, - Containers::Array{data, 8}, ImageFlag2D::Array, &state}; + ImageData2D a{ + CompressedPixelFormat::Bc1RGBAUnorm, {12, 8}, + Containers::Array{data, 7*8}, ImageFlag2D::Array, &state}; CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_VERIFY(a.isCompressed()); CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.compressedStorage().compressedBlockSize(), Vector3i{0}); + CORRADE_COMPARE(a.compressedStorage().rowLength(), 0); CORRADE_COMPARE(a.compressedFormat(), CompressedPixelFormat::Bc1RGBAUnorm); - CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(a.blockDataSize(), 8); + CORRADE_COMPARE(a.size(), (Vector2i{12, 8})); CORRADE_COMPARE(static_cast(a.data().data()), data); - CORRADE_COMPARE(a.data().size(), 8); + CORRADE_COMPARE(a.data().size(), 7*8); CORRADE_COMPARE(static_cast(a.mutableData().data()), data); - CORRADE_COMPARE(a.mutableData().size(), 8); + CORRADE_COMPARE(a.mutableData().size(), 7*8); CORRADE_COMPARE(a.importerState(), &state); } { - auto data = new char[8]; + auto data = new char[8*16]; int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ - ImageData2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, - Containers::Array{data, 8}, ImageFlag2D::Array, &state}; + ImageData2D a{ + CompressedPixelStorage{}.setRowLength(20), + CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, + Containers::Array{data, 8*16}, ImageFlag2D::Array, &state}; CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_VERIFY(a.isCompressed()); CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.compressedStorage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(a.compressedFormat(), CompressedPixelFormat::Bc1RGBAUnorm); - CORRADE_COMPARE(a.size(), Vector2i(4, 4)); + CORRADE_COMPARE(a.compressedStorage().rowLength(), 20); + CORRADE_COMPARE(a.compressedFormat(), CompressedPixelFormat::Astc5x5x4RGBAF); + CORRADE_COMPARE(a.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(a.blockDataSize(), 16); + CORRADE_COMPARE(a.size(), (Vector2i{15, 10})); CORRADE_COMPARE(static_cast(a.data().data()), data); - CORRADE_COMPARE(a.data().size(), 8); + CORRADE_COMPARE(a.data().size(), 8*16); CORRADE_COMPARE(static_cast(a.mutableData().data()), data); - CORRADE_COMPARE(a.mutableData().size(), 8); + CORRADE_COMPARE(a.mutableData().size(), 8*16); CORRADE_COMPARE(a.importerState(), &state); } } @@ -373,26 +415,51 @@ void ImageDataTest::constructCompressedGeneric() { void ImageDataTest::constructCompressedImplementationSpecific() { /* Format with autodetection */ { - auto data = new char[8]; + auto data = new char[8*8]; int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ - ImageData2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, - Containers::Array{data, 8}, ImageFlag2D::Array, &state}; + ImageData2D a{ + CompressedPixelStorage{}.setRowLength(16), + GL::CompressedPixelFormat::RGBS3tcDxt1, {12, 8}, + Containers::Array{data, 8*8}, ImageFlag2D::Array, &state}; CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_VERIFY(a.isCompressed()); CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.compressedStorage().compressedBlockSize(), Vector3i{4}); + CORRADE_COMPARE(a.compressedStorage().rowLength(), 16); CORRADE_COMPARE(a.compressedFormat(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(a.blockDataSize(), 8); + CORRADE_COMPARE(a.size(), (Vector2i{12, 8})); CORRADE_COMPARE(static_cast(a.data().data()), data); - CORRADE_COMPARE(a.data().size(), 8); + CORRADE_COMPARE(a.data().size(), 8*8); CORRADE_COMPARE(static_cast(a.mutableData().data()), data); - CORRADE_COMPARE(a.mutableData().size(), 8); + CORRADE_COMPARE(a.mutableData().size(), 8*8); CORRADE_COMPARE(a.importerState(), &state); } - /* Manual properties not implemented yet */ + /* Manual block properties */ + { + auto data = new char[6*12]; + int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ + ImageData2D a{ + CompressedPixelStorage{}.setRowLength(6), + 111, {3, 4, 5}, 12, {3, 8}, Containers::Array{data, 6*12}, + ImageFlag2D::Array, &state}; + + CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_VERIFY(a.isCompressed()); + CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); + CORRADE_COMPARE(a.compressedStorage().rowLength(), 6); + CORRADE_COMPARE(a.compressedFormat(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + CORRADE_COMPARE(a.blockSize(), (Vector3i{3, 4, 5})); + CORRADE_COMPARE(a.blockDataSize(), 12); + CORRADE_COMPARE(a.size(), (Vector2i{3, 8})); + CORRADE_COMPARE(static_cast(a.data().data()), data); + CORRADE_COMPARE(a.data().size(), 6*12); + CORRADE_COMPARE(static_cast(a.mutableData().data()), data); + CORRADE_COMPARE(a.mutableData().size(), 6*12); + CORRADE_COMPARE(a.importerState(), &state); + } } void ImageDataTest::constructGenericNotOwned() { @@ -533,42 +600,48 @@ void ImageDataTest::constructCompressedGenericNotOwned() { setTestCaseDescription(instanceData.name); { - char data[8]; - int state; - ImageData2D a{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, + char data[6*8]; + int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ + ImageData2D a{ + CompressedPixelFormat::Bc1RGBAUnorm, {12, 8}, instanceData.dataFlags, data, ImageFlag2D::Array, &state}; CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags); CORRADE_VERIFY(a.isCompressed()); CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.compressedStorage().compressedBlockSize(), Vector3i{0}); + CORRADE_COMPARE(a.compressedStorage().rowLength(), 0); CORRADE_COMPARE(a.compressedFormat(), CompressedPixelFormat::Bc1RGBAUnorm); - CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(a.blockDataSize(), 8); + CORRADE_COMPARE(a.size(), (Vector2i{12, 8})); CORRADE_COMPARE(static_cast(a.data().data()), data); - CORRADE_COMPARE(a.data().size(), 8); + CORRADE_COMPARE(a.data().size(), 6*8); if(instanceData.dataFlags & DataFlag::Mutable) { CORRADE_COMPARE(static_cast(a.mutableData().data()), data); - CORRADE_COMPARE(a.mutableData().size(), 8); + CORRADE_COMPARE(a.mutableData().size(), 6*8); } CORRADE_COMPARE(a.importerState(), &state); } { - char data[8]; - int state; - ImageData2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, + char data[8*16]; + int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ + ImageData2D a{ + CompressedPixelStorage{}.setRowLength(20), + CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, instanceData.dataFlags, data, ImageFlag2D::Array, &state}; CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags); CORRADE_VERIFY(a.isCompressed()); CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.compressedStorage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(a.compressedFormat(), CompressedPixelFormat::Bc1RGBAUnorm); - CORRADE_COMPARE(a.size(), Vector2i(4, 4)); + CORRADE_COMPARE(a.compressedStorage().rowLength(), 20); + CORRADE_COMPARE(a.compressedFormat(), CompressedPixelFormat::Astc5x5x4RGBAF); + CORRADE_COMPARE(a.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(a.blockDataSize(), 16); + CORRADE_COMPARE(a.size(), (Vector2i{15, 10})); CORRADE_COMPARE(static_cast(a.data().data()), data); - CORRADE_COMPARE(a.data().size(), 8); + CORRADE_COMPARE(a.data().size(), 8*16); if(instanceData.dataFlags & DataFlag::Mutable) { CORRADE_COMPARE(static_cast(a.mutableData().data()), data); - CORRADE_COMPARE(a.mutableData().size(), 8); + CORRADE_COMPARE(a.mutableData().size(), 8*16); } CORRADE_COMPARE(a.importerState(), &state); } @@ -580,28 +653,55 @@ void ImageDataTest::constructCompressedImplementationSpecificNotOwned() { /* Format with autodetection */ { - char data[8]; - int state; - ImageData2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, + char data[8*8]; + int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ + ImageData2D a{ + CompressedPixelStorage{}.setRowLength(16), + GL::CompressedPixelFormat::RGBS3tcDxt1, {12, 8}, instanceData.dataFlags, data, ImageFlag2D::Array, &state}; CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags); CORRADE_VERIFY(a.isCompressed()); CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(a.compressedStorage().compressedBlockSize(), Vector3i{4}); + CORRADE_COMPARE(a.compressedStorage().rowLength(), 16); CORRADE_COMPARE(a.compressedFormat(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(a.blockDataSize(), 8); + CORRADE_COMPARE(a.size(), (Vector2i{12, 8})); CORRADE_COMPARE(static_cast(a.data().data()), data); - CORRADE_COMPARE(a.data().size(), 8); + CORRADE_COMPARE(a.data().size(), 8*8); if(instanceData.dataFlags & DataFlag::Mutable) { CORRADE_COMPARE(static_cast(a.mutableData().data()), data); - CORRADE_COMPARE(a.mutableData().size(), 8); + CORRADE_COMPARE(a.mutableData().size(), 8*8); } CORRADE_COMPARE(a.importerState(), &state); } - /* Manual properties not implemented yet */ + /* Manual block properties */ + { + char data[6*12]; + int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ + ImageData2D a{ + CompressedPixelStorage{}.setRowLength(6), + 111, {3, 4, 5}, 12, {3, 8}, + instanceData.dataFlags, data, ImageFlag2D::Array, &state}; + + CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags); + CORRADE_VERIFY(a.isCompressed()); + CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); + CORRADE_COMPARE(a.compressedStorage().rowLength(), 6); + CORRADE_COMPARE(a.compressedFormat(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + CORRADE_COMPARE(a.blockSize(), (Vector3i{3, 4, 5})); + CORRADE_COMPARE(a.blockDataSize(), 12); + CORRADE_COMPARE(a.size(), (Vector2i{3, 8})); + CORRADE_COMPARE(static_cast(a.data().data()), data); + CORRADE_COMPARE(a.data().size(), 6*12); + if(instanceData.dataFlags & DataFlag::Mutable) { + CORRADE_COMPARE(static_cast(a.mutableData().data()), data); + CORRADE_COMPARE(a.mutableData().size(), 6*12); + } + CORRADE_COMPARE(a.importerState(), &state); + } } void ImageDataTest::constructGenericNotOwnedFlagOwned() { @@ -639,9 +739,13 @@ void ImageDataTest::constructCompressedGenericNotOwnedFlagOwned() { Containers::String out; Error redirectError{&out}; - ImageData2D{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, DataFlag::Owned, data}; - ImageData2D{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, DataFlag::Owned, data}; + ImageData2D{ + CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, + DataFlag::Owned, data}; + ImageData2D{ + CompressedPixelStorage{}.setRowLength(4), + CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, + DataFlag::Owned, data}; CORRADE_COMPARE(out, "Trade::ImageData: can't construct a non-owned instance with Trade::DataFlag::Owned\n" "Trade::ImageData: can't construct a non-owned instance with Trade::DataFlag::Owned\n"); @@ -654,8 +758,10 @@ void ImageDataTest::constructCompressedImplementationSpecificNotOwnedFlagOwned() Containers::String out; Error redirectError{&out}; - ImageData2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, DataFlag::Owned, data}; + ImageData2D a{ + CompressedPixelStorage{}.setRowLength(4), + GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, + DataFlag::Owned, data}; CORRADE_COMPARE(out, "Trade::ImageData: can't construct a non-owned instance with Trade::DataFlag::Owned\n"); } @@ -725,10 +831,77 @@ void ImageDataTest::constructInvalidCubeMap() { "Trade::ImageData: expected square faces for a cube map, got {4, 3}\n"); } -void ImageDataTest::constructCompressedInvalidSize() { +void ImageDataTest::constructCompressedUnknownImplementationSpecificBlockSize() { + CORRADE_SKIP_IF_NO_ASSERT(); + + const char data[1]{}; + + Containers::String out; + Error redirectError{&out}; + ImageData2D{compressedPixelFormatWrap(0x666), {1, 1}, Containers::Array{NoInit, 1}}; + ImageData2D{compressedPixelFormatWrap(0x777), {1, 1}, DataFlags{}, data}; + CORRADE_COMPARE_AS(out, + "Trade::ImageData: can't determine block size of an implementation-specific pixel format 0x666, pass it explicitly\n" + /* The next messages are printed because it cannot exit the + construction from the middle of the member initializer list. It does + so with non-graceful asserts tho and just one message is printed. */ + "Trade::ImageData: expected block size to be greater than zero and less than 256 but got {0, 0, 0}\n" + + "Trade::ImageData: can't determine block size of an implementation-specific pixel format 0x777, pass it explicitly\n" + "Trade::ImageData: expected block size to be greater than zero and less than 256 but got {0, 0, 0}\n", + TestSuite::Compare::String); +} + +void ImageDataTest::constructCompressedInvalidBlockSize() { CORRADE_SKIP_IF_NO_ASSERT(); - CORRADE_EXPECT_FAIL("Size checking for compressed image data is not implemented yet."); + /* This is all okay */ + ImageData2D{CompressedPixelStorage{}, 666, {4, 5, 6}, 8, {1, 1}, Containers::Array{NoInit, 8}}; + ImageData2D{CompressedPixelStorage{} + .setCompressedBlockSize({4, 5, 6}) + .setCompressedBlockDataSize(8), + 666, {4, 5, 6}, 8, {1, 1}, Containers::Array{NoInit, 8}}; + + const char data[8]{}; + + /* Tested mainly in ImageViewTest, here is just a subset to verify the same + helper is used internally and a proper prefix is printed */ + Containers::String out; + Error redirectError{&out}; + ImageData2D{CompressedPixelStorage{}, 666, {0, 0, 0}, 4, {1, 1}, nullptr}; + ImageData2D{CompressedPixelStorage{}, 666, {0, 0, 0}, 4, {1, 1}, {}, nullptr}; + ImageData2D{CompressedPixelStorage{}, 666, {4, 4, 4}, 0, {1, 1}, nullptr}; + ImageData2D{CompressedPixelStorage{}, 666, {4, 4, 4}, 0, {1, 1}, {}, nullptr}; + ImageData2D{CompressedPixelStorage{} + .setCompressedBlockSize({5, 5, 5}) + .setCompressedBlockDataSize(8), + 666, {4, 4, 1}, 8, {1, 1}, Containers::Array{NoInit, 8}}; + ImageData2D{CompressedPixelStorage{} + .setCompressedBlockSize({5, 5, 5}) + .setCompressedBlockDataSize(8), + 666, {4, 4, 1}, 8, {1, 1}, {}, data}; + ImageData2D{CompressedPixelStorage{} + .setCompressedBlockSize({4, 4, 1}) + .setCompressedBlockDataSize(4), + 666, {4, 4, 1}, 8, {1, 1}, Containers::Array{NoInit, 8}}; + ImageData2D{CompressedPixelStorage{} + .setCompressedBlockSize({4, 4, 1}) + .setCompressedBlockDataSize(4), + 666, {4, 4, 1}, 8, {1, 1}, {}, data}; + CORRADE_COMPARE_AS(out, + "Trade::ImageData: expected block size to be greater than zero and less than 256 but got {0, 0, 0}\n" + "Trade::ImageData: expected block size to be greater than zero and less than 256 but got {0, 0, 0}\n" + "Trade::ImageData: expected block data size to be non-zero and less than 256 but got 0\n" + "Trade::ImageData: expected block data size to be non-zero and less than 256 but got 0\n" + "Trade::ImageData: expected pixel storage block size to be either not set at all or equal to {4, 4, 1} but got {5, 5, 5}\n" + "Trade::ImageData: expected pixel storage block size to be either not set at all or equal to {4, 4, 1} but got {5, 5, 5}\n" + "Trade::ImageData: expected pixel storage block data size to be either not set at all or equal to 8 but got 4\n" + "Trade::ImageData: expected pixel storage block data size to be either not set at all or equal to 8 but got 4\n", + TestSuite::Compare::String); +} + +void ImageDataTest::constructCompressedInvalidSize() { + CORRADE_SKIP_IF_NO_ASSERT(); /* Too small for given format */ { @@ -856,11 +1029,12 @@ void ImageDataTest::constructMoveImplementationSpecific() { } void ImageDataTest::constructMoveCompressedGeneric() { - auto data = new char[8]; + auto data = new char[8*16]; int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ ImageData2D a{ - CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - CompressedPixelFormat::Bc3RGBAUnorm, {4, 4}, Containers::Array{data, 8}, ImageFlag2D::Array, &state}; + CompressedPixelStorage{}.setRowLength(20), + CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, + Containers::Array{data, 8*16}, ImageFlag2D::Array, &state}; ImageData2D b{Utility::move(a)}; CORRADE_COMPARE(a.data(), static_cast(nullptr)); @@ -869,11 +1043,13 @@ void ImageDataTest::constructMoveCompressedGeneric() { CORRADE_COMPARE(b.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_VERIFY(b.isCompressed()); CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(b.compressedStorage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(b.compressedFormat(), CompressedPixelFormat::Bc3RGBAUnorm); - CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(b.compressedStorage().rowLength(), 20); + CORRADE_COMPARE(b.compressedFormat(), CompressedPixelFormat::Astc5x5x4RGBAF); + CORRADE_COMPARE(b.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(b.blockDataSize(), 16); + CORRADE_COMPARE(b.size(), (Vector2i{15, 10})); CORRADE_COMPARE(b.data(), static_cast(data)); - CORRADE_COMPARE(b.data().size(), 8); + CORRADE_COMPARE(b.data().size(), 8*16); CORRADE_COMPARE(b.importerState(), &state); auto data2 = new char[16]; @@ -886,20 +1062,23 @@ void ImageDataTest::constructMoveCompressedGeneric() { CORRADE_COMPARE(c.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_VERIFY(c.isCompressed()); CORRADE_COMPARE(c.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(c.compressedStorage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(c.compressedFormat(), CompressedPixelFormat::Bc3RGBAUnorm); - CORRADE_COMPARE(c.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(c.compressedStorage().rowLength(), 20); + CORRADE_COMPARE(c.compressedFormat(), CompressedPixelFormat::Astc5x5x4RGBAF); + CORRADE_COMPARE(c.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(c.blockDataSize(), 16); + CORRADE_COMPARE(c.size(), (Vector2i{15, 10})); CORRADE_COMPARE(c.data(), static_cast(data)); - CORRADE_COMPARE(c.data().size(), 8); + CORRADE_COMPARE(c.data().size(), 8*16); CORRADE_COMPARE(c.importerState(), &state); } void ImageDataTest::constructMoveCompressedImplementationSpecific() { - auto data = new char[8]; + auto data = new char[8*16]; int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ ImageData2D a{ - CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, Containers::Array{data, 8}, ImageFlag2D::Array, &state}; + CompressedPixelStorage{}.setRowLength(20), + Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, + Containers::Array{data, 8*16}, ImageFlag2D::Array, &state}; ImageData2D b{Utility::move(a)}; CORRADE_COMPARE(a.data(), static_cast(nullptr)); @@ -908,15 +1087,17 @@ void ImageDataTest::constructMoveCompressedImplementationSpecific() { CORRADE_COMPARE(b.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_VERIFY(b.isCompressed()); CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(b.compressedStorage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(b.compressedFormat(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(b.compressedStorage().rowLength(), 20); + CORRADE_COMPARE(b.compressedFormat(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + CORRADE_COMPARE(b.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(b.blockDataSize(), 16); + CORRADE_COMPARE(b.size(), (Vector2i{15, 10})); CORRADE_COMPARE(b.data(), static_cast(data)); - CORRADE_COMPARE(b.data().size(), 8); + CORRADE_COMPARE(b.data().size(), 8*16); CORRADE_COMPARE(b.importerState(), &state); auto data2 = new char[16]; - ImageData2D c{CompressedPixelFormat::Bc2RGBAUnorm, {8, 4}, Containers::Array{data2, 16}}; + ImageData2D c{CompressedPixelFormat::Bc1RGBAUnorm, {8, 4}, Containers::Array{data2, 16}}; c = Utility::move(b); CORRADE_COMPARE(b.data(), static_cast(data2)); @@ -925,11 +1106,13 @@ void ImageDataTest::constructMoveCompressedImplementationSpecific() { CORRADE_COMPARE(c.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_VERIFY(c.isCompressed()); CORRADE_COMPARE(c.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(c.compressedStorage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(c.compressedFormat(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(c.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(c.compressedStorage().rowLength(), 20); + CORRADE_COMPARE(c.compressedFormat(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + CORRADE_COMPARE(c.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(c.blockDataSize(), 16); + CORRADE_COMPARE(c.size(), (Vector2i{15, 10})); CORRADE_COMPARE(c.data(), static_cast(data)); - CORRADE_COMPARE(c.data().size(), 8); + CORRADE_COMPARE(c.data().size(), 8*16); CORRADE_COMPARE(c.importerState(), &state); } @@ -958,12 +1141,13 @@ void ImageDataTest::constructMoveAttachState() { } void ImageDataTest::constructMoveCompressedAttachState() { - auto data = new char[8]; + auto data = new char[8*8]; /* GCC 11 pointlessly complains that "maybe uninitialized" w/o the {} */ int stateOld{}, stateNew{}; ImageData2D a{ - CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, Containers::Array{data, 8}, ImageFlag2D::Array, &stateOld}; + CompressedPixelStorage{}.setRowLength(16), + GL::CompressedPixelFormat::RGBS3tcDxt1, {12, 8}, + Containers::Array{data, 8*8}, ImageFlag2D::Array, &stateOld}; ImageData2D b{Utility::move(a), &stateNew}; CORRADE_COMPARE(a.data(), static_cast(nullptr)); @@ -972,11 +1156,13 @@ void ImageDataTest::constructMoveCompressedAttachState() { CORRADE_COMPARE(b.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_VERIFY(b.isCompressed()); CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(b.compressedStorage().compressedBlockSize(), Vector3i{4}); + CORRADE_COMPARE(b.compressedStorage().rowLength(), 16); CORRADE_COMPARE(b.compressedFormat(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(b.blockSize(), (Vector3i{4, 4, 1})); + CORRADE_COMPARE(b.blockDataSize(), 8); + CORRADE_COMPARE(b.size(), (Vector2i{12, 8})); CORRADE_COMPARE(b.data(), static_cast(data)); - CORRADE_COMPARE(b.data().size(), 8); + CORRADE_COMPARE(b.data().size(), 8*8); CORRADE_COMPARE(b.importerState(), &stateNew); } @@ -1000,7 +1186,7 @@ void ImageDataTest::moveCompressedToUncompressed() { .setRowLength(2) .setImageHeight(6) .setSkip({0, 1, 0}), - PixelFormat::R8I, 1337, 1, {2, 5}, Containers::Array{data2, 24}, ImageFlag2D(0x80), &state2}; + PixelFormat::R8I, 0x12345678, 1, {2, 5}, Containers::Array{data2, 24}, ImageFlag2D(0x80), &state2}; /* The operation should swap the contents completely, not just partially because one is compressed and the other not */ @@ -1014,7 +1200,7 @@ void ImageDataTest::moveCompressedToUncompressed() { CORRADE_COMPARE(a.storage().imageHeight(), 6); CORRADE_COMPARE(a.storage().skip(), (Vector3i{0, 1, 0})); CORRADE_COMPARE(a.format(), PixelFormat::R8I); - CORRADE_COMPARE(a.formatExtra(), 1337); + CORRADE_COMPARE(a.formatExtra(), 0x12345678); CORRADE_COMPARE(a.pixelSize(), 1); CORRADE_COMPARE(a.size(), (Vector2i{2, 5})); CORRADE_COMPARE(a.data(), static_cast(data2)); @@ -1045,7 +1231,7 @@ void ImageDataTest::moveUncompressedToCompressed() { .setRowLength(2) .setImageHeight(6) .setSkip({0, 1, 0}), - PixelFormat::R8I, 1337, 1, {2, 5}, Containers::Array{data, 24}, ImageFlag2D::Array, &state}; + PixelFormat::R8I, 0x12345678, 1, {2, 5}, Containers::Array{data, 24}, ImageFlag2D::Array, &state}; auto data2 = new char[8*16]; int state2{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ @@ -1084,7 +1270,7 @@ void ImageDataTest::moveUncompressedToCompressed() { CORRADE_COMPARE(b.storage().imageHeight(), 6); CORRADE_COMPARE(b.storage().skip(), (Vector3i{0, 1, 0})); CORRADE_COMPARE(b.format(), PixelFormat::R8I); - CORRADE_COMPARE(b.formatExtra(), 1337); + CORRADE_COMPARE(b.formatExtra(), 0x12345678); CORRADE_COMPARE(b.pixelSize(), 1); CORRADE_COMPARE(b.size(), (Vector2i{2, 5})); CORRADE_COMPARE(b.data(), static_cast(data)); @@ -1103,6 +1289,9 @@ void ImageDataTest::propertiesInvalid() { Error redirectError{&out}; uncompressed.compressedStorage(); uncompressed.compressedFormat(); + uncompressed.blockSize(); + uncompressed.blockDataSize(); + uncompressed.compressedDataProperties(); compressed.storage(); compressed.format(); compressed.formatExtra(); @@ -1117,6 +1306,9 @@ void ImageDataTest::propertiesInvalid() { CORRADE_COMPARE_AS(out, "Trade::ImageData::compressedStorage(): the image is not compressed\n" "Trade::ImageData::compressedFormat(): the image is not compressed\n" + "Trade::ImageData::blockSize(): the image is not compressed\n" + "Trade::ImageData::blockDataSize(): the image is not compressed\n" + "Trade::ImageData::compressedDataProperties(): the image is not compressed\n" "Trade::ImageData::storage(): the image is compressed\n" "Trade::ImageData::format(): the image is compressed\n" "Trade::ImageData::formatExtra(): the image is compressed\n" @@ -1164,35 +1356,41 @@ template void ImageDataTest::toViewImplementationSpecific() { template void ImageDataTest::toViewCompressedGeneric() { setTestCaseTemplateName(MutabilityTraits::name()); - auto data = new char[8]; + auto data = new char[8*16]; typename MutabilityTraits::ImageType a{ - CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - CompressedPixelFormat::Bc1RGBUnorm, {4, 4}, Containers::Array{data, 8}, ImageFlag2D::Array}; + CompressedPixelStorage{}.setRowLength(20), + CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, + Containers::Array{data, 8*16}, ImageFlag2D::Array}; CompressedImageView<2, T> b = a; CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(b.format(), CompressedPixelFormat::Bc1RGBUnorm); - CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(b.storage().rowLength(), 20); + CORRADE_COMPARE(b.format(), CompressedPixelFormat::Astc5x5x4RGBAF); + CORRADE_COMPARE(b.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(b.blockDataSize(), 16); + CORRADE_COMPARE(b.size(), (Vector2i{15, 10})); CORRADE_COMPARE(b.data(), static_cast(data)); - CORRADE_COMPARE(b.data().size(), 8); + CORRADE_COMPARE(b.data().size(), 8*16); } template void ImageDataTest::toViewCompressedImplementationSpecific() { setTestCaseTemplateName(MutabilityTraits::name()); - auto data = new char[8]; + auto data = new char[8*16]; typename MutabilityTraits::ImageType a{ - CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), - GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, Containers::Array{data, 8}, ImageFlag2D::Array}; + CompressedPixelStorage{}.setRowLength(20), + Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, + Containers::Array{data, 8*16}, ImageFlag2D::Array}; CompressedImageView<2, T> b = a; CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); - CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); - CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); - CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); + CORRADE_COMPARE(b.storage().rowLength(), 20); + CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF)); + CORRADE_COMPARE(b.blockSize(), (Vector3i{5, 5, 4})); + CORRADE_COMPARE(b.blockDataSize(), 16); + CORRADE_COMPARE(b.size(), (Vector2i{15, 10})); CORRADE_COMPARE(b.data(), static_cast(data)); - CORRADE_COMPARE(b.data().size(), 8); + CORRADE_COMPARE(b.data().size(), 8*16); } void ImageDataTest::data() { @@ -1214,18 +1412,19 @@ void ImageDataTest::mutableAccessNotAllowed() { CORRADE_SKIP_IF_NO_ASSERT(); const char data[4*4]{}; - ImageData2D a{PixelFormat::RGBA8Unorm, {2, 2}, DataFlags{}, data}; + ImageData2D uncompressed{PixelFormat::RGBA8Unorm, {2, 2}, DataFlags{}, data}; + ImageData2D compressed{CompressedPixelFormat::Bc1RGBAUnorm, {2, 2}, DataFlags{}, data}; Containers::String out; Error redirectError{&out}; - a.mutableData(); - a.mutablePixels(); + uncompressed.mutableData(); + uncompressed.mutablePixels(); /* Can't do just MutableImageView2D(a) because the compiler then treats it as a function. Can't do MutableImageView2D{a} because that doesn't work on old Clang. So it's this mess, then. Sigh. */ - auto b = MutableImageView2D(a); + auto b = MutableImageView2D(uncompressed); static_cast(b); - auto c = MutableCompressedImageView2D(a); + auto c = MutableCompressedImageView2D(compressed); static_cast(c); /* a.mutablePixels() calls non-templated mutablePixels(), so assume there it will blow up correctly as well (can't test because it asserts @@ -1248,6 +1447,18 @@ void ImageDataTest::dataProperties() { (std::pair, Math::Vector3>{{3, 16, 32}, {8, 4, 6}})); } +void ImageDataTest::dataPropertiesCompressed() { + ImageData3D image{ + CompressedPixelStorage{} + .setRowLength(12) + .setImageHeight(8) + .setSkip({8, 4, 4}), + CompressedPixelFormat::Bc1RGBAUnorm, {2, 3, 3}, + Containers::Array{NoInit, 336}}; + CORRADE_COMPARE(image.compressedDataProperties(), + (std::pair, Math::Vector3>{{16, 24, 192}, {3, 2, 3}})); +} + void ImageDataTest::release() { char data[] = {'b', 'e', 'e', 'r'}; Trade::ImageData2D a{PixelFormat::RGBA8Unorm, {1, 1}, Containers::Array{data, 4}};