Browse Source

Store block size properties in compressed images.

Similarly like pixel size is stored in uncompressed images, block size
makes it possible to perform size checks on passed data, slice the images
and so on. It only took over a decade to get that done.

The block properties coming from CompressedPixelStorage are currently
expected to be either not set at all or exactly match what's stored in
the image for given format. The PixelStorage will get eventually
deprecated in favor of a simpler and more flexible representation, but
that's another big chunk of work so it's first done like this.

The GL library tests currently blow up on various assertions and it
isn't yet updated to make use of the known format properties instead of
querying them from GL. That'll be done in the following commits.
pull/680/head
Vladimír Vondruš 10 months ago
parent
commit
1c1ded5869
  1. 26
      src/Magnum/GL/AbstractTexture.cpp
  2. 33
      src/Magnum/GL/BufferImage.cpp
  3. 44
      src/Magnum/GL/BufferImage.h
  4. 8
      src/Magnum/GL/CubeMapTexture.cpp
  5. 17
      src/Magnum/GL/Implementation/imageProperties.h
  6. 291
      src/Magnum/GL/Test/BufferImageGLTest.cpp
  7. 2
      src/Magnum/GL/Test/BufferImageTest.cpp
  8. 34
      src/Magnum/GL/Test/PixelStorageTest.cpp
  9. 43
      src/Magnum/Image.cpp
  10. 113
      src/Magnum/Image.h
  11. 48
      src/Magnum/ImageView.cpp
  12. 181
      src/Magnum/ImageView.h
  13. 62
      src/Magnum/Implementation/ImageProperties.h
  14. 31
      src/Magnum/PixelStorage.h
  15. 316
      src/Magnum/Test/ImageTest.cpp
  16. 419
      src/Magnum/Test/ImageViewTest.cpp
  17. 85
      src/Magnum/Test/PixelStorageTest.cpp
  18. 93
      src/Magnum/Trade/ImageData.cpp
  19. 165
      src/Magnum/Trade/ImageData.h
  20. 429
      src/Magnum/Trade/Test/ImageDataTest.cpp

26
src/Magnum/GL/AbstractTexture.cpp

@ -2174,7 +2174,7 @@ void AbstractTexture::DataHelper<1>::setCompressedImage(AbstractTexture& texture
Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack);
Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); Context::current().state().renderer.applyPixelStorageUnpack(image.storage());
texture.bindInternal(); 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) { 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); image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack);
Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); Context::current().state().renderer.applyPixelStorageUnpack(image.storage());
texture.bindInternal(); 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) { 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) { void AbstractTexture::DataHelper<1>::setCompressedSubImage(AbstractTexture& texture, const GLint level, const Math::Vector<1, GLint>& offset, const CompressedImageView1D& image) {
Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack);
Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); 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) { 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) { void AbstractTexture::DataHelper<1>::setCompressedSubImage(AbstractTexture& texture, const GLint level, const Math::Vector<1, GLint>& offset, CompressedBufferImage1D& image) {
image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack);
Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); 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 #endif
@ -2234,7 +2234,7 @@ void AbstractTexture::DataHelper<2>::setCompressedImage(AbstractTexture& texture
#endif #endif
Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); Context::current().state().renderer.applyPixelStorageUnpack(image.storage());
texture.bindInternal(); 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 #ifndef MAGNUM_TARGET_GLES2
@ -2249,7 +2249,7 @@ void AbstractTexture::DataHelper<2>::setCompressedImage(AbstractTexture& texture
image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack);
Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); Context::current().state().renderer.applyPixelStorageUnpack(image.storage());
texture.bindInternal(); 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 #endif
@ -2270,7 +2270,7 @@ void AbstractTexture::DataHelper<2>::setCompressedSubImage(AbstractTexture& text
Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack);
#endif #endif
Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); 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 #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) { void AbstractTexture::DataHelper<2>::setCompressedSubImage(AbstractTexture& texture, const GLint level, const Vector2i& offset, CompressedBufferImage2D& image) {
image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack);
Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); 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 #endif
@ -2307,9 +2307,9 @@ void AbstractTexture::DataHelper<3>::setCompressedImage(AbstractTexture& texture
Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); Context::current().state().renderer.applyPixelStorageUnpack(image.storage());
texture.bindInternal(); texture.bindInternal();
#ifndef MAGNUM_TARGET_GLES2 #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 #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
} }
#endif #endif
@ -2326,7 +2326,7 @@ void AbstractTexture::DataHelper<3>::setCompressedImage(AbstractTexture& texture
image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack);
Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); Context::current().state().renderer.applyPixelStorageUnpack(image.storage());
texture.bindInternal(); 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 #endif
@ -2348,7 +2348,7 @@ void AbstractTexture::DataHelper<3>::setCompressedSubImage(AbstractTexture& text
Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack);
#endif #endif
Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); 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 #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) { void AbstractTexture::DataHelper<3>::setCompressedSubImage(AbstractTexture& texture, const GLint level, const Vector3i& offset, CompressedBufferImage3D& image) {
image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack);
Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); 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 #endif

33
src/Magnum/GL/BufferImage.cpp

@ -95,23 +95,32 @@ template<UnsignedInt dimensions> void BufferImage<dimensions>::setData(const Pix
} }
template<UnsignedInt dimensions> CompressedBufferImage<dimensions>::CompressedBufferImage(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const Containers::ArrayView<const void> data, const BufferUsage usage): CompressedBufferImage{storage, format, size, Buffer{Buffer::TargetHint::PixelPack}, data.size()} { template<UnsignedInt dimensions> CompressedBufferImage<dimensions>::CompressedBufferImage(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const Containers::ArrayView<const void> 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); _buffer.setData(data, usage);
} }
template<UnsignedInt dimensions> CompressedBufferImage<dimensions>::CompressedBufferImage(const CompressedPixelStorage storage, const Magnum::CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const Containers::ArrayView<const void> data, const BufferUsage usage): CompressedBufferImage{storage, compressedPixelFormat(format), size, data, usage} {} template<UnsignedInt dimensions> CompressedBufferImage<dimensions>::CompressedBufferImage(const CompressedPixelStorage storage, const Magnum::CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const Containers::ArrayView<const void> data, const BufferUsage usage): CompressedBufferImage{storage, compressedPixelFormat(format), size, data, usage} {}
template<UnsignedInt dimensions> CompressedBufferImage<dimensions>::CompressedBufferImage(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Buffer&& buffer, const std::size_t dataSize) noexcept: _storage{storage}, _format{format}, _size{size}, _buffer{Utility::move(buffer)}, _dataSize{dataSize} {} template<UnsignedInt dimensions> CompressedBufferImage<dimensions>::CompressedBufferImage(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& 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<UnsignedInt dimensions> CompressedBufferImage<dimensions>::CompressedBufferImage(const CompressedPixelStorage storage, const Magnum::CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Buffer&& buffer, const std::size_t dataSize) noexcept: CompressedBufferImage{storage, compressedPixelFormat(format), size, Utility::move(buffer), dataSize} {} template<UnsignedInt dimensions> CompressedBufferImage<dimensions>::CompressedBufferImage(const CompressedPixelStorage storage, const Magnum::CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Buffer&& buffer, const std::size_t dataSize) noexcept: CompressedBufferImage{storage, compressedPixelFormat(format), size, Utility::move(buffer), dataSize} {}
template<UnsignedInt dimensions> CompressedBufferImage<dimensions>::CompressedBufferImage(const CompressedPixelStorage storage): _storage{storage}, _format{}, _buffer{Buffer::TargetHint::PixelPack}, _dataSize{} { template<UnsignedInt dimensions> CompressedBufferImage<dimensions>::CompressedBufferImage(const CompressedPixelStorage storage): _storage{storage}, _format{}, _blockDataSize{}, _buffer{Buffer::TargetHint::PixelPack}, _dataSize{} {
/* Not delegating to the (buffer&&, dataSize) constructor to avoid a size CORRADE_ASSERT(storage.compressedBlockSize() == Vector3i{},
assertion that'd happen with certain storage parameters */ "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<UnsignedInt dimensions> CompressedBufferImage<dimensions>::CompressedBufferImage(NoCreateT) noexcept: _format{}, _buffer{NoCreate}, _dataSize{} {} template<UnsignedInt dimensions> CompressedBufferImage<dimensions>::CompressedBufferImage(NoCreateT) noexcept: _format{}, _blockDataSize{}, _buffer{NoCreate}, _dataSize{} {}
template<UnsignedInt dimensions> CompressedBufferImage<dimensions>::CompressedBufferImage(CompressedBufferImage<dimensions>&& 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<UnsignedInt dimensions> CompressedBufferImage<dimensions>::CompressedBufferImage(CompressedBufferImage<dimensions>&& 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._size = {};
other._dataSize = {}; other._dataSize = {};
} }
@ -120,6 +129,8 @@ template<UnsignedInt dimensions> CompressedBufferImage<dimensions>& CompressedBu
using Utility::swap; using Utility::swap;
swap(_storage, other._storage); swap(_storage, other._storage);
swap(_format, other._format); swap(_format, other._format);
swap(_blockSize, other._blockSize);
swap(_blockDataSize, other._blockDataSize);
swap(_size, other._size); swap(_size, other._size);
swap(_buffer, other._buffer); swap(_buffer, other._buffer);
swap(_dataSize, other._dataSize); swap(_dataSize, other._dataSize);
@ -133,10 +144,18 @@ template<UnsignedInt dimensions> std::pair<VectorTypeFor<dimensions, std::size_t
template<UnsignedInt dimensions> void CompressedBufferImage<dimensions>::setData(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const Containers::ArrayView<const void> data, const BufferUsage usage) { template<UnsignedInt dimensions> void CompressedBufferImage<dimensions>::setData(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const Containers::ArrayView<const void> data, const BufferUsage usage) {
_storage = storage; _storage = storage;
_format = format; _format = format;
_blockSize = Vector3ub(compressedPixelFormatBlockSize(format));
_blockDataSize = UnsignedByte(compressedPixelFormatBlockDataSize(format));
_size = size; _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 */ /* 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); _buffer.setData(data, usage);
_dataSize = data.size(); _dataSize = data.size();
} }

44
src/Magnum/GL/BufferImage.h

@ -454,6 +454,12 @@ template<UnsignedInt dimensions> class CompressedBufferImage {
* @param data Image data * @param data Image data
* @param usage Image buffer usage * @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} * @requires_gl42 Extension @gl_extension{ARB,compressed_texture_pixel_storage}
* for non-default @ref CompressedPixelStorage * for non-default @ref CompressedPixelStorage
* @requires_gl Non-default @ref CompressedPixelStorage is not * @requires_gl Non-default @ref CompressedPixelStorage is not
@ -507,6 +513,12 @@ template<UnsignedInt dimensions> class CompressedBufferImage {
* @param buffer Buffer * @param buffer Buffer
* @param dataSize Buffer data size * @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 * If @p dataSize is @cpp 0 @ce, the buffer is unconditionally
* reallocated on the first call to @ref setData(). * reallocated on the first call to @ref setData().
* @requires_gl42 Extension @gl_extension{ARB,compressed_texture_pixel_storage} * @requires_gl42 Extension @gl_extension{ARB,compressed_texture_pixel_storage}
@ -558,8 +570,12 @@ template<UnsignedInt dimensions> class CompressedBufferImage {
* @brief Construct an image placeholder * @brief Construct an image placeholder
* @param storage Storage of compressed pixel data * @param storage Storage of compressed pixel data
* *
* Format is undefined, size is zero and buffer is empty, call * Format and block properties are undefined, size is zero and buffer
* @ref setData() to fill the image with data. * 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} * @requires_gl42 Extension @gl_extension{ARB,compressed_texture_pixel_storage}
* for non-default @ref CompressedPixelStorage * for non-default @ref CompressedPixelStorage
* @requires_gl Non-default @ref CompressedPixelStorage is not * @requires_gl Non-default @ref CompressedPixelStorage is not
@ -608,6 +624,25 @@ template<UnsignedInt dimensions> class CompressedBufferImage {
/** @brief Format of compressed pixel data */ /** @brief Format of compressed pixel data */
CompressedPixelFormat format() const { return _format; } 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 */ /** @brief Image size */
VectorTypeFor<Dimensions, Int> size() const { return _size; } VectorTypeFor<Dimensions, Int> size() const { return _size; }
@ -706,6 +741,11 @@ template<UnsignedInt dimensions> class CompressedBufferImage {
private: private:
CompressedPixelStorage _storage; CompressedPixelStorage _storage;
CompressedPixelFormat _format; 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<Dimensions, Int> _size; Math::Vector<Dimensions, Int> _size;
Buffer _buffer; Buffer _buffer;
std::size_t _dataSize; std::size_t _dataSize;

8
src/Magnum/GL/CubeMapTexture.cpp

@ -561,7 +561,7 @@ CubeMapTexture& CubeMapTexture::setCompressedSubImage(const Int level, const Vec
Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack);
Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); 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; return *this;
} }
@ -572,7 +572,7 @@ CubeMapTexture& CubeMapTexture::setCompressedSubImage(const Int level, const Vec
image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack);
Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); 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; return *this;
} }
#endif #endif
@ -604,7 +604,7 @@ CubeMapTexture& CubeMapTexture::setCompressedSubImage(const CubeMapCoordinate co
Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack); Buffer::unbindInternal(Buffer::TargetHint::PixelUnpack);
#endif #endif
Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); 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; 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) { CubeMapTexture& CubeMapTexture::setCompressedSubImage(const CubeMapCoordinate coordinate, const Int level, const Vector2i& offset, CompressedBufferImage2D& image) {
image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack); image.buffer().bindInternal(Buffer::TargetHint::PixelUnpack);
Context::current().state().renderer.applyPixelStorageUnpack(image.storage()); 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; return *this;
} }
#endif #endif

17
src/Magnum/GL/Implementation/imageProperties.h

@ -26,7 +26,7 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#include "Magnum/PixelStorage.h" #include "Magnum/Math/Vector3.h"
namespace Magnum { namespace GL { namespace Implementation { 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. ARB_compressed_texture_pixel_storage (which makes skip, row length etc.
possible for compressed formats) didn't bother thinking about what the possible for compressed formats) didn't bother thinking about what the
existing parameter is for, just left it unchanged, and nobody else in the existing parameter is for, just left it unchanged, and nobody else in the
commitee bothered either. commitee bothered either. */
template<class T> std::size_t occupiedCompressedImageDataSize(const T& image) {
const auto realBlockCount = Math::Vector3<std::size_t>{(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 return realBlockCount.product()*image.blockDataSize();
used as a backup, which might still be correct in most cases. */
template<class T> std::size_t occupiedCompressedImageDataSize(const T& image, std::size_t dataSize) {
if(image.storage().compressedBlockSize().product() && image.storage().compressedBlockDataSize()) {
const auto realBlockCount = Math::Vector3<std::size_t>{(Vector3i::pad(image.size(), 1) + image.storage().compressedBlockSize() - Vector3i{1})/image.storage().compressedBlockSize()};
return realBlockCount.product()*image.storage().compressedBlockDataSize();
}
return dataSize;
} }
}}} }}}

291
src/Magnum/GL/Test/BufferImageGLTest.cpp

@ -52,6 +52,7 @@ struct BufferImageGLTest: OpenGLTester {
void constructBufferCompressedGeneric(); void constructBufferCompressedGeneric();
void constructInvalidSize(); void constructInvalidSize();
void constructCompressedInvalidBlockSize();
void constructCompressedInvalidSize(); void constructCompressedInvalidSize();
void constructMove(); void constructMove();
@ -67,6 +68,7 @@ struct BufferImageGLTest: OpenGLTester {
void setDataCompressedGeneric(); void setDataCompressedGeneric();
void setDataCompressedKeepStorage(); void setDataCompressedKeepStorage();
void setDataInvalidSize(); void setDataInvalidSize();
void setDataCompressedInvalidBlockSize();
void setDataCompressedInvalidSize(); void setDataCompressedInvalidSize();
void release(); void release();
@ -86,6 +88,7 @@ BufferImageGLTest::BufferImageGLTest() {
&BufferImageGLTest::constructBufferCompressedGeneric, &BufferImageGLTest::constructBufferCompressedGeneric,
&BufferImageGLTest::constructInvalidSize, &BufferImageGLTest::constructInvalidSize,
&BufferImageGLTest::constructCompressedInvalidBlockSize,
&BufferImageGLTest::constructCompressedInvalidSize, &BufferImageGLTest::constructCompressedInvalidSize,
&BufferImageGLTest::constructMove, &BufferImageGLTest::constructMove,
@ -101,6 +104,7 @@ BufferImageGLTest::BufferImageGLTest() {
&BufferImageGLTest::setDataCompressedGeneric, &BufferImageGLTest::setDataCompressedGeneric,
&BufferImageGLTest::setDataCompressedKeepStorage, &BufferImageGLTest::setDataCompressedKeepStorage,
&BufferImageGLTest::setDataInvalidSize, &BufferImageGLTest::setDataInvalidSize,
&BufferImageGLTest::setDataCompressedInvalidBlockSize,
&BufferImageGLTest::setDataCompressedInvalidSize, &BufferImageGLTest::setDataCompressedInvalidSize,
&BufferImageGLTest::release, &BufferImageGLTest::release,
@ -188,13 +192,16 @@ void BufferImageGLTest::constructPlaceholder() {
} }
void BufferImageGLTest::constructCompressed() { 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{ CompressedBufferImage2D a{
#ifndef MAGNUM_TARGET_GLES CompressedPixelStorage{}.setRowLength(16),
CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}),
#endif
CompressedPixelFormat::RGBAS3tcDxt1, CompressedPixelFormat::RGBAS3tcDxt1,
{4, 4}, data, BufferUsage::StaticDraw}; {12, 8}, data, BufferUsage::StaticDraw};
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
const auto imageData = a.buffer().data(); const auto imageData = a.buffer().data();
@ -202,12 +209,12 @@ void BufferImageGLTest::constructCompressed() {
MAGNUM_VERIFY_NO_GL_ERROR(); MAGNUM_VERIFY_NO_GL_ERROR();
#ifndef MAGNUM_TARGET_GLES CORRADE_COMPARE(a.storage().rowLength(), 16);
CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4});
#endif
CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt1); CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt1);
CORRADE_COMPARE(a.size(), Vector2i(4, 4)); CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1}));
CORRADE_COMPARE(a.dataSize(), 8); 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? */ /** @todo How to verify the contents in ES? */
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
@ -217,13 +224,16 @@ void BufferImageGLTest::constructCompressed() {
} }
void BufferImageGLTest::constructCompressedGeneric() { 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{ CompressedBufferImage2D a{
#ifndef MAGNUM_TARGET_GLES CompressedPixelStorage{}.setRowLength(16),
CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}),
#endif
Magnum::CompressedPixelFormat::Bc1RGBAUnorm, Magnum::CompressedPixelFormat::Bc1RGBAUnorm,
{4, 4}, data, BufferUsage::StaticDraw}; {12, 8}, data, BufferUsage::StaticDraw};
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
const auto imageData = a.buffer().data(); const auto imageData = a.buffer().data();
@ -231,12 +241,12 @@ void BufferImageGLTest::constructCompressedGeneric() {
MAGNUM_VERIFY_NO_GL_ERROR(); MAGNUM_VERIFY_NO_GL_ERROR();
#ifndef MAGNUM_TARGET_GLES CORRADE_COMPARE(a.storage().rowLength(), 16);
CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4});
#endif
CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt1); CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt1);
CORRADE_COMPARE(a.size(), Vector2i(4, 4)); CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1}));
CORRADE_COMPARE(a.dataSize(), 8); 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? */ /** @todo How to verify the contents in ES? */
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
@ -251,6 +261,8 @@ void BufferImageGLTest::constructCompressedPlaceholder() {
CORRADE_COMPARE(a.storage().rowLength(), 0); CORRADE_COMPARE(a.storage().rowLength(), 0);
CORRADE_COMPARE(a.format(), CompressedPixelFormat{}); CORRADE_COMPARE(a.format(), CompressedPixelFormat{});
CORRADE_COMPARE(a.blockSize(), Vector3i{});
CORRADE_COMPARE(a.blockDataSize(), 0);
CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(a.size(), Vector2i{});
CORRADE_COMPARE(a.dataSize(), 0); CORRADE_COMPARE(a.dataSize(), 0);
CORRADE_VERIFY(a.buffer().id()); 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().skip(), (Vector3i{1, 0, 0}));
CORRADE_COMPARE(a.storage().rowLength(), 12); CORRADE_COMPARE(a.storage().rowLength(), 12);
CORRADE_COMPARE(a.format(), CompressedPixelFormat{}); CORRADE_COMPARE(a.format(), CompressedPixelFormat{});
CORRADE_COMPARE(a.blockSize(), Vector3i{});
CORRADE_COMPARE(a.blockDataSize(), 0);
CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(a.size(), Vector2i{});
CORRADE_COMPARE(a.dataSize(), 0); CORRADE_COMPARE(a.dataSize(), 0);
CORRADE_VERIFY(a.buffer().id()); CORRADE_VERIFY(a.buffer().id());
@ -334,17 +348,20 @@ void BufferImageGLTest::constructBufferGeneric() {
} }
void BufferImageGLTest::constructBufferCompressed() { 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 buffer;
buffer.setData(data, BufferUsage::StaticDraw); buffer.setData(data, BufferUsage::StaticDraw);
const UnsignedInt id = buffer.id(); const UnsignedInt id = buffer.id();
CompressedBufferImage2D a{ CompressedBufferImage2D a{
#ifndef MAGNUM_TARGET_GLES CompressedPixelStorage{}.setRowLength(16),
CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}),
#endif
CompressedPixelFormat::RGBAS3tcDxt1, CompressedPixelFormat::RGBAS3tcDxt1,
{4, 4}, Utility::move(buffer), sizeof(data)}; {12, 8}, Utility::move(buffer), sizeof(data)};
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
const auto imageData = a.buffer().data(); const auto imageData = a.buffer().data();
@ -352,14 +369,14 @@ void BufferImageGLTest::constructBufferCompressed() {
MAGNUM_VERIFY_NO_GL_ERROR(); MAGNUM_VERIFY_NO_GL_ERROR();
#ifndef MAGNUM_TARGET_GLES CORRADE_COMPARE(a.storage().rowLength(), 16);
CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4});
#endif
CORRADE_VERIFY(!buffer.id()); CORRADE_VERIFY(!buffer.id());
CORRADE_COMPARE(a.buffer().id(), id); CORRADE_COMPARE(a.buffer().id(), id);
CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt1); CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt1);
CORRADE_COMPARE(a.size(), Vector2i(4, 4)); CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1}));
CORRADE_COMPARE(a.dataSize(), 8); 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? */ /** @todo How to verify the contents in ES? */
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
@ -369,17 +386,20 @@ void BufferImageGLTest::constructBufferCompressed() {
} }
void BufferImageGLTest::constructBufferCompressedGeneric() { 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 buffer;
buffer.setData(data, BufferUsage::StaticDraw); buffer.setData(data, BufferUsage::StaticDraw);
const UnsignedInt id = buffer.id(); const UnsignedInt id = buffer.id();
CompressedBufferImage2D a{ CompressedBufferImage2D a{
#ifndef MAGNUM_TARGET_GLES CompressedPixelStorage{}.setRowLength(16),
CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}),
#endif
Magnum::CompressedPixelFormat::Bc1RGBAUnorm, Magnum::CompressedPixelFormat::Bc1RGBAUnorm,
{4, 4}, Utility::move(buffer), sizeof(data)}; {12, 8}, Utility::move(buffer), sizeof(data)};
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
const auto imageData = a.buffer().data(); const auto imageData = a.buffer().data();
@ -387,14 +407,14 @@ void BufferImageGLTest::constructBufferCompressedGeneric() {
MAGNUM_VERIFY_NO_GL_ERROR(); MAGNUM_VERIFY_NO_GL_ERROR();
#ifndef MAGNUM_TARGET_GLES CORRADE_COMPARE(a.storage().rowLength(), 16);
CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4});
#endif
CORRADE_VERIFY(!buffer.id()); CORRADE_VERIFY(!buffer.id());
CORRADE_COMPARE(a.buffer().id(), id); CORRADE_COMPARE(a.buffer().id(), id);
CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt1); CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt1);
CORRADE_COMPARE(a.size(), Vector2i(4, 4)); CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1}));
CORRADE_COMPARE(a.dataSize(), 8); 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? */ /** @todo How to verify the contents in ES? */
#ifndef MAGNUM_TARGET_GLES #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"); 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_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 */ /* Too small for given format */
{ {
@ -488,8 +541,16 @@ void BufferImageGLTest::constructMove() {
} }
void BufferImageGLTest::constructMoveCompressed() { void BufferImageGLTest::constructMoveCompressed() {
const char data[] = { 'a', 0, 0, 0, 'b', 0, 0, 0 }; const char data[]{
CompressedBufferImage2D a{CompressedPixelFormat::RGBAS3tcDxt1, {4, 4}, data, BufferUsage::StaticDraw}; '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(); const Int id = a.buffer().id();
MAGNUM_VERIFY_NO_GL_ERROR(); MAGNUM_VERIFY_NO_GL_ERROR();
@ -501,20 +562,20 @@ void BufferImageGLTest::constructMoveCompressed() {
CORRADE_COMPARE(a.size(), Vector2i()); CORRADE_COMPARE(a.size(), Vector2i());
CORRADE_COMPARE(a.dataSize(), 0); CORRADE_COMPARE(a.dataSize(), 0);
#ifndef MAGNUM_TARGET_GLES CORRADE_COMPARE(b.storage().rowLength(), 16);
CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{0});
#endif
CORRADE_COMPARE(b.format(), CompressedPixelFormat::RGBAS3tcDxt1); CORRADE_COMPARE(b.format(), CompressedPixelFormat::RGBAS3tcDxt1);
CORRADE_COMPARE(b.size(), Vector2i(4, 4)); CORRADE_COMPARE(b.blockSize(), (Vector3i{4, 4, 1}));
CORRADE_COMPARE(b.dataSize(), 8); CORRADE_COMPARE(b.blockDataSize(), 8);
CORRADE_COMPARE(b.size(), (Vector2i{12, 8}));
CORRADE_COMPARE(b.dataSize(), 8*8);
CORRADE_COMPARE(b.buffer().id(), id); 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{ CompressedBufferImage2D c{
#ifndef MAGNUM_TARGET_GLES CompressedPixelFormat::RGBAAstc5x5, {5, 5},
CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), data2, BufferUsage::StaticDraw};
#endif
CompressedPixelFormat::RGBAS3tcDxt1, {8, 4}, data2, BufferUsage::StaticDraw};
const Int cId = c.buffer().id(); const Int cId = c.buffer().id();
c = Utility::move(b); c = Utility::move(b);
@ -522,15 +583,17 @@ void BufferImageGLTest::constructMoveCompressed() {
CORRADE_VERIFY(cId > 0); CORRADE_VERIFY(cId > 0);
CORRADE_COMPARE(b.buffer().id(), cId); 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); CORRADE_COMPARE(b.dataSize(), 16);
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
CORRADE_COMPARE(c.storage().compressedBlockSize(), Vector3i{0}); CORRADE_COMPARE(c.storage().compressedBlockSize(), Vector3i{0});
#endif #endif
CORRADE_COMPARE(c.format(), CompressedPixelFormat::RGBAS3tcDxt1); CORRADE_COMPARE(c.format(), CompressedPixelFormat::RGBAS3tcDxt1);
CORRADE_COMPARE(c.size(), Vector2i(4, 4)); CORRADE_COMPARE(c.blockSize(), (Vector3i{4, 4, 1}));
CORRADE_COMPARE(c.dataSize(), 8); 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_COMPARE(c.buffer().id(), id);
CORRADE_VERIFY(std::is_nothrow_move_constructible<CompressedBufferImage2D>::value); CORRADE_VERIFY(std::is_nothrow_move_constructible<CompressedBufferImage2D>::value);
@ -549,17 +612,16 @@ void BufferImageGLTest::dataProperties() {
} }
void BufferImageGLTest::dataPropertiesCompressed() { void BufferImageGLTest::dataPropertiesCompressed() {
/* Yes, I know, this is totally bogus and doesn't match the BC1 format */ const char data[336]{};
const char data[1]{};
CompressedBufferImage3D image{ CompressedBufferImage3D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({3, 4, 5}) .setRowLength(12)
.setCompressedBlockDataSize(16) .setImageHeight(8)
.setImageHeight(12) .setSkip({8, 4, 4}),
.setSkip({5, 8, 11}), CompressedPixelFormat::RGBAS3tcDxt1, {2, 3, 3},
Magnum::CompressedPixelFormat::Bc1RGBAUnorm, {2, 8, 11}, data, BufferUsage::StaticDraw}; data, BufferUsage::StaticDraw};
CORRADE_COMPARE(image.dataProperties(), CORRADE_COMPARE(image.dataProperties(),
(std::pair<Math::Vector3<std::size_t>, Math::Vector3<std::size_t>>{{2*16, 2*16, 9*16}, {1, 3, 3}})); (std::pair<Math::Vector3<std::size_t>, Math::Vector3<std::size_t>>{{16, 24, 192}, {3, 2, 3}}));
} }
void BufferImageGLTest::setData() { void BufferImageGLTest::setData() {
@ -654,15 +716,21 @@ void BufferImageGLTest::setDataKeepStorage() {
} }
void BufferImageGLTest::setDataCompressed() { void BufferImageGLTest::setDataCompressed() {
const char data[] = { 'a', 0, 0, 0, 'b', 0, 0, 0 }; const char data[]{
CompressedBufferImage2D a{CompressedPixelFormat::RGBAS3tcDxt1, {4, 4}, data, BufferUsage::StaticDraw}; '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( a.setData(
#ifndef MAGNUM_TARGET_GLES CompressedPixelStorage{}.setRowLength(16),
CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedPixelFormat::RGBAS3tcDxt1,
#endif {12, 8}, data2, BufferUsage::StaticDraw);
CompressedPixelFormat::RGBAS3tcDxt3, {8, 4}, data2, BufferUsage::StaticDraw);
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
const auto imageData = a.buffer().data(); const auto imageData = a.buffer().data();
@ -670,12 +738,12 @@ void BufferImageGLTest::setDataCompressed() {
MAGNUM_VERIFY_NO_GL_ERROR(); MAGNUM_VERIFY_NO_GL_ERROR();
#ifndef MAGNUM_TARGET_GLES CORRADE_COMPARE(a.storage().rowLength(), 16);
CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt1);
#endif CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1}));
CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt3); CORRADE_COMPARE(a.blockDataSize(), 8);
CORRADE_COMPARE(a.size(), Vector2i(8, 4)); CORRADE_COMPARE(a.size(), (Vector2i{12, 8}));
CORRADE_COMPARE(a.dataSize(), 16); CORRADE_COMPARE(a.dataSize(), 8*8);
/** @todo How to verify the contents in ES? */ /** @todo How to verify the contents in ES? */
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
@ -685,15 +753,21 @@ void BufferImageGLTest::setDataCompressed() {
} }
void BufferImageGLTest::setDataCompressedGeneric() { void BufferImageGLTest::setDataCompressedGeneric() {
const char data[] = { 'a', 0, 0, 0, 'b', 0, 0, 0 }; const char data[]{
CompressedBufferImage2D a{CompressedPixelFormat::RGBAS3tcDxt1, {4, 4}, data, BufferUsage::StaticDraw}; '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( a.setData(
#ifndef MAGNUM_TARGET_GLES CompressedPixelStorage{}.setRowLength(16),
CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), Magnum::CompressedPixelFormat::Bc1RGBAUnorm,
#endif {12, 8}, data2, BufferUsage::StaticDraw);
Magnum::CompressedPixelFormat::Bc2RGBAUnorm, {8, 4}, data2, BufferUsage::StaticDraw);
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
const auto imageData = a.buffer().data(); const auto imageData = a.buffer().data();
@ -701,12 +775,12 @@ void BufferImageGLTest::setDataCompressedGeneric() {
MAGNUM_VERIFY_NO_GL_ERROR(); MAGNUM_VERIFY_NO_GL_ERROR();
#ifndef MAGNUM_TARGET_GLES CORRADE_COMPARE(a.storage().rowLength(), 16);
CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt1);
#endif CORRADE_COMPARE(a.blockSize(), (Vector3i{4, 4, 1}));
CORRADE_COMPARE(a.format(), CompressedPixelFormat::RGBAS3tcDxt3); CORRADE_COMPARE(a.blockDataSize(), 8);
CORRADE_COMPARE(a.size(), Vector2i(8, 4)); CORRADE_COMPARE(a.size(), (Vector2i{12, 8}));
CORRADE_COMPARE(a.dataSize(), 16); CORRADE_COMPARE(a.dataSize(), 8*8);
/** @todo How to verify the contents in ES? */ /** @todo How to verify the contents in ES? */
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
@ -763,6 +837,39 @@ void BufferImageGLTest::setDataInvalidSize() {
TestSuite::Compare::String); 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() { void BufferImageGLTest::setDataCompressedInvalidSize() {
CORRADE_SKIP_IF_NO_ASSERT(); CORRADE_SKIP_IF_NO_ASSERT();
@ -770,8 +877,6 @@ void BufferImageGLTest::setDataCompressedInvalidSize() {
CompressedBufferImage2D a{CompressedPixelFormat::RGBAS3tcDxt1, {4, 4}, "helloheyhellhe", BufferUsage::StaticDraw}; CompressedBufferImage2D a{CompressedPixelFormat::RGBAS3tcDxt1, {4, 4}, "helloheyhellhe", BufferUsage::StaticDraw};
CORRADE_COMPARE(a.dataSize(), 15); CORRADE_COMPARE(a.dataSize(), 15);
CORRADE_EXPECT_FAIL("Size checking for compressed image data is not implemented yet.");
/* Too small for given format */ /* Too small for given format */
{ {
Containers::String out; Containers::String out;

2
src/Magnum/GL/Test/BufferImageTest.cpp

@ -70,6 +70,8 @@ void BufferImageTest::constructNoCreateCompressed() {
CORRADE_COMPARE(image.buffer().id(), 0); CORRADE_COMPARE(image.buffer().id(), 0);
CORRADE_COMPARE(image.size(), Vector2i{}); CORRADE_COMPARE(image.size(), Vector2i{});
CORRADE_COMPARE(image.format(), CompressedPixelFormat{}); CORRADE_COMPARE(image.format(), CompressedPixelFormat{});
CORRADE_COMPARE(image.blockSize(), Vector3i{});
CORRADE_COMPARE(image.blockDataSize(), 0);
CORRADE_COMPARE(image.dataSize(), 0); CORRADE_COMPARE(image.dataSize(), 0);
} }

34
src/Magnum/GL/Test/PixelStorageTest.cpp

@ -48,51 +48,37 @@ void PixelStorageTest::occupiedCompressedImageDataSize() {
being 55x28x12 */ being 55x28x12 */
const char data[(55/5)*(28/4)*(12/2)*16]{}; const char data[(55/5)*(28/4)*(12/2)*16]{};
/* If we have no block properties, the passed data size is taken, assuming /* The size is calculated from block properties and the *image* size, not
it's the best bet. Image size or row length and image height isn't taken the supplied row length / image height. This is what GL wants, it has no
into account in any way. */ relation to anything useful. For comparison see the PixelStorageTest::dataOffsetSizeCompressed() test in the core
{
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
library. */ library. */
} { {
CompressedImageView3D image{ CompressedImageView3D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({5, 4, 2})
.setCompressedBlockDataSize(16)
.setRowLength(55) .setRowLength(55)
.setImageHeight(28) .setImageHeight(28)
.setSkip({10, 8, 4}), .setSkip({10, 8, 4}),
42069, /* custom format */ 42069, /* custom format */
{5, 4, 2},
16,
{35, 20, 6}, {35, 20, 6},
data}; data};
CORRADE_COMPARE(Implementation::occupiedCompressedImageDataSize(image, 1337), CORRADE_COMPARE(Implementation::occupiedCompressedImageDataSize(image),
(35/5)*(20/4)*(6/2)*16); (35/5)*(20/4)*(6/2)*16);
/* Same result if the size isn't whole blocks */ /* Same result if the size isn't whole blocks */
} { } {
CompressedImageView3D image{ CompressedImageView3D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({5, 4, 2})
.setCompressedBlockDataSize(16)
.setRowLength(55) .setRowLength(55)
.setImageHeight(28) .setImageHeight(28)
.setSkip({10, 8, 4}), .setSkip({10, 8, 4}),
42069, /* custom format */ 42069, /* custom format */
{5, 4, 2},
16,
{31, 19, 5}, {31, 19, 5},
data}; data};
CORRADE_COMPARE(Implementation::occupiedCompressedImageDataSize(image, 1337), CORRADE_COMPARE(Implementation::occupiedCompressedImageDataSize(image),
(35/5)*(20/4)*(6/2)*16); (35/5)*(20/4)*(6/2)*16);
} }
} }

43
src/Magnum/Image.cpp

@ -96,18 +96,45 @@ template<UnsignedInt dimensions> Containers::Array<char> Image<dimensions>::rele
return data; return data;
} }
template<UnsignedInt dimensions> CompressedImage<dimensions>::CompressedImage(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const ImageFlags<dimensions> flags) noexcept: _storage{storage}, _format{format}, _flags{flags}, _size{size}, _data{Utility::move(data)} { template<UnsignedInt dimensions> CompressedImage<dimensions>::CompressedImage(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const ImageFlags<dimensions> 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 #ifndef CORRADE_NO_ASSERT
isCompressedPixelFormatImplementationSpecific(format) ? 0 :
#endif
compressedPixelFormatBlockDataSize(format)
), size, Utility::move(data), flags} {}
template<UnsignedInt dimensions> CompressedImage<dimensions>::CompressedImage(const CompressedPixelStorage storage, const CompressedPixelFormat format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const ImageFlags<dimensions> 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<void>(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); Implementation::checkImageFlagsForSize("CompressedImage:", flags, size);
#endif #endif
} }
template<UnsignedInt dimensions> CompressedImage<dimensions>::CompressedImage(const CompressedPixelStorage storage, const UnsignedInt format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const ImageFlags<dimensions> flags) noexcept: CompressedImage{storage, compressedPixelFormatWrap(format), size, Utility::move(data), flags} {} template<UnsignedInt dimensions> CompressedImage<dimensions>::CompressedImage(const CompressedPixelStorage storage, const UnsignedInt format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const ImageFlags<dimensions> flags) noexcept: CompressedImage{storage, compressedPixelFormatWrap(format), blockSize, blockDataSize, size, Utility::move(data), flags} {}
template<UnsignedInt dimensions> CompressedImage<dimensions>::CompressedImage(const CompressedPixelStorage storage) noexcept: _storage{storage}, _format{} {} template<UnsignedInt dimensions> CompressedImage<dimensions>::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<UnsignedInt dimensions> CompressedImage<dimensions>::CompressedImage(CompressedImage<dimensions>&& 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<UnsignedInt dimensions> CompressedImage<dimensions>::CompressedImage(CompressedImage<dimensions>&& 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 = {}; other._size = {};
} }
@ -116,17 +143,19 @@ template<UnsignedInt dimensions> CompressedImage<dimensions>& CompressedImage<di
swap(_storage, other._storage); swap(_storage, other._storage);
swap(_format, other._format); swap(_format, other._format);
swap(_flags, other._flags); swap(_flags, other._flags);
swap(_blockSize, other._blockSize);
swap(_blockDataSize, other._blockDataSize);
swap(_size, other._size); swap(_size, other._size);
swap(_data, other._data); swap(_data, other._data);
return *this; return *this;
} }
template<UnsignedInt dimensions> CompressedImage<dimensions>::operator BasicMutableCompressedImageView<dimensions>() { template<UnsignedInt dimensions> CompressedImage<dimensions>::operator BasicMutableCompressedImageView<dimensions>() {
return BasicMutableCompressedImageView<dimensions>{_storage, _format, _size, _data, _flags}; return BasicMutableCompressedImageView<dimensions>{_storage, _format, Vector3i{_blockSize}, _blockDataSize, _size, _data, _flags};
} }
template<UnsignedInt dimensions> CompressedImage<dimensions>::operator BasicCompressedImageView<dimensions>() const { template<UnsignedInt dimensions> CompressedImage<dimensions>::operator BasicCompressedImageView<dimensions>() const {
return BasicCompressedImageView<dimensions>{_storage, _format, _size, _data, _flags}; return BasicCompressedImageView<dimensions>{_storage, _format, Vector3i{_blockSize}, _blockDataSize, _size, _data, _flags};
} }
template<UnsignedInt dimensions> std::pair<VectorTypeFor<dimensions, std::size_t>, VectorTypeFor<dimensions, std::size_t>> CompressedImage<dimensions>::dataProperties() const { template<UnsignedInt dimensions> std::pair<VectorTypeFor<dimensions, std::size_t>, VectorTypeFor<dimensions, std::size_t>> CompressedImage<dimensions>::dataProperties() const {

113
src/Magnum/Image.h

@ -573,8 +573,23 @@ template<UnsignedInt dimensions> class CompressedImage {
* @param data Image data * @param data Image data
* @param flags Image layout flags * @param flags Image layout flags
* *
* For a 3D image, if @p flags contain @ref ImageFlag3D::CubeMap, the * The @p data array is expected to be of proper size for given
* @p size is expected to match its restrictions. * 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<dimensions, Int>&, Containers::Array<char>&&, ImageFlags<dimensions>)
* overload to explicitly pass pass an implementation-specific
* @ref CompressedPixelFormat along with its block properties, or the
* @ref CompressedImage(CompressedPixelStorage, T, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, ImageFlags<dimensions>)
* 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<dimensions, Int>& size, Containers::Array<char>&& data, ImageFlags<dimensions> flags = {}) noexcept; explicit CompressedImage(CompressedPixelStorage storage, CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, ImageFlags<dimensions> flags = {}) noexcept;
@ -590,6 +605,51 @@ template<UnsignedInt dimensions> class CompressedImage {
*/ */
explicit CompressedImage(CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, ImageFlags<dimensions> flags = {}) noexcept: CompressedImage{{}, format, size, Utility::move(data), flags} {} explicit CompressedImage(CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, ImageFlags<dimensions> 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<dimensions, Int>&, Containers::Array<char>&&, ImageFlags<dimensions>),
* 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<dimensions, Int>& size, Containers::Array<char>&& data, ImageFlags<dimensions> 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<dimensions, Int>& size, Containers::Array<char>&& data, ImageFlags<dimensions> flags = {}) noexcept;
/** /**
* @brief Construct a compressed image with implementation-specific format * @brief Construct a compressed image with implementation-specific format
* @param storage Storage of compressed pixel data * @param storage Storage of compressed pixel data
@ -598,11 +658,10 @@ template<UnsignedInt dimensions> class CompressedImage {
* @param data Image data * @param data Image data
* @param flags Image layout flags * @param flags Image layout flags
* *
* Uses @ref compressedPixelFormatWrap() internally to convert * Uses ADL to find a corresponding @cpp compressedPixelFormatBlockSize(T) @ce
* @p format to @ref CompressedPixelFormat. * and @cpp compressedPixelFormatBlockDataSize(T) @ce overloads, then
* * calls @ref CompressedImage(CompressedPixelStorage, UnsignedInt, const Vector3i&, UnsignedInt, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, ImageFlags<dimensions>)
* For a 3D image, if @p flags contain @ref ImageFlag3D::CubeMap, the * with determined block size properties.
* @p size is expected to match its restrictions.
*/ */
template<class T> explicit CompressedImage(CompressedPixelStorage storage, T format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, ImageFlags<dimensions> flags = {}) noexcept; template<class T> explicit CompressedImage(CompressedPixelStorage storage, T format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, ImageFlags<dimensions> flags = {}) noexcept;
@ -622,9 +681,13 @@ template<UnsignedInt dimensions> class CompressedImage {
* @brief Construct an image placeholder * @brief Construct an image placeholder
* @param storage Storage of compressed pixel data * @param storage Storage of compressed pixel data
* *
* Format is undefined, size is zero, data is @cpp nullptr @ce and * Format and block properties are undefined, size is zero, data is
* data layout flags are empty. Move over a non-empty instance to make * @cpp nullptr @ce and data layout flags are empty. Move over a
* it useful. * 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 /* No ImageFlags parameter here as this constructor is mainly used to
query GL textures, and there the flags are forcibly reset */ query GL textures, and there the flags are forcibly reset */
@ -681,6 +744,25 @@ template<UnsignedInt dimensions> class CompressedImage {
*/ */
CompressedPixelFormat format() const { return _format; } 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 */ /** @brief Image size in pixels */
/* Unlike other getters this one is a const& so it's possible to slice /* 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 to the sizes when all images are in an array, for example for use
@ -732,13 +814,14 @@ template<UnsignedInt dimensions> class CompressedImage {
Containers::Array<char> release(); Containers::Array<char> release();
private: 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<dimensions, Int>& size, Containers::Array<char>&& data, ImageFlags<dimensions> flags) noexcept;
CompressedPixelStorage _storage; CompressedPixelStorage _storage;
CompressedPixelFormat _format; CompressedPixelFormat _format;
ImageFlags<dimensions> _flags; ImageFlags<dimensions> _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<dimensions, Int> _size; VectorTypeFor<dimensions, Int> _size;
Containers::Array<char> _data; Containers::Array<char> _data;
}; };
@ -772,7 +855,7 @@ template<UnsignedInt dimensions> template<class T> inline Image<dimensions>::Ima
"format types larger than 32bits are not supported"); "format types larger than 32bits are not supported");
} }
template<UnsignedInt dimensions> template<class T> inline CompressedImage<dimensions>::CompressedImage(const CompressedPixelStorage storage, const T format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const ImageFlags<dimensions> flags) noexcept: CompressedImage{storage, UnsignedInt(format), size, Utility::move(data), flags} { template<UnsignedInt dimensions> template<class T> inline CompressedImage<dimensions>::CompressedImage(const CompressedPixelStorage storage, const T format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const ImageFlags<dimensions> flags) noexcept: CompressedImage{storage, UnsignedInt(format), compressedPixelFormatBlockSize(format), compressedPixelFormatBlockDataSize(format), size, Utility::move(data), flags} {
static_assert(sizeof(T) <= 4, static_assert(sizeof(T) <= 4,
"format types larger than 32bits are not supported"); "format types larger than 32bits are not supported");
} }

48
src/Magnum/ImageView.cpp

@ -68,26 +68,66 @@ template<UnsignedInt dimensions, class T> auto ImageView<dimensions, T>::pixels(
return Implementation::imagePixelView<dimensions, Type>(*this, data()); return Implementation::imagePixelView<dimensions, Type>(*this, data());
} }
template<UnsignedInt dimensions, class T> CompressedImageView<dimensions, T>::CompressedImageView(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const Containers::ArrayView<ErasedType> data, const ImageFlags<dimensions> flags) noexcept: _storage{storage}, _format{format}, _flags{flags}, _size{size}, _data{reinterpret_cast<Type*>(data.data()), data.size()} { template<UnsignedInt dimensions, class T> CompressedImageView<dimensions, T>::CompressedImageView(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const Containers::ArrayView<ErasedType> data, const ImageFlags<dimensions> 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 #ifndef CORRADE_NO_ASSERT
isCompressedPixelFormatImplementationSpecific(format) ? 0 :
#endif
compressedPixelFormatBlockDataSize(format)
), size, data, flags} {}
template<UnsignedInt dimensions, class T> CompressedImageView<dimensions, T>::CompressedImageView(const CompressedPixelStorage storage, const CompressedPixelFormat format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor<dimensions, Int>& size, const Containers::ArrayView<ErasedType> data, const ImageFlags<dimensions> flags) noexcept: _storage{storage}, _format{format}, _flags{flags}, _blockSize{Vector3ub{blockSize}}, _blockDataSize{UnsignedByte(blockDataSize)}, _size{size}, _data{reinterpret_cast<Type*>(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<void>(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); Implementation::checkImageFlagsForSize("CompressedImageView:", flags, size);
#endif #endif
} }
template<UnsignedInt dimensions, class T> CompressedImageView<dimensions, T>::CompressedImageView(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const ImageFlags<dimensions> flags) noexcept: _storage{storage}, _format{format}, _flags{flags}, _size{size} { template<UnsignedInt dimensions, class T> CompressedImageView<dimensions, T>::CompressedImageView(const CompressedPixelStorage storage, const UnsignedInt format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor<dimensions, Int>& size, const Containers::ArrayView<ErasedType> data, const ImageFlags<dimensions> flags) noexcept: CompressedImageView{storage, compressedPixelFormatWrap(format), blockSize, blockDataSize, size, data, flags} {}
template<UnsignedInt dimensions, class T> CompressedImageView<dimensions, T>::CompressedImageView(const CompressedPixelStorage storage, const CompressedPixelFormat format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor<dimensions, Int>& size, const ImageFlags<dimensions> flags) noexcept: _storage{storage}, _format{format}, _flags{flags}, _blockSize{Vector3ub{blockSize}}, _blockDataSize{UnsignedByte(blockDataSize)}, _size{size} {
#ifndef CORRADE_NO_ASSERT #ifndef CORRADE_NO_ASSERT
Implementation::checkBlockProperties("CompressedImageView:", blockSize, blockDataSize);
Implementation::checkBlockPropertiesForStorage("CompressedImageView:", blockSize, blockDataSize, storage);
Implementation::checkImageFlagsForSize("CompressedImageView:", flags, size); Implementation::checkImageFlagsForSize("CompressedImageView:", flags, size);
#endif #endif
} }
template<UnsignedInt dimensions, class T> CompressedImageView<dimensions, T>::CompressedImageView(const CompressedPixelStorage storage, const UnsignedInt format, const VectorTypeFor<dimensions, Int>& size, const Containers::ArrayView<ErasedType> data, const ImageFlags<dimensions> flags) noexcept: CompressedImageView{storage, compressedPixelFormatWrap(format), size, data, flags} {} template<UnsignedInt dimensions, class T> CompressedImageView<dimensions, T>::CompressedImageView(const CompressedPixelStorage storage, const UnsignedInt format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor<dimensions, Int>& size, const ImageFlags<dimensions> flags) noexcept: CompressedImageView{storage, compressedPixelFormatWrap(format), blockSize, blockDataSize, size, flags} {}
template<UnsignedInt dimensions, class T> CompressedImageView<dimensions, T>::CompressedImageView(const CompressedPixelStorage storage, const UnsignedInt format, const VectorTypeFor<dimensions, Int>& size, const ImageFlags<dimensions> flags) noexcept: CompressedImageView{storage, compressedPixelFormatWrap(format), size, flags} {} template<UnsignedInt dimensions, class T> CompressedImageView<dimensions, T>::CompressedImageView(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const ImageFlags<dimensions> 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<UnsignedInt dimensions, class T> std::pair<VectorTypeFor<dimensions, std::size_t>, VectorTypeFor<dimensions, std::size_t>> CompressedImageView<dimensions, T>::dataProperties() const { template<UnsignedInt dimensions, class T> std::pair<VectorTypeFor<dimensions, std::size_t>, VectorTypeFor<dimensions, std::size_t>> CompressedImageView<dimensions, T>::dataProperties() const {
return Implementation::compressedImageDataProperties<dimensions>(*this); return Implementation::compressedImageDataProperties<dimensions>(*this);
} }
template<UnsignedInt dimensions, class T> void CompressedImageView<dimensions, T>::setData(const Containers::ArrayView<ErasedType> 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<Type*>(data.data()), data.size()};
}
template class MAGNUM_EXPORT ImageView<1, const char>; template class MAGNUM_EXPORT ImageView<1, const char>;
template class MAGNUM_EXPORT ImageView<2, const char>; template class MAGNUM_EXPORT ImageView<2, const char>;
template class MAGNUM_EXPORT ImageView<3, const char>; template class MAGNUM_EXPORT ImageView<3, const char>;

181
src/Magnum/ImageView.h

@ -730,8 +730,23 @@ template<UnsignedInt dimensions, class T> class CompressedImageView {
* @param data Image data * @param data Image data
* @param flags Image layout flags * @param flags Image layout flags
* *
* For a 3D image, if @p flags contain @ref ImageFlag3D::CubeMap, the * The @p data array is expected to be of proper size for given
* @p size is expected to match its restrictions. * 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<dimensions, Int>&, Containers::ArrayView<ErasedType>, ImageFlags<dimensions>)
* overload to explicitly pass pass an implementation-specific
* @ref CompressedPixelFormat along with its block properties, or the
* @ref CompressedImageView(CompressedPixelStorage, U, const VectorTypeFor<dimensions, Int>&, Containers::ArrayView<ErasedType>, ImageFlags<dimensions>)
* 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<dimensions, Int>& size, Containers::ArrayView<ErasedType> data, ImageFlags<dimensions> flags = {}) noexcept; explicit CompressedImageView(CompressedPixelStorage storage, CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::ArrayView<ErasedType> data, ImageFlags<dimensions> flags = {}) noexcept;
@ -758,6 +773,19 @@ template<UnsignedInt dimensions, class T> class CompressedImageView {
* assign a memory view to the image. For a 3D image, if @p flags * 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 * contain @ref ImageFlag3D::CubeMap, the @p size is expected to match
* its restrictions. * its restrictions.
*
* The @p format is expected to not be implementation-specific, use the
* @ref CompressedImageView(CompressedPixelStorage, CompressedPixelFormat, const Vector3i&, UnsignedInt, const VectorTypeFor<dimensions, Int>&, ImageFlags<dimensions>)
* overload to explicitly pass pass an implementation-specific
* @ref CompressedPixelFormat along with its block properties, or the
* @ref CompressedImageView(CompressedPixelStorage, U, const VectorTypeFor<dimensions, Int>&, ImageFlags<dimensions>)
* 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<dimensions, Int>& size, ImageFlags<dimensions> flags = {}) noexcept; explicit CompressedImageView(CompressedPixelStorage storage, CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, ImageFlags<dimensions> flags = {}) noexcept;
@ -772,6 +800,94 @@ template<UnsignedInt dimensions, class T> class CompressedImageView {
*/ */
explicit CompressedImageView(CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, ImageFlags<dimensions> flags = {}) noexcept: CompressedImageView{{}, format, size, flags} {} explicit CompressedImageView(CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, ImageFlags<dimensions> 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<dimensions, Int>&, Containers::ArrayView<ErasedType>, ImageFlags<dimensions>),
* 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<dimensions, Int>& size, Containers::ArrayView<ErasedType> data, ImageFlags<dimensions> 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<dimensions, Int>& size, Containers::ArrayView<ErasedType> data, ImageFlags<dimensions> 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<dimensions, Int>&, ImageFlags<dimensions>),
* 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<dimensions, Int>& size, ImageFlags<dimensions> 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<dimensions, Int>& size, ImageFlags<dimensions> flags = {}) noexcept;
/** /**
* @brief Construct an image view with implementation-specific format * @brief Construct an image view with implementation-specific format
* @param storage Storage of compressed pixel data * @param storage Storage of compressed pixel data
@ -780,11 +896,10 @@ template<UnsignedInt dimensions, class T> class CompressedImageView {
* @param data Image data * @param data Image data
* @param flags Image layout flags * @param flags Image layout flags
* *
* Uses @ref compressedPixelFormatWrap() internally to convert * Uses ADL to find a corresponding @cpp compressedPixelFormatBlockSize(U) @ce
* @p format to @ref CompressedPixelFormat. * and @cpp compressedPixelFormatBlockDataSize(U) @ce overloads, then
* * calls @ref CompressedImageView(CompressedPixelStorage, UnsignedInt, const Vector3i&, UnsignedInt, const VectorTypeFor<dimensions, Int>&, Containers::ArrayView<ErasedType>, ImageFlags<dimensions>)
* For a 3D image, if @p flags contain @ref ImageFlag3D::CubeMap, the * with determined block size properties.
* @p size is expected to match its restrictions.
*/ */
template<class U> explicit CompressedImageView(CompressedPixelStorage storage, U format, const VectorTypeFor<dimensions, Int>& size, Containers::ArrayView<ErasedType> data, ImageFlags<dimensions> flags = {}) noexcept; template<class U> explicit CompressedImageView(CompressedPixelStorage storage, U format, const VectorTypeFor<dimensions, Int>& size, Containers::ArrayView<ErasedType> data, ImageFlags<dimensions> flags = {}) noexcept;
@ -807,13 +922,10 @@ template<UnsignedInt dimensions, class T> class CompressedImageView {
* @param size Image size * @param size Image size
* @param flags Image layout flags * @param flags Image layout flags
* *
* Uses @ref compressedPixelFormatWrap() internally to convert * Uses ADL to find a corresponding @cpp compressedPixelFormatBlockSize(U) @ce
* @p format to @ref CompressedPixelFormat. * and @cpp compressedPixelFormatBlockDataSize(U) @ce overloads, then
* * calls @ref CompressedImageView(CompressedPixelStorage, UnsignedInt, const Vector3i&, UnsignedInt, const VectorTypeFor<dimensions, Int>&, ImageFlags<dimensions>)
* Data pointer is set to @cpp nullptr @ce, call @ref setData() to * with determined block size properties.
* 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.
*/ */
template<class U> explicit CompressedImageView(CompressedPixelStorage storage, U format, const VectorTypeFor<dimensions, Int>& size, ImageFlags<dimensions> flags = {}) noexcept; template<class U> explicit CompressedImageView(CompressedPixelStorage storage, U format, const VectorTypeFor<dimensions, Int>& size, ImageFlags<dimensions> flags = {}) noexcept;
@ -874,6 +986,25 @@ template<UnsignedInt dimensions, class T> class CompressedImageView {
*/ */
CompressedPixelFormat format() const { return _format; } 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 */ /** @brief Image size in pixels */
/* Unlike other getters this one is a const& so it's possible to slice /* 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 to the sizes when all images are in an array, for example for use
@ -897,22 +1028,20 @@ template<UnsignedInt dimensions, class T> class CompressedImageView {
* The data array is expected to be of proper size for parameters * The data array is expected to be of proper size for parameters
* specified in the constructor. * specified in the constructor.
*/ */
void setData(Containers::ArrayView<ErasedType> data) { void setData(Containers::ArrayView<ErasedType> data);
_data = {reinterpret_cast<Type*>(data.data()), data.size()};
}
private: private:
/* Needed for mutable->const conversion */ /* Needed for mutable->const conversion */
template<UnsignedInt, class> friend class CompressedImageView; template<UnsignedInt, class> 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<dimensions, Int>& size, Containers::ArrayView<ErasedType> data, ImageFlags<dimensions> flags) noexcept;
explicit CompressedImageView(CompressedPixelStorage storage, UnsignedInt format, const VectorTypeFor<dimensions, Int>& size, ImageFlags<dimensions> flags) noexcept;
CompressedPixelStorage _storage; CompressedPixelStorage _storage;
CompressedPixelFormat _format; CompressedPixelFormat _format;
ImageFlags<dimensions> _flags; ImageFlags<dimensions> _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<dimensions, Int> _size; VectorTypeFor<dimensions, Int> _size;
Containers::ArrayView<Type> _data; Containers::ArrayView<Type> _data;
}; };
@ -1024,12 +1153,12 @@ template<UnsignedInt dimensions, class T> template<UnsignedInt otherDimensions,
template<UnsignedInt dimensions, class T> template<class U, typename std::enable_if<std::is_const<T>::value && !std::is_const<U>::value, int>::type> ImageView<dimensions, T>::ImageView(const ImageView<dimensions, U>& other) noexcept: _storage{other._storage}, _format{other._format}, _formatExtra{other._formatExtra}, _pixelSize{other._pixelSize}, _flags{other._flags}, _size{other._size}, _data{other._data} {} template<UnsignedInt dimensions, class T> template<class U, typename std::enable_if<std::is_const<T>::value && !std::is_const<U>::value, int>::type> ImageView<dimensions, T>::ImageView(const ImageView<dimensions, U>& other) noexcept: _storage{other._storage}, _format{other._format}, _formatExtra{other._formatExtra}, _pixelSize{other._pixelSize}, _flags{other._flags}, _size{other._size}, _data{other._data} {}
#endif #endif
template<UnsignedInt dimensions, class T> template<class U> inline CompressedImageView<dimensions, T>::CompressedImageView(const CompressedPixelStorage storage, const U format, const VectorTypeFor<dimensions, Int>& size, const Containers::ArrayView<ErasedType> data, const ImageFlags<dimensions> flags) noexcept: CompressedImageView{storage, UnsignedInt(format), size, data, flags} { template<UnsignedInt dimensions, class T> template<class U> inline CompressedImageView<dimensions, T>::CompressedImageView(const CompressedPixelStorage storage, const U format, const VectorTypeFor<dimensions, Int>& size, const Containers::ArrayView<ErasedType> data, const ImageFlags<dimensions> flags) noexcept: CompressedImageView{storage, UnsignedInt(format), compressedPixelFormatBlockSize(format), compressedPixelFormatBlockDataSize(format), size, data, flags} {
static_assert(sizeof(U) <= 4, static_assert(sizeof(U) <= 4,
"format types larger than 32bits are not supported"); "format types larger than 32bits are not supported");
} }
template<UnsignedInt dimensions, class T> template<class U> inline CompressedImageView<dimensions, T>::CompressedImageView(const CompressedPixelStorage storage, const U format, const VectorTypeFor<dimensions, Int>& size, const ImageFlags<dimensions> flags) noexcept: CompressedImageView{storage, UnsignedInt(format), size, flags} { template<UnsignedInt dimensions, class T> template<class U> inline CompressedImageView<dimensions, T>::CompressedImageView(const CompressedPixelStorage storage, const U format, const VectorTypeFor<dimensions, Int>& size, const ImageFlags<dimensions> flags) noexcept: CompressedImageView{storage, UnsignedInt(format), compressedPixelFormatBlockSize(format), compressedPixelFormatBlockDataSize(format), size, flags} {
static_assert(sizeof(U) <= 4, static_assert(sizeof(U) <= 4,
"format types larger than 32bits are not supported"); "format types larger than 32bits are not supported");
} }
@ -1040,10 +1169,12 @@ template<UnsignedInt dimensions, class T> template<UnsignedInt otherDimensions,
_format{other._format}, _format{other._format},
/* Removing the Array bit and transferring the rest, as documented */ /* Removing the Array bit and transferring the rest, as documented */
_flags{ImageFlag<dimensions>(UnsignedShort(other._flags)&~UnsignedShort(ImageFlag2D::Array))|flags}, _flags{ImageFlag<dimensions>(UnsignedShort(other._flags)&~UnsignedShort(ImageFlag2D::Array))|flags},
_blockSize{other._blockSize},
_blockDataSize{other._blockDataSize},
_size{Math::Vector<dimensions, Int>::pad(other._size, 1)}, _size{Math::Vector<dimensions, Int>::pad(other._size, 1)},
_data{other._data} {} _data{other._data} {}
template<UnsignedInt dimensions, class T> template<class U, typename std::enable_if<std::is_const<T>::value && !std::is_const<U>::value, int>::type> CompressedImageView<dimensions, T>::CompressedImageView(const CompressedImageView<dimensions, U>& other) noexcept: _storage{other._storage}, _format{other._format}, _flags{other._flags}, _size{other._size}, _data{other._data} {} template<UnsignedInt dimensions, class T> template<class U, typename std::enable_if<std::is_const<T>::value && !std::is_const<U>::value, int>::type> CompressedImageView<dimensions, T>::CompressedImageView(const CompressedImageView<dimensions, U>& other) noexcept: _storage{other._storage}, _format{other._format}, _flags{other._flags}, _blockSize{other._blockSize}, _blockDataSize{other._blockDataSize}, _size{other._size}, _data{other._data} {}
#endif #endif
} }

62
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, ); 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 ImageFlags1D, const Math::Vector<1, Int>&) {}
inline void checkImageFlagsForSize(const char*, const ImageFlags2D, const Vector2i&) {} inline void checkImageFlagsForSize(const char*, const ImageFlags2D, const Vector2i&) {}
inline void checkImageFlagsForSize(const char* inline void checkImageFlagsForSize(const char*
@ -73,7 +100,8 @@ template<std::size_t dimensions, class T> std::pair<Math::Vector<dimensions, std
} }
/* Used in CompressedPixelStorage::dataProperties(), where it passes the /* Used in CompressedPixelStorage::dataProperties(), where it passes the
storage-supplied block size */ storage-supplied block size, and in compressedImageDataSizeFor() below where
it passes the block size from the image */
inline std::pair<Math::Vector3<std::size_t>, Math::Vector3<std::size_t>> compressedDataProperties(const CompressedPixelStorage& storage, const Vector3i& blockSize, const UnsignedInt blockDataSize, const Vector3i& size) { inline std::pair<Math::Vector3<std::size_t>, Math::Vector3<std::size_t>> compressedDataProperties(const CompressedPixelStorage& storage, const Vector3i& blockSize, const UnsignedInt blockDataSize, const Vector3i& size) {
const Vector3i blockCount = (size + blockSize - Vector3i{1})/blockSize; const Vector3i blockCount = (size + blockSize - Vector3i{1})/blockSize;
const Math::Vector3<std::size_t> dataSize{ const Math::Vector3<std::size_t> dataSize{
@ -87,9 +115,17 @@ inline std::pair<Math::Vector3<std::size_t>, Math::Vector3<std::size_t>> compres
return std::make_pair(offset, size.product() ? dataSize : Math::Vector3<std::size_t>{}); return std::make_pair(offset, size.product() ? dataSize : Math::Vector3<std::size_t>{});
} }
template<class, class = void> struct CompressedImageTraits;
template<class T> struct CompressedImageTraits<T, typename std::enable_if<std::is_same<decltype(std::declval<T>().storage()), CompressedPixelStorage>::value>::type> {
static CompressedPixelStorage storage(const T& image) { return image.storage(); }
};
template<class T> struct CompressedImageTraits<T, typename std::enable_if<std::is_same<decltype(std::declval<T>().storage()), PixelStorage>::value>::type> {
static CompressedPixelStorage storage(const T& image) { return image.compressedStorage(); }
};
/* Used in Compressed*Image::dataProperties() */ /* Used in Compressed*Image::dataProperties() */
template<std::size_t dimensions, class T> std::pair<Math::Vector<dimensions, std::size_t>, Math::Vector<dimensions, std::size_t>> compressedImageDataProperties(const T& image) { template<std::size_t dimensions, class T> std::pair<Math::Vector<dimensions, std::size_t>, Math::Vector<dimensions, std::size_t>> compressedImageDataProperties(const T& image) {
std::pair<Math::Vector3<std::size_t>, Math::Vector3<std::size_t>> dataProperties = image.storage().dataProperties(Vector3i::pad(image.size(), 1)); std::pair<Math::Vector3<std::size_t>, Math::Vector3<std::size_t>> dataProperties = compressedDataProperties(CompressedImageTraits<T>::storage(image), image.blockSize(), image.blockDataSize(), Vector3i::pad(image.size(), 1));
return std::make_pair(Math::Vector<dimensions, std::size_t>::pad(dataProperties.first), Math::Vector<dimensions, std::size_t>::pad(dataProperties.second)); return std::make_pair(Math::Vector<dimensions, std::size_t>::pad(dataProperties.first), Math::Vector<dimensions, std::size_t>::pad(dataProperties.second));
} }
@ -126,10 +162,8 @@ template<class T> inline std::size_t imageDataSize(const T& image) {
where the nv-cubemap-broken-full-compressed-image-query workaround needs to 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 go slice by slice, taking offset and incrementing it by size divided by the
Z dimension. */ Z dimension. */
template<std::size_t dimensions, class T> std::pair<std::size_t, std::size_t> compressedImageDataOffsetSizeFor(const T& image, const Math::Vector<dimensions, Int>& size) { template<std::size_t dimensions> std::pair<std::size_t, std::size_t> compressedImageDataOffsetSizeFor(const CompressedPixelStorage& storage, const Vector3i& blockSize, const UnsignedInt blockDataSize, const Math::Vector<dimensions, Int>& size) {
CORRADE_INTERNAL_ASSERT(image.storage().compressedBlockSize().product() && image.storage().compressedBlockDataSize()); std::pair<Math::Vector3<std::size_t>, Math::Vector3<std::size_t>> dataProperties = compressedDataProperties(storage, blockSize, blockDataSize, Vector3i::pad(size, 1));
std::pair<Math::Vector3<std::size_t>, Math::Vector3<std::size_t>> dataProperties = image.storage().dataProperties(Vector3i::pad(size, 1));
/* Smallest line/rectangle/cube that covers the area. Same logic as in /* Smallest line/rectangle/cube that covers the area. Same logic as in
imageDataSizeFor() above. */ imageDataSizeFor() above. */
@ -137,13 +171,17 @@ template<std::size_t dimensions, class T> std::pair<std::size_t, std::size_t> co
if(dataProperties.first.z()) if(dataProperties.first.z())
dataOffset += dataProperties.first.z(); dataOffset += dataProperties.first.z();
else if(dataProperties.first.y()) { else if(dataProperties.first.y()) {
if(!image.storage().imageHeight()) if(!storage.imageHeight())
dataOffset += dataProperties.first.y(); dataOffset += dataProperties.first.y();
} else if(dataProperties.first.x()) { } else if(dataProperties.first.x()) {
if(!image.storage().rowLength()) if(!storage.rowLength())
dataOffset += dataProperties.first.x(); dataOffset += dataProperties.first.x();
} }
return {dataOffset, dataProperties.second.product()*image.storage().compressedBlockDataSize()}; return {dataOffset, dataProperties.second.product()*blockDataSize};
}
template<std::size_t dimensions, class T> std::pair<std::size_t, std::size_t> compressedImageDataOffsetSizeFor(const T& image, const Math::Vector<dimensions, Int>& size) {
return compressedImageDataOffsetSizeFor(CompressedImageTraits<T>::storage(image), image.blockSize(), image.blockDataSize(), size);
} }
/* Used in image query functions */ /* Used in image query functions */
@ -152,6 +190,12 @@ template<std::size_t dimensions, class T> std::size_t compressedImageDataSizeFor
return r.first + r.second; return r.first + r.second;
} }
/* Used in data size assertions */
template<class T> inline std::size_t compressedImageDataSize(const T& image) {
auto r = compressedImageDataOffsetSizeFor(image, image.size());
return r.first + r.second;
}
template<std::size_t dimensions, class T> std::ptrdiff_t pixelStorageSkipOffsetFor(const T& image, const Math::Vector<dimensions, Int>& size) { template<std::size_t dimensions, class T> std::ptrdiff_t pixelStorageSkipOffsetFor(const T& image, const Math::Vector<dimensions, Int>& size) {
return image.storage().dataProperties(image.pixelSize(), Vector3i::pad(size, 1)).first.sum(); return image.storage().dataProperties(image.pixelSize(), Vector3i::pad(size, 1)).first.sum();
} }

31
src/Magnum/PixelStorage.h

@ -177,31 +177,40 @@ class MAGNUM_EXPORT CompressedPixelStorage: public PixelStorage {
return !operator==(other); return !operator==(other);
} }
/** @brief Compressed block size */ /** @brief Compressed block size in pixels */
constexpr Vector3i compressedBlockSize() const { return _blockSize; } 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 * Expected to either match size information for a particular
* particular compressed format is used. Default is @cpp 0 @ce in all * compressed format of image the storage is used with or be set to
* dimensions. * @cpp 0 @ce. Default is @cpp 0 @ce in all dimensions.
* @see @ref Magnum::compressedPixelFormatBlockSize() * @see @ref compressedPixelFormatBlockSize(),
* @ref CompressedImageView::blockSize(),
* @ref CompressedImage::blockSize(),
* @ref Trade::ImageData::compressedBlockSize(),
* @ref GL::CompressedBufferImage::blockSize(),
*/ */
CompressedPixelStorage& setCompressedBlockSize(const Vector3i& size) { CompressedPixelStorage& setCompressedBlockSize(const Vector3i& size) {
_blockSize = size; _blockSize = size;
return *this; return *this;
} }
/** @brief Compressed block data size (in bytes) */ /** @brief Compressed block data size in bytes */
constexpr Int compressedBlockDataSize() const { return _blockDataSize; } 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 * Expected to either match size information for a particular
* format is used. Default is @cpp 0 @ce in all dimensions. * compressed format of image the storage is used with or be set to
* @see @ref Magnum::compressedPixelFormatBlockDataSize() * @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) { CompressedPixelStorage& setCompressedBlockDataSize(Int size) {
_blockDataSize = size; _blockDataSize = size;

316
src/Magnum/Test/ImageTest.cpp

@ -56,6 +56,8 @@ struct ImageTest: TestSuite::Tester {
void constructInvalidPixelSize(); void constructInvalidPixelSize();
void constructInvalidSize(); void constructInvalidSize();
void constructInvalidCubeMap(); void constructInvalidCubeMap();
void constructCompressedUnknownImplementationSpecificBlockSize();
void constructCompressedInvalidBlockSize();
void constructCompressedInvalidSize(); void constructCompressedInvalidSize();
void constructCompressedInvalidCubeMap(); void constructCompressedInvalidCubeMap();
@ -115,6 +117,8 @@ ImageTest::ImageTest() {
&ImageTest::constructInvalidPixelSize, &ImageTest::constructInvalidPixelSize,
&ImageTest::constructInvalidSize, &ImageTest::constructInvalidSize,
&ImageTest::constructInvalidCubeMap, &ImageTest::constructInvalidCubeMap,
&ImageTest::constructCompressedUnknownImplementationSpecificBlockSize,
&ImageTest::constructCompressedInvalidBlockSize,
&ImageTest::constructCompressedInvalidSize, &ImageTest::constructCompressedInvalidSize,
&ImageTest::constructCompressedInvalidCubeMap, &ImageTest::constructCompressedInvalidCubeMap,
@ -168,6 +172,20 @@ namespace GL {
} }
enum class CompressedPixelFormat { RGBS3tcDxt1 = 21 }; enum class CompressedPixelFormat { RGBS3tcDxt1 = 21 };
Vector3i compressedPixelFormatBlockSize(CompressedPixelFormat format) {
#ifdef CORRADE_NO_ASSERT
static_cast<void>(format);
#endif
CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::RGBS3tcDxt1);
return {4, 4, 1};
}
UnsignedInt compressedPixelFormatBlockDataSize(CompressedPixelFormat format) {
#ifdef CORRADE_NO_ASSERT
static_cast<void>(format);
#endif
CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::RGBS3tcDxt1);
return 8;
}
} }
namespace Vk { namespace Vk {
@ -182,6 +200,22 @@ namespace Vk {
#endif #endif
return 12; return 12;
} }
enum class CompressedPixelFormat { Astc5x5x4RGBAF = 111 };
Vector3i compressedPixelFormatBlockSize(CompressedPixelFormat format) {
#ifdef CORRADE_NO_ASSERT
static_cast<void>(format);
#endif
CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::Astc5x5x4RGBAF);
return {5, 5, 4};
}
UnsignedInt compressedPixelFormatBlockDataSize(CompressedPixelFormat format) {
#ifdef CORRADE_NO_ASSERT
static_cast<void>(format);
#endif
CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::Astc5x5x4RGBAF);
return 16;
}
} }
void ImageTest::constructGeneric() { void ImageTest::constructGeneric() {
@ -394,28 +428,34 @@ void ImageTest::constructImplementationSpecificPlaceholder() {
void ImageTest::constructCompressedGeneric() { void ImageTest::constructCompressedGeneric() {
{ {
auto data = new char[8]; auto data = new char[6*8];
CompressedImage2D a{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, CompressedImage2D a{
Containers::Array<char>{data, 8}, ImageFlag2D::Array}; CompressedPixelFormat::Bc1RGBAUnorm, {12, 8},
Containers::Array<char>{data, 6*8}, ImageFlag2D::Array};
CORRADE_COMPARE(a.flags(), 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.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<const void*>(data)); CORRADE_COMPARE(a.data(), static_cast<const void*>(data));
CORRADE_COMPARE(a.data().size(), 8); CORRADE_COMPARE(a.data().size(), 6*8);
} { } {
auto data = new char[8]; auto data = new char[8*16];
CompressedImage2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedImage2D a{
CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, CompressedPixelStorage{}.setRowLength(20),
Containers::Array<char>{data, 8}, ImageFlag2D::Array}; CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10},
Containers::Array<char>{data, 8*16}, ImageFlag2D::Array};
CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); CORRADE_COMPARE(a.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(a.storage().rowLength(), 20);
CORRADE_COMPARE(a.format(), CompressedPixelFormat::Bc1RGBAUnorm); CORRADE_COMPARE(a.format(), CompressedPixelFormat::Astc5x5x4RGBAF);
CORRADE_COMPARE(a.size(), Vector2i(4, 4)); 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<const void*>(data)); CORRADE_COMPARE(a.data(), static_cast<const void*>(data));
CORRADE_COMPARE(a.data().size(), 8); CORRADE_COMPARE(a.data().size(), 8*16);
} }
} }
@ -424,8 +464,10 @@ void ImageTest::constructCompressedGenericPlaceholder() {
CompressedImage2D a; CompressedImage2D a;
CORRADE_COMPARE(a.flags(), ImageFlags2D{}); 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.format(), CompressedPixelFormat{});
CORRADE_COMPARE(a.blockSize(), (Vector3i{}));
CORRADE_COMPARE(a.blockDataSize(), 0);
CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(a.size(), Vector2i{});
CORRADE_COMPARE(a.data(), static_cast<const void*>(nullptr)); CORRADE_COMPARE(a.data(), static_cast<const void*>(nullptr));
} { } {
@ -433,12 +475,14 @@ void ImageTest::constructCompressedGenericPlaceholder() {
CompressedPixelStorage{} CompressedPixelStorage{}
/* Even with skip it shouldn't assert on data size */ /* Even with skip it shouldn't assert on data size */
.setSkip({1, 0, 0}) .setSkip({1, 0, 0})
.setCompressedBlockSize(Vector3i{4})}; .setRowLength(20)};
CORRADE_COMPARE(a.flags(), ImageFlags2D{}); CORRADE_COMPARE(a.flags(), ImageFlags2D{});
CORRADE_COMPARE(a.storage().skip(), (Vector3i{1, 0, 0})); 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.format(), CompressedPixelFormat{});
CORRADE_COMPARE(a.blockSize(), (Vector3i{}));
CORRADE_COMPARE(a.blockDataSize(), 0);
CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(a.size(), Vector2i{});
CORRADE_COMPARE(a.data(), static_cast<const void*>(nullptr)); CORRADE_COMPARE(a.data(), static_cast<const void*>(nullptr));
} }
@ -447,31 +491,54 @@ void ImageTest::constructCompressedGenericPlaceholder() {
void ImageTest::constructCompressedImplementationSpecific() { void ImageTest::constructCompressedImplementationSpecific() {
/* Format with autodetection */ /* Format with autodetection */
{ {
auto data = new char[8]; auto data = new char[6*8];
CompressedImage2D a{GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, CompressedImage2D a{
Containers::Array<char>{data, 8}, ImageFlag2D::Array}; GL::CompressedPixelFormat::RGBS3tcDxt1, {12, 8},
Containers::Array<char>{data, 6*8}, ImageFlag2D::Array};
CORRADE_COMPARE(a.flags(), 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.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<const void*>(data)); CORRADE_COMPARE(a.data(), static_cast<const void*>(data));
CORRADE_COMPARE(a.data().size(), 8); CORRADE_COMPARE(a.data().size(), 6*8);
} { } {
auto data = new char[8]; auto data = new char[8*16];
CompressedImage2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedImage2D a{
GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, CompressedPixelStorage{}.setRowLength(20),
Containers::Array<char>{data, 8}, ImageFlag2D::Array}; Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10},
Containers::Array<char>{data, 8*16}, ImageFlag2D::Array};
CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); CORRADE_COMPARE(a.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(a.storage().rowLength(), 20);
CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF));
CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); 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<const void*>(data)); CORRADE_COMPARE(a.data(), static_cast<const void*>(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<char>{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<const void*>(data));
CORRADE_COMPARE(a.data().size(), 6*12);
}
} }
void ImageTest::constructUnknownImplementationSpecificPixelSize() { void ImageTest::constructUnknownImplementationSpecificPixelSize() {
@ -539,10 +606,62 @@ void ImageTest::constructInvalidCubeMap() {
"Image: expected square faces for a cube map, got {4, 3}\n"); "Image: expected square faces for a cube map, got {4, 3}\n");
} }
void ImageTest::constructCompressedInvalidSize() { void ImageTest::constructCompressedUnknownImplementationSpecificBlockSize() {
CORRADE_SKIP_IF_NO_ASSERT(); 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<char>{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<char>{NoInit, 8}};
CompressedImage2D{CompressedPixelStorage{}
.setCompressedBlockSize({4, 5, 6})
.setCompressedBlockDataSize(8),
666, {4, 5, 6}, 8, {1, 1}, Containers::Array<char>{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<char>{NoInit, 8}};
CompressedImage2D{CompressedPixelStorage{}
.setCompressedBlockSize({4, 4, 1})
.setCompressedBlockDataSize(4),
666, {4, 4, 1}, 8, {1, 1}, Containers::Array<char>{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 */ /* Too small for given format */
{ {
@ -661,70 +780,80 @@ void ImageTest::constructMoveImplementationSpecific() {
} }
void ImageTest::constructMoveCompressedGeneric() { void ImageTest::constructMoveCompressedGeneric() {
auto data = new char[8]; auto data = new char[8*16];
CompressedImage2D a{ CompressedImage2D a{
CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedPixelStorage{}.setRowLength(20),
CompressedPixelFormat::Bc3RGBAUnorm, {4, 4}, Containers::Array<char>{data, 8}, ImageFlag2D::Array}; CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10},
Containers::Array<char>{data, 8*16}, ImageFlag2D::Array};
CompressedImage2D b{Utility::move(a)}; CompressedImage2D b{Utility::move(a)};
CORRADE_COMPARE(a.data(), static_cast<const void*>(nullptr)); CORRADE_COMPARE(a.data(), static_cast<const void*>(nullptr));
CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(a.size(), Vector2i{});
CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); CORRADE_COMPARE(b.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(b.storage().rowLength(), 20);
CORRADE_COMPARE(b.format(), CompressedPixelFormat::Bc3RGBAUnorm); CORRADE_COMPARE(b.format(), CompressedPixelFormat::Astc5x5x4RGBAF);
CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); 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<const void*>(data)); CORRADE_COMPARE(b.data(), static_cast<const void*>(data));
CORRADE_COMPARE(b.data().size(), 8); CORRADE_COMPARE(b.data().size(), 8*16);
auto data2 = new char[16]; auto data2 = new char[2*8];
CompressedImage2D c{CompressedPixelFormat::Bc1RGBAUnorm, {8, 4}, Containers::Array<char>{data2, 16}}; CompressedImage2D c{CompressedPixelFormat::Bc1RGBAUnorm, {8, 4}, Containers::Array<char>{data2, 2*8}};
c = Utility::move(b); c = Utility::move(b);
CORRADE_COMPARE(b.data(), static_cast<const void*>(data2)); CORRADE_COMPARE(b.data(), static_cast<const void*>(data2));
CORRADE_COMPARE(b.size(), (Vector2i{8, 4})); CORRADE_COMPARE(b.size(), (Vector2i{8, 4}));
CORRADE_COMPARE(c.flags(), ImageFlag2D::Array); CORRADE_COMPARE(c.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(c.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(c.storage().rowLength(), 20);
CORRADE_COMPARE(c.format(), CompressedPixelFormat::Bc3RGBAUnorm); CORRADE_COMPARE(c.format(), CompressedPixelFormat::Astc5x5x4RGBAF);
CORRADE_COMPARE(c.size(), (Vector2i{4, 4})); 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<const void*>(data)); CORRADE_COMPARE(c.data(), static_cast<const void*>(data));
CORRADE_COMPARE(c.data().size(), 8); CORRADE_COMPARE(c.data().size(), 8*16);
CORRADE_VERIFY(std::is_nothrow_move_constructible<CompressedImage2D>::value); CORRADE_VERIFY(std::is_nothrow_move_constructible<CompressedImage2D>::value);
CORRADE_VERIFY(std::is_nothrow_move_assignable<CompressedImage2D>::value); CORRADE_VERIFY(std::is_nothrow_move_assignable<CompressedImage2D>::value);
} }
void ImageTest::constructMoveCompressedImplementationSpecific() { void ImageTest::constructMoveCompressedImplementationSpecific() {
auto data = new char[8]; auto data = new char[8*16];
CompressedImage2D a{ CompressedImage2D a{
CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedPixelStorage{}.setRowLength(20),
GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, Containers::Array<char>{data, 8}, ImageFlag2D::Array}; Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10},
Containers::Array<char>{data, 8*16}, ImageFlag2D::Array};
CompressedImage2D b{Utility::move(a)}; CompressedImage2D b{Utility::move(a)};
CORRADE_COMPARE(a.data(), static_cast<const void*>(nullptr)); CORRADE_COMPARE(a.data(), static_cast<const void*>(nullptr));
CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(a.size(), Vector2i{});
CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); CORRADE_COMPARE(b.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(b.storage().rowLength(), 20);
CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF));
CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); 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<const void*>(data)); CORRADE_COMPARE(b.data(), static_cast<const void*>(data));
CORRADE_COMPARE(b.data().size(), 8); CORRADE_COMPARE(b.data().size(), 8*16);
auto data2 = new char[16]; auto data2 = new char[2*8];
CompressedImage2D c{CompressedPixelFormat::Bc2RGBAUnorm, {8, 4}, Containers::Array<char>{data2, 16}}; CompressedImage2D c{CompressedPixelFormat::Bc1RGBAUnorm, {8, 4}, Containers::Array<char>{data2, 2*8}};
c = Utility::move(b); c = Utility::move(b);
CORRADE_COMPARE(b.data(), static_cast<const void*>(data2)); CORRADE_COMPARE(b.data(), static_cast<const void*>(data2));
CORRADE_COMPARE(b.size(), (Vector2i{8, 4})); CORRADE_COMPARE(b.size(), (Vector2i{8, 4}));
CORRADE_COMPARE(c.flags(), ImageFlag2D::Array); CORRADE_COMPARE(c.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(c.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(c.storage().rowLength(), 20);
CORRADE_COMPARE(c.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(c.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF));
CORRADE_COMPARE(c.size(), (Vector2i{4, 4})); 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<const void*>(data)); CORRADE_COMPARE(c.data(), static_cast<const void*>(data));
CORRADE_COMPARE(c.data().size(), 8); CORRADE_COMPARE(c.data().size(), 8*16);
} }
template<class T> void ImageTest::toViewGeneric() { template<class T> void ImageTest::toViewGeneric() {
@ -764,35 +893,41 @@ template<class T> void ImageTest::toViewImplementationSpecific() {
template<class T> void ImageTest::toViewCompressedGeneric() { template<class T> void ImageTest::toViewCompressedGeneric() {
setTestCaseTemplateName(MutabilityTraits<T>::name()); setTestCaseTemplateName(MutabilityTraits<T>::name());
auto data = new char[8]; auto data = new char[8*16];
typename MutabilityTraits<T>::CompressedImageType a{ typename MutabilityTraits<T>::CompressedImageType a{
CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedPixelStorage{}.setRowLength(20),
CompressedPixelFormat::Bc1RGBUnorm, {4, 4}, Containers::Array<char>{data, 8}, ImageFlag2D::Array}; CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10},
Containers::Array<char>{data, 8*16}, ImageFlag2D::Array};
CompressedImageView<2, T> b = a; CompressedImageView<2, T> b = a;
CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); CORRADE_COMPARE(b.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(b.storage().rowLength(), 20);
CORRADE_COMPARE(b.format(), CompressedPixelFormat::Bc1RGBUnorm); CORRADE_COMPARE(b.format(), CompressedPixelFormat::Astc5x5x4RGBAF);
CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); 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<const void*>(data)); CORRADE_COMPARE(b.data(), static_cast<const void*>(data));
CORRADE_COMPARE(b.data().size(), 8); CORRADE_COMPARE(b.data().size(), 8*16);
} }
template<class T> void ImageTest::toViewCompressedImplementationSpecific() { template<class T> void ImageTest::toViewCompressedImplementationSpecific() {
setTestCaseTemplateName(MutabilityTraits<T>::name()); setTestCaseTemplateName(MutabilityTraits<T>::name());
auto data = new char[8]; auto data = new char[8*16];
typename MutabilityTraits<T>::CompressedImageType a{ typename MutabilityTraits<T>::CompressedImageType a{
CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedPixelStorage{}.setRowLength(20),
GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, Containers::Array<char>{data, 8}, ImageFlag2D::Array}; Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10},
Containers::Array<char>{data, 8*16}, ImageFlag2D::Array};
CompressedImageView<2, T> b = a; CompressedImageView<2, T> b = a;
CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); CORRADE_COMPARE(b.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(b.storage().rowLength(), 20);
CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF));
CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); 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<const void*>(data)); CORRADE_COMPARE(b.data(), static_cast<const void*>(data));
CORRADE_COMPARE(b.data().size(), 8); CORRADE_COMPARE(b.data().size(), 8*16);
} }
void ImageTest::data() { void ImageTest::data() {
@ -804,9 +939,10 @@ void ImageTest::data() {
} }
void ImageTest::dataCompressed() { void ImageTest::dataCompressed() {
auto data = new char[8]; auto data = new char[6*8];
CompressedImage2D a{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, CompressedImage2D a{
Containers::Array<char>{data, 8}}; CompressedPixelFormat::Bc1RGBAUnorm, {12, 8},
Containers::Array<char>{data, 6*8}, ImageFlag2D::Array};
const CompressedImage2D& ca = a; const CompressedImage2D& ca = a;
CORRADE_COMPARE(a.data(), static_cast<const void*>(data)); CORRADE_COMPARE(a.data(), static_cast<const void*>(data));
CORRADE_COMPARE(ca.data(), static_cast<const void*>(data)); CORRADE_COMPARE(ca.data(), static_cast<const void*>(data));
@ -820,10 +956,10 @@ void ImageTest::dataRvalue() {
} }
void ImageTest::dataRvalueCompressed() { void ImageTest::dataRvalueCompressed() {
auto data = new char[8]; auto data = new char[6*8];
Containers::Array<char> released = CompressedImage2D{ Containers::Array<char> released = CompressedImage2D{
CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, CompressedPixelFormat::Bc1RGBAUnorm, {12, 8},
Containers::Array<char>{data, 8}}.data(); Containers::Array<char>{data, 6*8}, ImageFlag2D::Array}.data();
CORRADE_COMPARE(released.data(), static_cast<const void*>(data)); CORRADE_COMPARE(released.data(), static_cast<const void*>(data));
} }
@ -839,17 +975,15 @@ void ImageTest::dataProperties() {
} }
void ImageTest::dataPropertiesCompressed() { void ImageTest::dataPropertiesCompressed() {
/* Yes, I know, this is totally bogus and doesn't match the BC1 format */
CompressedImage3D image{ CompressedImage3D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({3, 4, 5}) .setRowLength(12)
.setCompressedBlockDataSize(16) .setImageHeight(8)
.setImageHeight(12) .setSkip({8, 4, 4}),
.setSkip({5, 8, 11}), CompressedPixelFormat::Bc1RGBAUnorm, {2, 3, 3},
CompressedPixelFormat::Bc1RGBAUnorm, {2, 8, 11}, Containers::Array<char>{NoInit, 336}};
Containers::Array<char>{1}};
CORRADE_COMPARE(image.dataProperties(), CORRADE_COMPARE(image.dataProperties(),
(std::pair<Math::Vector3<std::size_t>, Math::Vector3<std::size_t>>{{2*16, 2*16, 9*16}, {1, 3, 3}})); (std::pair<Math::Vector3<std::size_t>, Math::Vector3<std::size_t>>{{16, 24, 192}, {3, 2, 3}}));
} }
void ImageTest::release() { void ImageTest::release() {
@ -863,8 +997,10 @@ void ImageTest::release() {
} }
void ImageTest::releaseCompressed() { void ImageTest::releaseCompressed() {
char data[8]; char data[6*8];
CompressedImage2D a{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, Containers::Array<char>{data, 8}}; CompressedImage2D a{
CompressedPixelFormat::Bc1RGBAUnorm, {12, 8},
Containers::Array<char>{data, 6*8}, ImageFlag2D::Array};
const char* const pointer = a.release().release(); const char* const pointer = a.release().release();
CORRADE_COMPARE(pointer, data); CORRADE_COMPARE(pointer, data);

419
src/Magnum/Test/ImageViewTest.cpp

@ -64,6 +64,8 @@ struct ImageViewTest: TestSuite::Tester {
void constructInvalidPixelSize(); void constructInvalidPixelSize();
void constructInvalidSize(); void constructInvalidSize();
void constructInvalidCubeMap(); void constructInvalidCubeMap();
void constructCompressedUnknownImplementationSpecificBlockSize();
void constructCompressedInvalidBlockSize();
void constructCompressedInvalidSize(); void constructCompressedInvalidSize();
void constructCompressedInvalidCubeMap(); void constructCompressedInvalidCubeMap();
@ -120,6 +122,8 @@ ImageViewTest::ImageViewTest() {
&ImageViewTest::constructInvalidPixelSize, &ImageViewTest::constructInvalidPixelSize,
&ImageViewTest::constructInvalidSize, &ImageViewTest::constructInvalidSize,
&ImageViewTest::constructInvalidCubeMap, &ImageViewTest::constructInvalidCubeMap,
&ImageViewTest::constructCompressedUnknownImplementationSpecificBlockSize,
&ImageViewTest::constructCompressedInvalidBlockSize,
&ImageViewTest::constructCompressedInvalidSize, &ImageViewTest::constructCompressedInvalidSize,
&ImageViewTest::constructCompressedInvalidCubeMap, &ImageViewTest::constructCompressedInvalidCubeMap,
@ -160,6 +164,20 @@ namespace GL {
} }
enum class CompressedPixelFormat { RGBS3tcDxt1 = 21 }; enum class CompressedPixelFormat { RGBS3tcDxt1 = 21 };
Vector3i compressedPixelFormatBlockSize(CompressedPixelFormat format) {
#ifdef CORRADE_NO_ASSERT
static_cast<void>(format);
#endif
CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::RGBS3tcDxt1);
return {4, 4, 1};
}
UnsignedInt compressedPixelFormatBlockDataSize(CompressedPixelFormat format) {
#ifdef CORRADE_NO_ASSERT
static_cast<void>(format);
#endif
CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::RGBS3tcDxt1);
return 8;
}
} }
namespace Vk { namespace Vk {
@ -174,6 +192,22 @@ namespace Vk {
#endif #endif
return 12; return 12;
} }
enum class CompressedPixelFormat { Astc5x5x4RGBAF = 111 };
Vector3i compressedPixelFormatBlockSize(CompressedPixelFormat format) {
#ifdef CORRADE_NO_ASSERT
static_cast<void>(format);
#endif
CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::Astc5x5x4RGBAF);
return {5, 5, 4};
}
UnsignedInt compressedPixelFormatBlockDataSize(CompressedPixelFormat format) {
#ifdef CORRADE_NO_ASSERT
static_cast<void>(format);
#endif
CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::Astc5x5x4RGBAF);
return 16;
}
} }
template<class T> void ImageViewTest::constructGeneric() { template<class T> void ImageViewTest::constructGeneric() {
@ -376,27 +410,34 @@ template<class T> void ImageViewTest::constructCompressedGeneric() {
setTestCaseTemplateName(MutabilityTraits<T>::name()); setTestCaseTemplateName(MutabilityTraits<T>::name());
{ {
T data[8]{}; T data[6*8]{};
CompressedImageView<2, T> a{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, data, ImageFlag2D::Array}; CompressedImageView<2, T> a{
CompressedPixelFormat::Bc1RGBAUnorm, {12, 8},
data, ImageFlag2D::Array};
CORRADE_COMPARE(a.flags(), 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.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<const void*>(data)); CORRADE_COMPARE(a.data(), static_cast<const void*>(data));
CORRADE_COMPARE(a.data().size(), 8); CORRADE_COMPARE(a.data().size(), 6*8);
} { } {
T data[8]{}; T data[8*16]{};
CompressedImageView<2, T> a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedImageView<2, T> a{
CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, CompressedPixelStorage{}.setRowLength(20),
CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10},
data, ImageFlag2D::Array}; data, ImageFlag2D::Array};
CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); CORRADE_COMPARE(a.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(a.storage().rowLength(), 20);
CORRADE_COMPARE(a.format(), CompressedPixelFormat::Bc1RGBAUnorm); CORRADE_COMPARE(a.format(), CompressedPixelFormat::Astc5x5x4RGBAF);
CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); 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<const void*>(data)); CORRADE_COMPARE(a.data(), static_cast<const void*>(data));
CORRADE_COMPARE(a.data().size(), 8); CORRADE_COMPARE(a.data().size(), 8*16);
} }
} }
@ -404,21 +445,27 @@ template<class T> void ImageViewTest::constructCompressedGenericEmpty() {
setTestCaseTemplateName(MutabilityTraits<T>::name()); setTestCaseTemplateName(MutabilityTraits<T>::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.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.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<const void*>(nullptr)); CORRADE_COMPARE(a.data(), static_cast<const void*>(nullptr));
} { } {
CompressedImageView<2, T> a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedImageView<2, T> a{
CompressedPixelFormat::Bc1RGBAUnorm, {8, 16}, ImageFlag2D::Array}; CompressedPixelStorage{}.setRowLength(20),
CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10},
ImageFlag2D::Array};
CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); CORRADE_COMPARE(a.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(a.storage().rowLength(), 20);
CORRADE_COMPARE(a.format(), CompressedPixelFormat::Bc1RGBAUnorm); CORRADE_COMPARE(a.format(), CompressedPixelFormat::Astc5x5x4RGBAF);
CORRADE_COMPARE(a.size(), (Vector2i{8, 16})); 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<const void*>(nullptr)); CORRADE_COMPARE(a.data(), static_cast<const void*>(nullptr));
} }
} }
@ -428,30 +475,54 @@ template<class T> void ImageViewTest::constructCompressedImplementationSpecific(
/* Format with autodetection */ /* Format with autodetection */
{ {
T data[8]{}; T data[6*8]{};
CompressedImageView<2, T> a{GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, CompressedImageView<2, T> a{
GL::CompressedPixelFormat::RGBS3tcDxt1, {12, 8},
data, ImageFlag2D::Array}; data, ImageFlag2D::Array};
CORRADE_COMPARE(a.flags(), 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.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<const void*>(data)); CORRADE_COMPARE(a.data(), static_cast<const void*>(data));
CORRADE_COMPARE(a.data().size(), 8); CORRADE_COMPARE(a.data().size(), 6*8);
} { } {
T data[8]{}; T data[8*16]{};
CompressedImageView<2, T> a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedImageView<2, T> a{
GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, data, ImageFlag2D::Array}; CompressedPixelStorage{}.setRowLength(20),
Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10},
data, ImageFlag2D::Array};
CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); CORRADE_COMPARE(a.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(a.storage().rowLength(), 20);
CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF));
CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); 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<const void*>(data)); CORRADE_COMPARE(a.data(), static_cast<const void*>(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<const void*>(data));
CORRADE_COMPARE(a.data().size(), 6*12);
}
} }
template<class T> void ImageViewTest::constructCompressedImplementationSpecificEmpty() { template<class T> void ImageViewTest::constructCompressedImplementationSpecificEmpty() {
@ -459,25 +530,47 @@ template<class T> void ImageViewTest::constructCompressedImplementationSpecificE
/* Format with autodetection */ /* 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.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.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<const void*>(nullptr)); CORRADE_COMPARE(a.data(), static_cast<const void*>(nullptr));
} { } {
CompressedImageView<2, T> a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedImageView<2, T> a{
GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 8}, ImageFlag2D::Array}; CompressedPixelStorage{}.setRowLength(20),
Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10}, ImageFlag2D::Array};
CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); CORRADE_COMPARE(a.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(a.storage().rowLength(), 20);
CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF));
CORRADE_COMPARE(a.size(), (Vector2i{4, 8})); 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<const void*>(nullptr)); CORRADE_COMPARE(a.data(), static_cast<const void*>(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<const void*>(nullptr));
}
} }
void ImageViewTest::construct3DFrom1D() { void ImageViewTest::construct3DFrom1D() {
@ -549,29 +642,35 @@ void ImageViewTest::construct3DFrom2D() {
void ImageViewTest::constructCompressed3DFrom1D() { void ImageViewTest::constructCompressed3DFrom1D() {
/* Copied from constructCompressedImplementationSpecific(), as that exposes /* Copied from constructCompressedImplementationSpecific(), as that exposes
most fields */ most fields. It's okay to use a 2D format for a 1D image, only the first
/** @todo S3TC doesn't have 1D compression so this might blow up once we row of each block gets used. */
check for block sizes */ char data[4*16]{};
const char data[8]{}; CompressedImageView1D a{
/** @todo use a real flag once it exists */ CompressedPixelStorage{}.setRowLength(20),
CompressedImageView1D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), Vk::CompressedPixelFormat::Astc5x5x4RGBAF, 15,
GL::CompressedPixelFormat::RGBS3tcDxt1, 4, data, ImageFlag1D(0xdea0)}; data, ImageFlag1D(0xdea0)};
CORRADE_COMPARE(a.flags(), ImageFlag1D(0xdea0)); CORRADE_COMPARE(a.flags(), ImageFlag1D(0xdea0));
CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(a.storage().rowLength(), 20);
CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF));
CORRADE_COMPARE(a.size(), 4); CORRADE_COMPARE(a.blockSize(), (Vector3i{5, 5, 4}));
CORRADE_COMPARE(a.blockDataSize(), 16);
CORRADE_COMPARE(a.size(), 15);
CORRADE_COMPARE(a.data(), static_cast<const void*>(data)); CORRADE_COMPARE(a.data(), static_cast<const void*>(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 /* Not testing the flags parameter here to be sure implicit conversion
works as well, it's tested in constructCompressed3DFrom2D() below */ works as well, it's tested in constructCompressed3DFrom2D() below */
CompressedImageView3D b = a; CompressedImageView3D b = a;
CORRADE_COMPARE(b.flags(), ImageFlag3D(0xdea0)); CORRADE_COMPARE(b.flags(), ImageFlag3D(0xdea0));
CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(b.storage().rowLength(), 20);
CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF));
CORRADE_COMPARE(b.size(), (Vector3i{4, 1, 1})); 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<const void*>(data)); CORRADE_COMPARE(b.data(), static_cast<const void*>(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 /* Conversion the other way is not allowed (will be later, but explicitly
via a slice<1>() like with StridedArrayView); conversion from const to via a slice<1>() like with StridedArrayView); conversion from const to
@ -584,25 +683,32 @@ void ImageViewTest::constructCompressed3DFrom1D() {
void ImageViewTest::constructCompressed3DFrom2D() { void ImageViewTest::constructCompressed3DFrom2D() {
/* Copied from constructCompressedImplementationSpecific(), as that exposes /* Copied from constructCompressedImplementationSpecific(), as that exposes
most fields */ most fields */
char data[8*2]{}; char data[8*16]{};
MutableCompressedImageView2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), MutableCompressedImageView2D a{
GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 8}, data, ImageFlag2D::Array|ImageFlag2D(0xde00)}; CompressedPixelStorage{}.setRowLength(20),
Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10},
data, ImageFlag2D::Array|ImageFlag2D(0xde00)};
CORRADE_COMPARE(a.flags(), ImageFlag2D::Array|ImageFlag2D(0xde00)); CORRADE_COMPARE(a.flags(), ImageFlag2D::Array|ImageFlag2D(0xde00));
CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(a.storage().rowLength(), 20);
CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF));
CORRADE_COMPARE(a.size(), (Vector2i{4, 8})); 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<const void*>(data)); CORRADE_COMPARE(a.data(), static_cast<const void*>(data));
CORRADE_COMPARE(a.data().size(), 8*2); CORRADE_COMPARE(a.data().size(), 8*16);
MutableCompressedImageView3D b = {a, ImageFlag3D(0x00a0)}; MutableCompressedImageView3D b = {a, ImageFlag3D(0x00a0)};
/* The Array flag got implicitly stripped away, the rest got passed through /* The Array flag got implicitly stripped away, the rest got passed through
and combined with the flags argument */ and combined with the flags argument */
CORRADE_COMPARE(b.flags(), ImageFlag3D(0xdea0)); CORRADE_COMPARE(b.flags(), ImageFlag3D(0xdea0));
CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(b.storage().rowLength(), 20);
CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF));
CORRADE_COMPARE(b.size(), (Vector3i{4, 8, 1})); 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<const void*>(data)); CORRADE_COMPARE(b.data(), static_cast<const void*>(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 /* Conversion the other way is not allowed (will be later, but explicitly
via a slice<1>() like with StridedArrayView) */ via a slice<1>() like with StridedArrayView) */
@ -638,23 +744,30 @@ void ImageViewTest::constructFromMutable() {
void ImageViewTest::constructCompressedFromMutable() { void ImageViewTest::constructCompressedFromMutable() {
/* Copied from constructCompressedImplementationSpecific(), as that exposes /* Copied from constructCompressedImplementationSpecific(), as that exposes
most fields */ most fields */
char data[8]{}; char data[8*16]{};
MutableCompressedImageView2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), MutableCompressedImageView2D a{
GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, data, ImageFlag2D::Array}; CompressedPixelStorage{}.setRowLength(20),
Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10},
data, ImageFlag2D::Array};
CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); CORRADE_COMPARE(a.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(a.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(a.storage().rowLength(), 20);
CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(a.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF));
CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); 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<const void*>(data)); CORRADE_COMPARE(a.data(), static_cast<const void*>(data));
CORRADE_COMPARE(a.data().size(), 8); CORRADE_COMPARE(a.data().size(), 8*16);
CompressedImageView2D b = a; CompressedImageView2D b = a;
CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); CORRADE_COMPARE(b.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(b.storage().rowLength(), 20);
CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF));
CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); 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<const void*>(data)); CORRADE_COMPARE(b.data(), static_cast<const void*>(data));
CORRADE_COMPARE(b.data().size(), 8); CORRADE_COMPARE(b.data().size(), 8*16);
} }
void ImageViewTest::constructUnknownImplementationSpecificPixelSize() { void ImageViewTest::constructUnknownImplementationSpecificPixelSize() {
@ -729,10 +842,126 @@ void ImageViewTest::constructInvalidCubeMap() {
"ImageView: expected exactly 6 faces for a cube map, got 5\n"); "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_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]{}; const char data[15]{};
@ -785,18 +1014,16 @@ void ImageViewTest::dataProperties() {
} }
void ImageViewTest::dataPropertiesCompressed() { void ImageViewTest::dataPropertiesCompressed() {
/* Yes, I know, this is totally bogus and doesn't match the BC1 format */ const char data[336]{};
const char data[1]{};
CompressedImageView3D image{ CompressedImageView3D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({3, 4, 5}) .setRowLength(12)
.setCompressedBlockDataSize(16) .setImageHeight(8)
.setImageHeight(12) .setSkip({8, 4, 4}),
.setSkip({5, 8, 11}), CompressedPixelFormat::Bc1RGBAUnorm, {2, 3, 3},
CompressedPixelFormat::Bc1RGBAUnorm, {2, 8, 11},
data}; data};
CORRADE_COMPARE(image.dataProperties(), CORRADE_COMPARE(image.dataProperties(),
(std::pair<Math::Vector3<std::size_t>, Math::Vector3<std::size_t>>{{2*16, 2*16, 9*16}, {1, 3, 3}})); (std::pair<Math::Vector3<std::size_t>, Math::Vector3<std::size_t>>{{16, 24, 192}, {3, 2, 3}}));
} }
template<class T> void ImageViewTest::setData() { template<class T> void ImageViewTest::setData() {
@ -811,23 +1038,23 @@ template<class T> void ImageViewTest::setData() {
CORRADE_COMPARE(a.storage().alignment(), 1); CORRADE_COMPARE(a.storage().alignment(), 1);
CORRADE_COMPARE(a.format(), PixelFormat::RGB8Snorm); CORRADE_COMPARE(a.format(), PixelFormat::RGB8Snorm);
CORRADE_COMPARE(a.size(), Vector2i(1, 3)); CORRADE_COMPARE(a.size(), Vector2i(1, 3));
CORRADE_COMPARE(a.data(), &data2[0]); CORRADE_COMPARE(a.data(), static_cast<const void*>(data2));
} }
template<class T> void ImageViewTest::setDataCompressed() { template<class T> void ImageViewTest::setDataCompressed() {
setTestCaseTemplateName(MutabilityTraits<T>::name()); setTestCaseTemplateName(MutabilityTraits<T>::name());
T data[8]{}; T data[2*8]{};
CompressedImageView<2, T> a{ CompressedImageView<2, T> a{
CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedPixelStorage{}.setRowLength(8),
CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, data}; CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, data};
T data2[16]{}; T data2[2*8]{};
a.setData(data2); 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.format(), CompressedPixelFormat::Bc1RGBAUnorm);
CORRADE_COMPARE(a.size(), Vector2i(4, 4)); CORRADE_COMPARE(a.size(), Vector2i(4, 4));
CORRADE_COMPARE(a.data(), &data2[0]); CORRADE_COMPARE(a.data(), static_cast<const void*>(data2));
} }
void ImageViewTest::setDataInvalidSize() { void ImageViewTest::setDataInvalidSize() {
@ -845,8 +1072,6 @@ void ImageViewTest::setDataInvalidSize() {
void ImageViewTest::setDataCompressedInvalidSize() { void ImageViewTest::setDataCompressedInvalidSize() {
CORRADE_SKIP_IF_NO_ASSERT(); CORRADE_SKIP_IF_NO_ASSERT();
CORRADE_EXPECT_FAIL("Size checking for compressed image data is not implemented yet.");
const char data[11]{}; const char data[11]{};
/* Too small for given format */ /* Too small for given format */

85
src/Magnum/Test/PixelStorageTest.cpp

@ -302,10 +302,10 @@ void PixelStorageTest::dataOffsetSizeCompressed1D() {
/* Image size in whole blocks, no skip */ /* Image size in whole blocks, no skip */
{ {
CompressedImageView1D image{ CompressedImageView1D image{
CompressedPixelStorage{} CompressedPixelStorage{},
.setCompressedBlockSize({5, 1, 1})
.setCompressedBlockDataSize(8),
42069, /* custom format */ 42069, /* custom format */
{5, 1, 1},
8,
1, /* this is ignored, the passed size is used instead */ 1, /* this is ignored, the passed size is used instead */
data}; data};
CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, (Math::Vector<1, Int>{55})), (std::pair<std::size_t, std::size_t>{ CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, (Math::Vector<1, Int>{55})), (std::pair<std::size_t, std::size_t>{
@ -316,10 +316,10 @@ void PixelStorageTest::dataOffsetSizeCompressed1D() {
} { } {
CompressedImageView1D image{ CompressedImageView1D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({5, 1, 1})
.setCompressedBlockDataSize(8)
.setSkip({10, 0, 0}), .setSkip({10, 0, 0}),
42069, 42069,
{5, 1, 1},
8,
1, 1,
data}; data};
std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Math::Vector<1, Int>{35}); std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Math::Vector<1, Int>{35});
@ -336,10 +336,10 @@ void PixelStorageTest::dataOffsetSizeCompressed1D() {
} { } {
CompressedImageView1D image{ CompressedImageView1D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({5, 1, 1})
.setCompressedBlockDataSize(8)
.setSkip({10, 0, 0}), .setSkip({10, 0, 0}),
42069, 42069,
{5, 1, 1},
8,
1, 1,
data}; data};
CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, (Math::Vector<1, Int>{35})), (std::pair<std::size_t, std::size_t>{ CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, (Math::Vector<1, Int>{35})), (std::pair<std::size_t, std::size_t>{
@ -356,10 +356,10 @@ void PixelStorageTest::dataOffsetSizeCompressed2D() {
/* Image size in whole blocks, no skip */ /* Image size in whole blocks, no skip */
{ {
CompressedImageView2D image{ CompressedImageView2D image{
CompressedPixelStorage{} CompressedPixelStorage{},
.setCompressedBlockSize({5, 4, 1})
.setCompressedBlockDataSize(8),
42069, /* custom format */ 42069, /* custom format */
{5, 4, 1},
8,
{1, 1}, /* this is ignored, the passed size is used instead */ {1, 1}, /* this is ignored, the passed size is used instead */
data}; data};
CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{55, 28}), (std::pair<std::size_t, std::size_t>{ CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{55, 28}), (std::pair<std::size_t, std::size_t>{
@ -370,11 +370,11 @@ void PixelStorageTest::dataOffsetSizeCompressed2D() {
} { } {
CompressedImageView2D image{ CompressedImageView2D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({5, 4, 1})
.setCompressedBlockDataSize(8)
.setRowLength(45) .setRowLength(45)
.setSkip({10, 0, 0}), .setSkip({10, 0, 0}),
42069, 42069,
{5, 4, 1},
8,
{1, 1}, {1, 1},
data}; data};
std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{35, 20}); std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{35, 20});
@ -392,10 +392,10 @@ void PixelStorageTest::dataOffsetSizeCompressed2D() {
} { } {
CompressedImageView2D image{ CompressedImageView2D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({5, 4, 1})
.setCompressedBlockDataSize(8)
.setSkip({0, 8, 0}), .setSkip({0, 8, 0}),
42069, 42069,
{5, 4, 1},
8,
{1, 1}, {1, 1},
data}; data};
std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{35, 20}); std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{35, 20});
@ -412,11 +412,11 @@ void PixelStorageTest::dataOffsetSizeCompressed2D() {
} { } {
CompressedImageView2D image{ CompressedImageView2D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({5, 4, 1})
.setCompressedBlockDataSize(8)
.setRowLength(45) .setRowLength(45)
.setSkip({0, 8, 0}), .setSkip({0, 8, 0}),
42069, 42069,
{5, 4, 1},
8,
{1, 1}, {1, 1},
data}; data};
std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{35, 20}); std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{35, 20});
@ -433,11 +433,11 @@ void PixelStorageTest::dataOffsetSizeCompressed2D() {
} { } {
CompressedImageView2D image{ CompressedImageView2D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({5, 4, 1})
.setCompressedBlockDataSize(8)
.setRowLength(45) .setRowLength(45)
.setSkip({10, 8, 0}), .setSkip({10, 8, 0}),
42069, 42069,
{5, 4, 1},
8,
{1, 1}, {1, 1},
data}; data};
std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{35, 20}); std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{35, 20});
@ -454,11 +454,11 @@ void PixelStorageTest::dataOffsetSizeCompressed2D() {
} { } {
CompressedImageView2D image{ CompressedImageView2D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({5, 4, 1})
.setCompressedBlockDataSize(8)
.setRowLength(41) .setRowLength(41)
.setSkip({10, 8, 0}), .setSkip({10, 8, 0}),
42069, 42069,
{5, 4, 1},
8,
{1, 1}, {1, 1},
data}; data};
CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{35, 20}), (std::pair<std::size_t, std::size_t>{ CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, Vector2i{35, 20}), (std::pair<std::size_t, std::size_t>{
@ -475,10 +475,10 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() {
/* Image size in whole blocks, no offset */ /* Image size in whole blocks, no offset */
{ {
CompressedImageView3D image{ CompressedImageView3D image{
CompressedPixelStorage{} CompressedPixelStorage{},
.setCompressedBlockSize({5, 4, 2})
.setCompressedBlockDataSize(16),
42069, /* custom format */ 42069, /* custom format */
{5, 4, 2},
16,
{1, 1, 1}, /* this is ignored, the passed size is used instead */ {1, 1, 1}, /* this is ignored, the passed size is used instead */
data}; data};
CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{45, 28, 6}), (std::pair<std::size_t, std::size_t>{ CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{45, 28, 6}), (std::pair<std::size_t, std::size_t>{
@ -489,11 +489,11 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() {
} { } {
CompressedImageView3D image{ CompressedImageView3D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({5, 4, 2})
.setCompressedBlockDataSize(16)
.setRowLength(45) .setRowLength(45)
.setSkip({10, 0, 0}), .setSkip({10, 0, 0}),
42069, 42069,
{5, 4, 2},
16,
{1, 1, 1}, {1, 1, 1},
data}; data};
std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6}); std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6});
@ -511,12 +511,12 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() {
} { } {
CompressedImageView3D image{ CompressedImageView3D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({5, 4, 2})
.setCompressedBlockDataSize(16)
.setRowLength(45) .setRowLength(45)
.setImageHeight(28) .setImageHeight(28)
.setSkip({20, 0, 0}), .setSkip({20, 0, 0}),
42069, 42069,
{5, 4, 2},
16,
{1, 1, 1}, {1, 1, 1},
data}; data};
std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6}); std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6});
@ -534,11 +534,11 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() {
} { } {
CompressedImageView3D image{ CompressedImageView3D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({5, 4, 2})
.setCompressedBlockDataSize(16)
.setImageHeight(28) .setImageHeight(28)
.setSkip({0, 8, 0}), .setSkip({0, 8, 0}),
42069, 42069,
{5, 4, 2},
16,
{1, 1, 1}, {1, 1, 1},
data}; data};
std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6}); std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6});
@ -556,12 +556,12 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() {
} { } {
CompressedImageView3D image{ CompressedImageView3D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({5, 4, 2})
.setCompressedBlockDataSize(16)
.setRowLength(45) .setRowLength(45)
.setImageHeight(28) .setImageHeight(28)
.setSkip({0, 8, 0}), .setSkip({0, 8, 0}),
42069, 42069,
{5, 4, 2},
16,
{1, 1, 1}, {1, 1, 1},
data}; data};
std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6}); std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6});
@ -579,12 +579,12 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() {
} { } {
CompressedImageView3D image{ CompressedImageView3D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({5, 4, 2})
.setCompressedBlockDataSize(16)
.setRowLength(45) .setRowLength(45)
.setImageHeight(28) .setImageHeight(28)
.setSkip({0, 0, 4}), .setSkip({0, 0, 4}),
42069, 42069,
{5, 4, 2},
16,
{1, 1, 1}, {1, 1, 1},
data}; data};
std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6}); std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6});
@ -601,12 +601,12 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() {
} { } {
CompressedImageView3D image{ CompressedImageView3D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({5, 4, 2})
.setCompressedBlockDataSize(16)
.setRowLength(45) .setRowLength(45)
.setImageHeight(28) .setImageHeight(28)
.setSkip({10, 8, 4}), .setSkip({10, 8, 4}),
42069, 42069,
{5, 4, 2},
16,
{1, 1, 1}, {1, 1, 1},
data}; data};
std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6}); std::pair<std::size_t, std::size_t> out = Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6});
@ -624,12 +624,12 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() {
} { } {
CompressedImageView3D image{ CompressedImageView3D image{
CompressedPixelStorage{} CompressedPixelStorage{}
.setCompressedBlockSize({5, 4, 2})
.setCompressedBlockDataSize(16)
.setRowLength(41) .setRowLength(41)
.setImageHeight(27) .setImageHeight(27)
.setSkip({10, 8, 4}), .setSkip({10, 8, 4}),
42069, 42069,
{5, 4, 2},
16,
{1, 1, 1}, {1, 1, 1},
data}; data};
CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6}), (std::pair<std::size_t, std::size_t>{ CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{35, 20, 6}), (std::pair<std::size_t, std::size_t>{
@ -639,9 +639,14 @@ void PixelStorageTest::dataOffsetSizeCompressed3D() {
} }
void PixelStorageTest::dataOffsetSizeCompressedZeroSize() { void PixelStorageTest::dataOffsetSizeCompressedZeroSize() {
const CompressedImage3D image{CompressedPixelStorage{} char data[16];
.setCompressedBlockSize({4, 4, 1}) const CompressedImageView3D image{
.setCompressedBlockDataSize(16)}; 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}), CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{0, 4, 4}),
(std::pair<std::size_t, std::size_t>{})); (std::pair<std::size_t, std::size_t>{}));
CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{4, 0, 4}), CORRADE_COMPARE(Implementation::compressedImageDataOffsetSizeFor(image, Vector3i{4, 0, 4}),

93
src/Magnum/Trade/ImageData.cpp

@ -72,32 +72,68 @@ template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const PixelSto
_dataFlags = dataFlags; _dataFlags = dataFlags;
} }
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const ImageFlags<dimensions> 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<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const ImageFlags<dimensions> 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 #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<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const CompressedPixelFormat format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const ImageFlags<dimensions> 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<void>(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); Magnum::Implementation::checkImageFlagsForSize("Trade::ImageData:", flags, size);
#endif #endif
} }
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const UnsignedInt format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const ImageFlags<dimensions> flags, const void* const importerState) noexcept: ImageData{storage, compressedPixelFormatWrap(format), blockSize, blockDataSize, size, Utility::move(data), flags, importerState} {}
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const ImageFlags<dimensions> flags, const void* const importerState) noexcept: ImageData{storage, format, size, Containers::Array<char>{const_cast<char*>(static_cast<const char*>(data.data())), data.size(), Implementation::nonOwnedArrayDeleter}, flags, importerState} { template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const ImageFlags<dimensions> flags, const void* const importerState) noexcept: ImageData{storage, format, size, Containers::Array<char>{const_cast<char*>(static_cast<const char*>(data.data())), data.size(), Implementation::nonOwnedArrayDeleter}, flags, importerState} {
CORRADE_ASSERT(!(dataFlags & DataFlag::Owned), CORRADE_ASSERT(!(dataFlags & DataFlag::Owned),
"Trade::ImageData: can't construct a non-owned instance with" << dataFlags, ); "Trade::ImageData: can't construct a non-owned instance with" << dataFlags, );
_dataFlags = dataFlags; _dataFlags = dataFlags;
} }
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const ImageFlags<dimensions> flags, const void* const importerState) noexcept: ImageData{{}, format, size, Utility::move(data), flags, importerState} {} template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const CompressedPixelFormat format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const ImageFlags<dimensions> flags, const void* const importerState) noexcept: ImageData{storage, format, blockSize, blockDataSize, size, Containers::Array<char>{const_cast<char*>(static_cast<const char*>(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<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const ImageFlags<dimensions> flags, const void* const importerState) noexcept: ImageData{{}, format, size, dataFlags, data, flags, importerState} {} template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const UnsignedInt format, const Vector3i& blockSize, const UnsignedInt blockDataSize, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const ImageFlags<dimensions> flags, const void* const importerState) noexcept: ImageData{storage, compressedPixelFormatWrap(format), blockSize, blockDataSize, size, dataFlags, data, flags, importerState} {}
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const UnsignedInt format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const ImageFlags<dimensions> flags, const void* const importerState) noexcept: ImageData{storage, compressedPixelFormatWrap(format), size, Utility::move(data), flags, importerState} {} template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const ImageFlags<dimensions> flags, const void* const importerState) noexcept: ImageData{{}, format, size, Utility::move(data), flags, importerState} {}
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const UnsignedInt format, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const ImageFlags<dimensions> flags, const void* const importerState) noexcept: ImageData{storage, compressedPixelFormatWrap(format), size, dataFlags, data, flags, importerState} {} template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const ImageFlags<dimensions> flags, const void* const importerState) noexcept: ImageData{{}, format, size, dataFlags, data, flags, importerState} {}
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(ImageData<dimensions>&& 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)} { template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(ImageData<dimensions>&& 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) { if(_compressed) {
new(&_compressedStorage) CompressedPixelStorage{Utility::move(other._compressedStorage)}; new(&_compressedStorage) CompressedPixelStorage{other._compressedStorage};
_compressedFormat = Utility::move(other._compressedFormat); _compressedFormat = other._compressedFormat;
} _blockSize = other._blockSize;
else { _blockDataSize = other._blockDataSize;
} else {
new(&_storage) PixelStorage{Utility::move(other._storage)}; new(&_storage) PixelStorage{Utility::move(other._storage)};
_format = Utility::move(other._format); _format = Utility::move(other._format);
_formatExtra = Utility::move(other._formatExtra); _formatExtra = Utility::move(other._formatExtra);
@ -116,20 +152,26 @@ template<UnsignedInt dimensions> ImageData<dimensions>& ImageData<dimensions>::o
swap(_dataFlags, other._dataFlags); swap(_dataFlags, other._dataFlags);
swap(_compressed, other._compressed); swap(_compressed, other._compressed);
swap(_flags, other._flags); swap(_flags, other._flags);
/* Because the CompressedPixelStorage is larger than the /* Because the CompressedPixelStorage is larger than the
uncompressed, copy it if either of the sides is compressed to ensure no uncompressed, copy it if either of the sides is compressed to ensure no
compressed properties are lost. The _storage / _compressedStorage and compressed properties are lost. The _storage / _compressedStorage and
_format / _compressedFormat are unions of trivially copyable contents so _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) { if(_compressed || other._compressed) {
swap(_compressedStorage, other._compressedStorage); swap(_compressedStorage, other._compressedStorage);
swap(_compressedFormat, other._compressedFormat); swap(_compressedFormat, other._compressedFormat);
swap(_blockDataSize, other._blockDataSize);
} else { } else {
swap(_storage, other._storage); swap(_storage, other._storage);
swap(_format, other._format); swap(_format, other._format);
swap(_pixelSize, other._pixelSize);
} }
swap(_formatExtra, other._formatExtra); swap(_formatExtra, other._formatExtra);
swap(_pixelSize, other._pixelSize);
swap(_size, other._size); swap(_size, other._size);
swap(_data, other._data); swap(_data, other._data);
swap(_importerState, other._importerState); swap(_importerState, other._importerState);
@ -166,11 +208,26 @@ template<UnsignedInt dimensions> UnsignedInt ImageData<dimensions>::pixelSize()
return _pixelSize; return _pixelSize;
} }
template<UnsignedInt dimensions> Vector3i ImageData<dimensions>::blockSize() const {
CORRADE_ASSERT(_compressed, "Trade::ImageData::blockSize(): the image is not compressed", {});
return Vector3i{_blockSize};
}
template<UnsignedInt dimensions> UnsignedInt ImageData<dimensions>::blockDataSize() const {
CORRADE_ASSERT(_compressed, "Trade::ImageData::blockDataSize(): the image is not compressed", {});
return _blockDataSize;
}
template<UnsignedInt dimensions> std::pair<VectorTypeFor<dimensions, std::size_t>, VectorTypeFor<dimensions, std::size_t>> ImageData<dimensions>::dataProperties() const { template<UnsignedInt dimensions> std::pair<VectorTypeFor<dimensions, std::size_t>, VectorTypeFor<dimensions, std::size_t>> ImageData<dimensions>::dataProperties() const {
CORRADE_ASSERT(!_compressed, "Trade::ImageData::dataProperties(): the image is compressed", {}); CORRADE_ASSERT(!_compressed, "Trade::ImageData::dataProperties(): the image is compressed", {});
return Magnum::Implementation::imageDataProperties<dimensions>(*this); return Magnum::Implementation::imageDataProperties<dimensions>(*this);
} }
template<UnsignedInt dimensions> std::pair<VectorTypeFor<dimensions, std::size_t>, VectorTypeFor<dimensions, std::size_t>> ImageData<dimensions>::compressedDataProperties() const {
CORRADE_ASSERT(_compressed, "Trade::ImageData::compressedDataProperties(): the image is not compressed", {});
return Magnum::Implementation::compressedImageDataProperties<dimensions>(*this);
}
template<UnsignedInt dimensions> Containers::ArrayView<char> ImageData<dimensions>::mutableData() & { template<UnsignedInt dimensions> Containers::ArrayView<char> ImageData<dimensions>::mutableData() & {
CORRADE_ASSERT(_dataFlags & DataFlag::Mutable, CORRADE_ASSERT(_dataFlags & DataFlag::Mutable,
"Trade::ImageData::mutableData(): the image is not mutable", {}); "Trade::ImageData::mutableData(): the image is not mutable", {});
@ -202,20 +259,16 @@ template<UnsignedInt dimensions> ImageData<dimensions>::operator BasicMutableIma
} }
template<UnsignedInt dimensions> ImageData<dimensions>::operator BasicCompressedImageView<dimensions>() const { template<UnsignedInt dimensions> ImageData<dimensions>::operator BasicCompressedImageView<dimensions>() const {
CORRADE_ASSERT(_compressed, "Trade::ImageData: the image is not compressed", (BasicCompressedImageView<dimensions>{_compressedStorage, _compressedFormat, _size})); CORRADE_ASSERT(_compressed, "Trade::ImageData: the image is not compressed", (BasicCompressedImageView<dimensions>{_compressedStorage, _compressedFormat, Vector3i{_blockSize}, _blockDataSize, _size}));
return BasicCompressedImageView<dimensions>{ return BasicCompressedImageView<dimensions>{_compressedStorage, _compressedFormat, Vector3i{_blockSize}, _blockDataSize, _size, _data, _flags};
_compressedStorage,
_compressedFormat, _size, _data, _flags};
} }
template<UnsignedInt dimensions> ImageData<dimensions>::operator BasicMutableCompressedImageView<dimensions>() { template<UnsignedInt dimensions> ImageData<dimensions>::operator BasicMutableCompressedImageView<dimensions>() {
CORRADE_ASSERT(_dataFlags & DataFlag::Mutable, CORRADE_ASSERT(_dataFlags & DataFlag::Mutable,
"Trade::ImageData: the image is not mutable", "Trade::ImageData: the image is not mutable",
(BasicMutableCompressedImageView<dimensions>{_compressedStorage, _compressedFormat, _size})); (BasicMutableCompressedImageView<dimensions>{_compressedStorage, _compressedFormat, Vector3i{_blockSize}, _blockDataSize, _size}));
CORRADE_ASSERT(_compressed, "Trade::ImageData: the image is not compressed", (BasicMutableCompressedImageView<dimensions>{_compressedStorage, _compressedFormat, _size})); CORRADE_ASSERT(_compressed, "Trade::ImageData: the image is not compressed", (BasicMutableCompressedImageView<dimensions>{_compressedStorage, _compressedFormat, Vector3i{_blockSize}, _blockDataSize, _size}));
return BasicMutableCompressedImageView<dimensions>{ return BasicMutableCompressedImageView<dimensions>{_compressedStorage, _compressedFormat, Vector3i{_blockSize}, _blockDataSize, _size, _data, _flags};
_compressedStorage,
_compressedFormat, _size, _data, _flags};
} }
template<UnsignedInt dimensions> Containers::Array<char> ImageData<dimensions>::release() { template<UnsignedInt dimensions> Containers::Array<char> ImageData<dimensions>::release() {

165
src/Magnum/Trade/ImageData.h

@ -476,6 +476,24 @@ template<UnsignedInt dimensions> class ImageData {
* @param flags Image layout flags * @param flags Image layout flags
* @param importerState Importer-specific state * @param importerState Importer-specific state
* @m_since_latest * @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<dimensions, Int>&, Containers::Array<char>&&, ImageFlags<dimensions>, 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<dimensions, Int>&, Containers::Array<char>&&, ImageFlags<dimensions>, 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<dimensions, Int>& size, Containers::Array<char>&& data, ImageFlags<dimensions> flags = {}, const void* importerState = nullptr) noexcept; explicit ImageData(CompressedPixelStorage storage, CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, ImageFlags<dimensions> flags = {}, const void* importerState = nullptr) noexcept;
@ -564,20 +582,65 @@ template<UnsignedInt dimensions> class ImageData {
#endif #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 storage Storage of compressed pixel data
* @param format Format 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 data Image data
* @param flags Image layout flags * @param flags Image layout flags
* @param importerState Importer-specific state * @param importerState Importer-specific state
* @m_since_latest * @m_since_latest
* *
* Uses @ref compressedPixelFormatWrap() internally to convert * Unlike with @ref ImageData(CompressedPixelStorage, CompressedPixelFormat, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, ImageFlags<dimensions>, const void*),
* @p format to @ref CompressedPixelFormat. * 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<dimensions, Int>& size, Containers::Array<char>&& data, ImageFlags<dimensions> 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<dimensions, Int>& size, Containers::Array<char>&& data, ImageFlags<dimensions> 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 * Uses ADL to find a corresponding @cpp compressedPixelFormatBlockSize(T) @ce
* @p size is expected to match its restrictions. * and @cpp compressedPixelFormatBlockDataSize(T) @ce overloads, then
* calls @ref ImageData(CompressedPixelStorage, UnsignedInt, const Vector3i&, UnsignedInt, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, ImageFlags<dimensions>, const void*)
* with determined block size properties.
*/ */
template<class T> explicit ImageData(CompressedPixelStorage storage, T format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, ImageFlags<dimensions> flags = {}, const void* importerState = nullptr) noexcept; template<class T> explicit ImageData(CompressedPixelStorage storage, T format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, ImageFlags<dimensions> flags = {}, const void* importerState = nullptr) noexcept;
@ -591,22 +654,52 @@ template<UnsignedInt dimensions> class ImageData {
#endif #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 storage Storage of compressed pixel data
* @param format Format 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 dataFlags Data flags
* @param data View on image data * @param data Image data
* @param flags Image layout flags * @param flags Image layout flags
* @param importerState Importer-specific state * @param importerState Importer-specific state
* @m_since_latest * @m_since_latest
* *
* Compared to @ref ImageData(CompressedPixelStorage, T, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, ImageFlags<dimensions>, const void*) * Compared to @ref ImageData(CompressedPixelStorage, UnsignedInt, const Vector3i&, UnsignedInt, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, ImageFlags<dimensions>, const void*)
* creates an instance that doesn't own the passed data. The * creates an instance that doesn't own the passed data. The
* @p dataFlags parameter can contain @ref DataFlag::Mutable to * @p dataFlags parameter can contain @ref DataFlag::Mutable to
* indicate the external data can be modified, and is expected to *not* * indicate the external data can be modified, and is expected to *not*
* have @ref DataFlag::Owned set. * have @ref DataFlag::Owned set.
*/ */
explicit ImageData(CompressedPixelStorage storage, UnsignedInt format, const Vector3i& blockSize, UnsignedInt blockDataSize, const VectorTypeFor<dimensions, Int>& size, DataFlags dataFlags, Containers::ArrayView<const void> data, ImageFlags<dimensions> 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<dimensions, Int>& size, DataFlags dataFlags, Containers::ArrayView<const void> data, ImageFlags<dimensions> 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<dimensions, Int>&, DataFlags, Containers::ArrayView<const void>, ImageFlags<dimensions>, const void*)
* with determined block size properties.
*/
template<class T> explicit ImageData(CompressedPixelStorage storage, T format, const VectorTypeFor<dimensions, Int>& size, DataFlags dataFlags, Containers::ArrayView<const void> data, ImageFlags<dimensions> flags = {}, const void* importerState = nullptr) noexcept; template<class T> explicit ImageData(CompressedPixelStorage storage, T format, const VectorTypeFor<dimensions, Int>& size, DataFlags dataFlags, Containers::ArrayView<const void> data, ImageFlags<dimensions> flags = {}, const void* importerState = nullptr) noexcept;
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
@ -759,6 +852,28 @@ template<UnsignedInt dimensions> class ImageData {
*/ */
UnsignedInt pixelSize() const; 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 */ /** @brief Image size in pixels */
/* Unlike other getters this one is a const& so it's possible to slice /* 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 to the sizes when all images are in an array, for example for use
@ -774,9 +889,15 @@ template<UnsignedInt dimensions> class ImageData {
*/ */
std::pair<VectorTypeFor<dimensions, std::size_t>, VectorTypeFor<dimensions, std::size_t>> dataProperties() const; std::pair<VectorTypeFor<dimensions, std::size_t>, VectorTypeFor<dimensions, std::size_t>> dataProperties() const;
/* compressed data properties are not available because the importers /**
are not setting any block size pixel storage properties to avoid * @brief Compressed image data properties
needless state changes -- thus the calculation can't be done */ * @m_since_latest
*
* The image is expected to be compressed. See
* @ref CompressedPixelStorage::dataProperties() for more information.
* @see @ref isCompressed()
*/
std::pair<VectorTypeFor<dimensions, std::size_t>, VectorTypeFor<dimensions, std::size_t>> compressedDataProperties() const;
/** /**
* @brief Raw image data * @brief Raw image data
@ -896,10 +1017,6 @@ template<UnsignedInt dimensions> class ImageData {
friend AbstractImporter; friend AbstractImporter;
friend AbstractImageConverter; friend AbstractImageConverter;
explicit ImageData(CompressedPixelStorage storage, UnsignedInt format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, ImageFlags<dimensions> flags, const void* importerState = nullptr) noexcept;
explicit ImageData(CompressedPixelStorage storage, UnsignedInt format, const VectorTypeFor<dimensions, Int>& size, DataFlags dataFlags, Containers::ArrayView<const void> data, ImageFlags<dimensions> flags, const void* importerState = nullptr) noexcept;
DataFlags _dataFlags; DataFlags _dataFlags;
bool _compressed; bool _compressed;
ImageFlags<dimensions> _flags; ImageFlags<dimensions> _flags;
@ -911,8 +1028,14 @@ template<UnsignedInt dimensions> class ImageData {
PixelFormat _format; PixelFormat _format;
CompressedPixelFormat _compressedFormat; CompressedPixelFormat _compressedFormat;
}; };
UnsignedInt _formatExtra; union {
UnsignedByte _pixelSize; UnsignedInt _formatExtra;
Vector3ub _blockSize;
};
union {
UnsignedByte _pixelSize;
UnsignedByte _blockDataSize;
};
/* 3 bytes free */ /* 3 bytes free */
VectorTypeFor<dimensions, Int> _size; VectorTypeFor<dimensions, Int> _size;
Containers::Array<char> _data; Containers::Array<char> _data;
@ -948,12 +1071,12 @@ template<UnsignedInt dimensions> template<class T> ImageData<dimensions>::ImageD
"format types larger than 32bits are not supported"); "format types larger than 32bits are not supported");
} }
template<UnsignedInt dimensions> template<class T> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const T format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const ImageFlags<dimensions> flags, const void* const importerState) noexcept: ImageData{storage, UnsignedInt(format), size, Utility::move(data), flags, importerState} { template<UnsignedInt dimensions> template<class T> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const T format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const ImageFlags<dimensions> 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, static_assert(sizeof(T) <= 4,
"format types larger than 32bits are not supported"); "format types larger than 32bits are not supported");
} }
template<UnsignedInt dimensions> template<class T> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const T format, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const ImageFlags<dimensions> flags, const void* const importerState) noexcept: ImageData{storage, UnsignedInt(format), size, dataFlags, data, flags, importerState} { template<UnsignedInt dimensions> template<class T> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const T format, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const ImageFlags<dimensions> flags, const void* const importerState) noexcept: ImageData{storage, UnsignedInt(format), compressedPixelFormatBlockSize(format), compressedPixelFormatBlockDataSize(format), size, dataFlags, data, flags, importerState} {
static_assert(sizeof(T) <= 4, static_assert(sizeof(T) <= 4,
"format types larger than 32bits are not supported"); "format types larger than 32bits are not supported");
} }

429
src/Magnum/Trade/Test/ImageDataTest.cpp

@ -62,6 +62,8 @@ struct ImageDataTest: TestSuite::Tester {
void constructInvalidPixelSize(); void constructInvalidPixelSize();
void constructInvalidSize(); void constructInvalidSize();
void constructInvalidCubeMap(); void constructInvalidCubeMap();
void constructCompressedUnknownImplementationSpecificBlockSize();
void constructCompressedInvalidBlockSize();
void constructCompressedInvalidSize(); void constructCompressedInvalidSize();
void constructCompressedInvalidCubeMap(); void constructCompressedInvalidCubeMap();
@ -90,6 +92,7 @@ struct ImageDataTest: TestSuite::Tester {
void mutableAccessNotAllowed(); void mutableAccessNotAllowed();
void dataProperties(); void dataProperties();
void dataPropertiesCompressed();
void release(); void release();
void releaseCompressed(); void releaseCompressed();
@ -140,6 +143,8 @@ ImageDataTest::ImageDataTest() {
&ImageDataTest::constructInvalidPixelSize, &ImageDataTest::constructInvalidPixelSize,
&ImageDataTest::constructInvalidSize, &ImageDataTest::constructInvalidSize,
&ImageDataTest::constructInvalidCubeMap, &ImageDataTest::constructInvalidCubeMap,
&ImageDataTest::constructCompressedUnknownImplementationSpecificBlockSize,
&ImageDataTest::constructCompressedInvalidBlockSize,
&ImageDataTest::constructCompressedInvalidSize, &ImageDataTest::constructCompressedInvalidSize,
&ImageDataTest::constructCompressedInvalidCubeMap, &ImageDataTest::constructCompressedInvalidCubeMap,
@ -172,6 +177,7 @@ ImageDataTest::ImageDataTest() {
&ImageDataTest::mutableAccessNotAllowed, &ImageDataTest::mutableAccessNotAllowed,
&ImageDataTest::dataProperties, &ImageDataTest::dataProperties,
&ImageDataTest::dataPropertiesCompressed,
&ImageDataTest::release, &ImageDataTest::release,
&ImageDataTest::releaseCompressed, &ImageDataTest::releaseCompressed,
@ -198,6 +204,20 @@ namespace GL {
} }
enum class CompressedPixelFormat { RGBS3tcDxt1 = 21 }; enum class CompressedPixelFormat { RGBS3tcDxt1 = 21 };
Vector3i compressedPixelFormatBlockSize(CompressedPixelFormat format) {
CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::RGBS3tcDxt1);
#ifdef CORRADE_NO_ASSERT
static_cast<void>(format);
#endif
return {4, 4, 1};
}
UnsignedInt compressedPixelFormatBlockDataSize(CompressedPixelFormat format) {
CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::RGBS3tcDxt1);
#ifdef CORRADE_NO_ASSERT
static_cast<void>(format);
#endif
return 8;
}
} }
namespace Vk { namespace Vk {
@ -212,6 +232,22 @@ namespace Vk {
#endif #endif
return 12; return 12;
} }
enum class CompressedPixelFormat { Astc5x5x4RGBAF = 111 };
Vector3i compressedPixelFormatBlockSize(CompressedPixelFormat format) {
#ifdef CORRADE_NO_ASSERT
static_cast<void>(format);
#endif
CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::Astc5x5x4RGBAF);
return {5, 5, 4};
}
UnsignedInt compressedPixelFormatBlockDataSize(CompressedPixelFormat format) {
#ifdef CORRADE_NO_ASSERT
static_cast<void>(format);
#endif
CORRADE_INTERNAL_ASSERT(format == CompressedPixelFormat::Astc5x5x4RGBAF);
return 16;
}
} }
void ImageDataTest::constructGeneric() { void ImageDataTest::constructGeneric() {
@ -333,39 +369,45 @@ void ImageDataTest::constructImplementationSpecific() {
void ImageDataTest::constructCompressedGeneric() { 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 {} */ int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */
ImageData2D a{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, ImageData2D a{
Containers::Array<char>{data, 8}, ImageFlag2D::Array, &state}; CompressedPixelFormat::Bc1RGBAUnorm, {12, 8},
Containers::Array<char>{data, 7*8}, ImageFlag2D::Array, &state};
CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(a.isCompressed()); CORRADE_VERIFY(a.isCompressed());
CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); 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.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<const void*>(a.data().data()), data); CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 8); CORRADE_COMPARE(a.data().size(), 7*8);
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data); CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 8); CORRADE_COMPARE(a.mutableData().size(), 7*8);
CORRADE_COMPARE(a.importerState(), &state); 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 {} */ int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */
ImageData2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), ImageData2D a{
CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, CompressedPixelStorage{}.setRowLength(20),
Containers::Array<char>{data, 8}, ImageFlag2D::Array, &state}; CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10},
Containers::Array<char>{data, 8*16}, ImageFlag2D::Array, &state};
CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(a.isCompressed()); CORRADE_VERIFY(a.isCompressed());
CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); CORRADE_COMPARE(a.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(a.compressedStorage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(a.compressedStorage().rowLength(), 20);
CORRADE_COMPARE(a.compressedFormat(), CompressedPixelFormat::Bc1RGBAUnorm); CORRADE_COMPARE(a.compressedFormat(), CompressedPixelFormat::Astc5x5x4RGBAF);
CORRADE_COMPARE(a.size(), Vector2i(4, 4)); CORRADE_COMPARE(a.blockSize(), (Vector3i{5, 5, 4}));
CORRADE_COMPARE(a.blockDataSize(), 16);
CORRADE_COMPARE(a.size(), (Vector2i{15, 10}));
CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data); CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 8); CORRADE_COMPARE(a.data().size(), 8*16);
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data); CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 8); CORRADE_COMPARE(a.mutableData().size(), 8*16);
CORRADE_COMPARE(a.importerState(), &state); CORRADE_COMPARE(a.importerState(), &state);
} }
} }
@ -373,26 +415,51 @@ void ImageDataTest::constructCompressedGeneric() {
void ImageDataTest::constructCompressedImplementationSpecific() { void ImageDataTest::constructCompressedImplementationSpecific() {
/* Format with autodetection */ /* 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 {} */ int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */
ImageData2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), ImageData2D a{
GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, CompressedPixelStorage{}.setRowLength(16),
Containers::Array<char>{data, 8}, ImageFlag2D::Array, &state}; GL::CompressedPixelFormat::RGBS3tcDxt1, {12, 8},
Containers::Array<char>{data, 8*8}, ImageFlag2D::Array, &state};
CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(a.isCompressed()); CORRADE_VERIFY(a.isCompressed());
CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); 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.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<const void*>(a.data().data()), data); CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 8); CORRADE_COMPARE(a.data().size(), 8*8);
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data); CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 8); CORRADE_COMPARE(a.mutableData().size(), 8*8);
CORRADE_COMPARE(a.importerState(), &state); 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<char>{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<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 6*12);
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 6*12);
CORRADE_COMPARE(a.importerState(), &state);
}
} }
void ImageDataTest::constructGenericNotOwned() { void ImageDataTest::constructGenericNotOwned() {
@ -533,42 +600,48 @@ void ImageDataTest::constructCompressedGenericNotOwned() {
setTestCaseDescription(instanceData.name); setTestCaseDescription(instanceData.name);
{ {
char data[8]; char data[6*8];
int state; int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */
ImageData2D a{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, ImageData2D a{
CompressedPixelFormat::Bc1RGBAUnorm, {12, 8},
instanceData.dataFlags, data, ImageFlag2D::Array, &state}; instanceData.dataFlags, data, ImageFlag2D::Array, &state};
CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags); CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags);
CORRADE_VERIFY(a.isCompressed()); CORRADE_VERIFY(a.isCompressed());
CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); 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.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<const void*>(a.data().data()), data); CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 8); CORRADE_COMPARE(a.data().size(), 6*8);
if(instanceData.dataFlags & DataFlag::Mutable) { if(instanceData.dataFlags & DataFlag::Mutable) {
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data); CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 8); CORRADE_COMPARE(a.mutableData().size(), 6*8);
} }
CORRADE_COMPARE(a.importerState(), &state); CORRADE_COMPARE(a.importerState(), &state);
} { } {
char data[8]; char data[8*16];
int state; int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */
ImageData2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), ImageData2D a{
CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, CompressedPixelStorage{}.setRowLength(20),
CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10},
instanceData.dataFlags, data, ImageFlag2D::Array, &state}; instanceData.dataFlags, data, ImageFlag2D::Array, &state};
CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags); CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags);
CORRADE_VERIFY(a.isCompressed()); CORRADE_VERIFY(a.isCompressed());
CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); CORRADE_COMPARE(a.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(a.compressedStorage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(a.compressedStorage().rowLength(), 20);
CORRADE_COMPARE(a.compressedFormat(), CompressedPixelFormat::Bc1RGBAUnorm); CORRADE_COMPARE(a.compressedFormat(), CompressedPixelFormat::Astc5x5x4RGBAF);
CORRADE_COMPARE(a.size(), Vector2i(4, 4)); CORRADE_COMPARE(a.blockSize(), (Vector3i{5, 5, 4}));
CORRADE_COMPARE(a.blockDataSize(), 16);
CORRADE_COMPARE(a.size(), (Vector2i{15, 10}));
CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data); CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 8); CORRADE_COMPARE(a.data().size(), 8*16);
if(instanceData.dataFlags & DataFlag::Mutable) { if(instanceData.dataFlags & DataFlag::Mutable) {
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data); CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 8); CORRADE_COMPARE(a.mutableData().size(), 8*16);
} }
CORRADE_COMPARE(a.importerState(), &state); CORRADE_COMPARE(a.importerState(), &state);
} }
@ -580,28 +653,55 @@ void ImageDataTest::constructCompressedImplementationSpecificNotOwned() {
/* Format with autodetection */ /* Format with autodetection */
{ {
char data[8]; char data[8*8];
int state; int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */
ImageData2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), ImageData2D a{
GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, CompressedPixelStorage{}.setRowLength(16),
GL::CompressedPixelFormat::RGBS3tcDxt1, {12, 8},
instanceData.dataFlags, data, ImageFlag2D::Array, &state}; instanceData.dataFlags, data, ImageFlag2D::Array, &state};
CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags); CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags);
CORRADE_VERIFY(a.isCompressed()); CORRADE_VERIFY(a.isCompressed());
CORRADE_COMPARE(a.flags(), ImageFlag2D::Array); 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.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<const void*>(a.data().data()), data); CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 8); CORRADE_COMPARE(a.data().size(), 8*8);
if(instanceData.dataFlags & DataFlag::Mutable) { if(instanceData.dataFlags & DataFlag::Mutable) {
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data); CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 8); CORRADE_COMPARE(a.mutableData().size(), 8*8);
} }
CORRADE_COMPARE(a.importerState(), &state); 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<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 6*12);
if(instanceData.dataFlags & DataFlag::Mutable) {
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 6*12);
}
CORRADE_COMPARE(a.importerState(), &state);
}
} }
void ImageDataTest::constructGenericNotOwnedFlagOwned() { void ImageDataTest::constructGenericNotOwnedFlagOwned() {
@ -639,9 +739,13 @@ void ImageDataTest::constructCompressedGenericNotOwnedFlagOwned() {
Containers::String out; Containers::String out;
Error redirectError{&out}; Error redirectError{&out};
ImageData2D{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, DataFlag::Owned, data}; ImageData2D{
ImageData2D{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedPixelFormat::Bc1RGBAUnorm, {4, 4},
CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, DataFlag::Owned, data}; DataFlag::Owned, data};
ImageData2D{
CompressedPixelStorage{}.setRowLength(4),
CompressedPixelFormat::Bc1RGBAUnorm, {4, 4},
DataFlag::Owned, data};
CORRADE_COMPARE(out, 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"
"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; Containers::String out;
Error redirectError{&out}; Error redirectError{&out};
ImageData2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), ImageData2D a{
GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, DataFlag::Owned, data}; CompressedPixelStorage{}.setRowLength(4),
GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4},
DataFlag::Owned, data};
CORRADE_COMPARE(out, 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");
} }
@ -725,10 +831,77 @@ void ImageDataTest::constructInvalidCubeMap() {
"Trade::ImageData: expected square faces for a cube map, got {4, 3}\n"); "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<char>{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_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<char>{NoInit, 8}};
ImageData2D{CompressedPixelStorage{}
.setCompressedBlockSize({4, 5, 6})
.setCompressedBlockDataSize(8),
666, {4, 5, 6}, 8, {1, 1}, Containers::Array<char>{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<char>{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<char>{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 */ /* Too small for given format */
{ {
@ -856,11 +1029,12 @@ void ImageDataTest::constructMoveImplementationSpecific() {
} }
void ImageDataTest::constructMoveCompressedGeneric() { 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 {} */ int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */
ImageData2D a{ ImageData2D a{
CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedPixelStorage{}.setRowLength(20),
CompressedPixelFormat::Bc3RGBAUnorm, {4, 4}, Containers::Array<char>{data, 8}, ImageFlag2D::Array, &state}; CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10},
Containers::Array<char>{data, 8*16}, ImageFlag2D::Array, &state};
ImageData2D b{Utility::move(a)}; ImageData2D b{Utility::move(a)};
CORRADE_COMPARE(a.data(), static_cast<const void*>(nullptr)); CORRADE_COMPARE(a.data(), static_cast<const void*>(nullptr));
@ -869,11 +1043,13 @@ void ImageDataTest::constructMoveCompressedGeneric() {
CORRADE_COMPARE(b.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(b.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(b.isCompressed()); CORRADE_VERIFY(b.isCompressed());
CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); CORRADE_COMPARE(b.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(b.compressedStorage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(b.compressedStorage().rowLength(), 20);
CORRADE_COMPARE(b.compressedFormat(), CompressedPixelFormat::Bc3RGBAUnorm); CORRADE_COMPARE(b.compressedFormat(), CompressedPixelFormat::Astc5x5x4RGBAF);
CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); 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<const void*>(data)); CORRADE_COMPARE(b.data(), static_cast<const void*>(data));
CORRADE_COMPARE(b.data().size(), 8); CORRADE_COMPARE(b.data().size(), 8*16);
CORRADE_COMPARE(b.importerState(), &state); CORRADE_COMPARE(b.importerState(), &state);
auto data2 = new char[16]; auto data2 = new char[16];
@ -886,20 +1062,23 @@ void ImageDataTest::constructMoveCompressedGeneric() {
CORRADE_COMPARE(c.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(c.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(c.isCompressed()); CORRADE_VERIFY(c.isCompressed());
CORRADE_COMPARE(c.flags(), ImageFlag2D::Array); CORRADE_COMPARE(c.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(c.compressedStorage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(c.compressedStorage().rowLength(), 20);
CORRADE_COMPARE(c.compressedFormat(), CompressedPixelFormat::Bc3RGBAUnorm); CORRADE_COMPARE(c.compressedFormat(), CompressedPixelFormat::Astc5x5x4RGBAF);
CORRADE_COMPARE(c.size(), (Vector2i{4, 4})); 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<const void*>(data)); CORRADE_COMPARE(c.data(), static_cast<const void*>(data));
CORRADE_COMPARE(c.data().size(), 8); CORRADE_COMPARE(c.data().size(), 8*16);
CORRADE_COMPARE(c.importerState(), &state); CORRADE_COMPARE(c.importerState(), &state);
} }
void ImageDataTest::constructMoveCompressedImplementationSpecific() { 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 {} */ int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */
ImageData2D a{ ImageData2D a{
CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedPixelStorage{}.setRowLength(20),
GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, Containers::Array<char>{data, 8}, ImageFlag2D::Array, &state}; Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10},
Containers::Array<char>{data, 8*16}, ImageFlag2D::Array, &state};
ImageData2D b{Utility::move(a)}; ImageData2D b{Utility::move(a)};
CORRADE_COMPARE(a.data(), static_cast<const void*>(nullptr)); CORRADE_COMPARE(a.data(), static_cast<const void*>(nullptr));
@ -908,15 +1087,17 @@ void ImageDataTest::constructMoveCompressedImplementationSpecific() {
CORRADE_COMPARE(b.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(b.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(b.isCompressed()); CORRADE_VERIFY(b.isCompressed());
CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); CORRADE_COMPARE(b.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(b.compressedStorage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(b.compressedStorage().rowLength(), 20);
CORRADE_COMPARE(b.compressedFormat(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(b.compressedFormat(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF));
CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); 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<const void*>(data)); CORRADE_COMPARE(b.data(), static_cast<const void*>(data));
CORRADE_COMPARE(b.data().size(), 8); CORRADE_COMPARE(b.data().size(), 8*16);
CORRADE_COMPARE(b.importerState(), &state); CORRADE_COMPARE(b.importerState(), &state);
auto data2 = new char[16]; auto data2 = new char[16];
ImageData2D c{CompressedPixelFormat::Bc2RGBAUnorm, {8, 4}, Containers::Array<char>{data2, 16}}; ImageData2D c{CompressedPixelFormat::Bc1RGBAUnorm, {8, 4}, Containers::Array<char>{data2, 16}};
c = Utility::move(b); c = Utility::move(b);
CORRADE_COMPARE(b.data(), static_cast<const void*>(data2)); CORRADE_COMPARE(b.data(), static_cast<const void*>(data2));
@ -925,11 +1106,13 @@ void ImageDataTest::constructMoveCompressedImplementationSpecific() {
CORRADE_COMPARE(c.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(c.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(c.isCompressed()); CORRADE_VERIFY(c.isCompressed());
CORRADE_COMPARE(c.flags(), ImageFlag2D::Array); CORRADE_COMPARE(c.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(c.compressedStorage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(c.compressedStorage().rowLength(), 20);
CORRADE_COMPARE(c.compressedFormat(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(c.compressedFormat(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF));
CORRADE_COMPARE(c.size(), (Vector2i{4, 4})); 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<const void*>(data)); CORRADE_COMPARE(c.data(), static_cast<const void*>(data));
CORRADE_COMPARE(c.data().size(), 8); CORRADE_COMPARE(c.data().size(), 8*16);
CORRADE_COMPARE(c.importerState(), &state); CORRADE_COMPARE(c.importerState(), &state);
} }
@ -958,12 +1141,13 @@ void ImageDataTest::constructMoveAttachState() {
} }
void ImageDataTest::constructMoveCompressedAttachState() { void ImageDataTest::constructMoveCompressedAttachState() {
auto data = new char[8]; auto data = new char[8*8];
/* GCC 11 pointlessly complains that "maybe uninitialized" w/o the {} */ /* GCC 11 pointlessly complains that "maybe uninitialized" w/o the {} */
int stateOld{}, stateNew{}; int stateOld{}, stateNew{};
ImageData2D a{ ImageData2D a{
CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedPixelStorage{}.setRowLength(16),
GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, Containers::Array<char>{data, 8}, ImageFlag2D::Array, &stateOld}; GL::CompressedPixelFormat::RGBS3tcDxt1, {12, 8},
Containers::Array<char>{data, 8*8}, ImageFlag2D::Array, &stateOld};
ImageData2D b{Utility::move(a), &stateNew}; ImageData2D b{Utility::move(a), &stateNew};
CORRADE_COMPARE(a.data(), static_cast<const void*>(nullptr)); CORRADE_COMPARE(a.data(), static_cast<const void*>(nullptr));
@ -972,11 +1156,13 @@ void ImageDataTest::constructMoveCompressedAttachState() {
CORRADE_COMPARE(b.dataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(b.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(b.isCompressed()); CORRADE_VERIFY(b.isCompressed());
CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); 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.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<const void*>(data)); CORRADE_COMPARE(b.data(), static_cast<const void*>(data));
CORRADE_COMPARE(b.data().size(), 8); CORRADE_COMPARE(b.data().size(), 8*8);
CORRADE_COMPARE(b.importerState(), &stateNew); CORRADE_COMPARE(b.importerState(), &stateNew);
} }
@ -1000,7 +1186,7 @@ void ImageDataTest::moveCompressedToUncompressed() {
.setRowLength(2) .setRowLength(2)
.setImageHeight(6) .setImageHeight(6)
.setSkip({0, 1, 0}), .setSkip({0, 1, 0}),
PixelFormat::R8I, 1337, 1, {2, 5}, Containers::Array<char>{data2, 24}, ImageFlag2D(0x80), &state2}; PixelFormat::R8I, 0x12345678, 1, {2, 5}, Containers::Array<char>{data2, 24}, ImageFlag2D(0x80), &state2};
/* The operation should swap the contents completely, not just partially /* The operation should swap the contents completely, not just partially
because one is compressed and the other not */ 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().imageHeight(), 6);
CORRADE_COMPARE(a.storage().skip(), (Vector3i{0, 1, 0})); CORRADE_COMPARE(a.storage().skip(), (Vector3i{0, 1, 0}));
CORRADE_COMPARE(a.format(), PixelFormat::R8I); CORRADE_COMPARE(a.format(), PixelFormat::R8I);
CORRADE_COMPARE(a.formatExtra(), 1337); CORRADE_COMPARE(a.formatExtra(), 0x12345678);
CORRADE_COMPARE(a.pixelSize(), 1); CORRADE_COMPARE(a.pixelSize(), 1);
CORRADE_COMPARE(a.size(), (Vector2i{2, 5})); CORRADE_COMPARE(a.size(), (Vector2i{2, 5}));
CORRADE_COMPARE(a.data(), static_cast<const void*>(data2)); CORRADE_COMPARE(a.data(), static_cast<const void*>(data2));
@ -1045,7 +1231,7 @@ void ImageDataTest::moveUncompressedToCompressed() {
.setRowLength(2) .setRowLength(2)
.setImageHeight(6) .setImageHeight(6)
.setSkip({0, 1, 0}), .setSkip({0, 1, 0}),
PixelFormat::R8I, 1337, 1, {2, 5}, Containers::Array<char>{data, 24}, ImageFlag2D::Array, &state}; PixelFormat::R8I, 0x12345678, 1, {2, 5}, Containers::Array<char>{data, 24}, ImageFlag2D::Array, &state};
auto data2 = new char[8*16]; auto data2 = new char[8*16];
int state2{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ 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().imageHeight(), 6);
CORRADE_COMPARE(b.storage().skip(), (Vector3i{0, 1, 0})); CORRADE_COMPARE(b.storage().skip(), (Vector3i{0, 1, 0}));
CORRADE_COMPARE(b.format(), PixelFormat::R8I); CORRADE_COMPARE(b.format(), PixelFormat::R8I);
CORRADE_COMPARE(b.formatExtra(), 1337); CORRADE_COMPARE(b.formatExtra(), 0x12345678);
CORRADE_COMPARE(b.pixelSize(), 1); CORRADE_COMPARE(b.pixelSize(), 1);
CORRADE_COMPARE(b.size(), (Vector2i{2, 5})); CORRADE_COMPARE(b.size(), (Vector2i{2, 5}));
CORRADE_COMPARE(b.data(), static_cast<const void*>(data)); CORRADE_COMPARE(b.data(), static_cast<const void*>(data));
@ -1103,6 +1289,9 @@ void ImageDataTest::propertiesInvalid() {
Error redirectError{&out}; Error redirectError{&out};
uncompressed.compressedStorage(); uncompressed.compressedStorage();
uncompressed.compressedFormat(); uncompressed.compressedFormat();
uncompressed.blockSize();
uncompressed.blockDataSize();
uncompressed.compressedDataProperties();
compressed.storage(); compressed.storage();
compressed.format(); compressed.format();
compressed.formatExtra(); compressed.formatExtra();
@ -1117,6 +1306,9 @@ void ImageDataTest::propertiesInvalid() {
CORRADE_COMPARE_AS(out, CORRADE_COMPARE_AS(out,
"Trade::ImageData::compressedStorage(): the image is not compressed\n" "Trade::ImageData::compressedStorage(): the image is not compressed\n"
"Trade::ImageData::compressedFormat(): 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::storage(): the image is compressed\n"
"Trade::ImageData::format(): the image is compressed\n" "Trade::ImageData::format(): the image is compressed\n"
"Trade::ImageData::formatExtra(): the image is compressed\n" "Trade::ImageData::formatExtra(): the image is compressed\n"
@ -1164,35 +1356,41 @@ template<class T> void ImageDataTest::toViewImplementationSpecific() {
template<class T> void ImageDataTest::toViewCompressedGeneric() { template<class T> void ImageDataTest::toViewCompressedGeneric() {
setTestCaseTemplateName(MutabilityTraits<T>::name()); setTestCaseTemplateName(MutabilityTraits<T>::name());
auto data = new char[8]; auto data = new char[8*16];
typename MutabilityTraits<T>::ImageType a{ typename MutabilityTraits<T>::ImageType a{
CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedPixelStorage{}.setRowLength(20),
CompressedPixelFormat::Bc1RGBUnorm, {4, 4}, Containers::Array<char>{data, 8}, ImageFlag2D::Array}; CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10},
Containers::Array<char>{data, 8*16}, ImageFlag2D::Array};
CompressedImageView<2, T> b = a; CompressedImageView<2, T> b = a;
CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); CORRADE_COMPARE(b.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(b.storage().rowLength(), 20);
CORRADE_COMPARE(b.format(), CompressedPixelFormat::Bc1RGBUnorm); CORRADE_COMPARE(b.format(), CompressedPixelFormat::Astc5x5x4RGBAF);
CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); 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<const void*>(data)); CORRADE_COMPARE(b.data(), static_cast<const void*>(data));
CORRADE_COMPARE(b.data().size(), 8); CORRADE_COMPARE(b.data().size(), 8*16);
} }
template<class T> void ImageDataTest::toViewCompressedImplementationSpecific() { template<class T> void ImageDataTest::toViewCompressedImplementationSpecific() {
setTestCaseTemplateName(MutabilityTraits<T>::name()); setTestCaseTemplateName(MutabilityTraits<T>::name());
auto data = new char[8]; auto data = new char[8*16];
typename MutabilityTraits<T>::ImageType a{ typename MutabilityTraits<T>::ImageType a{
CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}), CompressedPixelStorage{}.setRowLength(20),
GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, Containers::Array<char>{data, 8}, ImageFlag2D::Array}; Vk::CompressedPixelFormat::Astc5x5x4RGBAF, {15, 10},
Containers::Array<char>{data, 8*16}, ImageFlag2D::Array};
CompressedImageView<2, T> b = a; CompressedImageView<2, T> b = a;
CORRADE_COMPARE(b.flags(), ImageFlag2D::Array); CORRADE_COMPARE(b.flags(), ImageFlag2D::Array);
CORRADE_COMPARE(b.storage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(b.storage().rowLength(), 20);
CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(b.format(), compressedPixelFormatWrap(Vk::CompressedPixelFormat::Astc5x5x4RGBAF));
CORRADE_COMPARE(b.size(), (Vector2i{4, 4})); 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<const void*>(data)); CORRADE_COMPARE(b.data(), static_cast<const void*>(data));
CORRADE_COMPARE(b.data().size(), 8); CORRADE_COMPARE(b.data().size(), 8*16);
} }
void ImageDataTest::data() { void ImageDataTest::data() {
@ -1214,18 +1412,19 @@ void ImageDataTest::mutableAccessNotAllowed() {
CORRADE_SKIP_IF_NO_ASSERT(); CORRADE_SKIP_IF_NO_ASSERT();
const char data[4*4]{}; 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; Containers::String out;
Error redirectError{&out}; Error redirectError{&out};
a.mutableData(); uncompressed.mutableData();
a.mutablePixels(); uncompressed.mutablePixels();
/* Can't do just MutableImageView2D(a) because the compiler then treats it /* 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 as a function. Can't do MutableImageView2D{a} because that doesn't work
on old Clang. So it's this mess, then. Sigh. */ on old Clang. So it's this mess, then. Sigh. */
auto b = MutableImageView2D(a); auto b = MutableImageView2D(uncompressed);
static_cast<void>(b); static_cast<void>(b);
auto c = MutableCompressedImageView2D(a); auto c = MutableCompressedImageView2D(compressed);
static_cast<void>(c); static_cast<void>(c);
/* a.mutablePixels<T>() calls non-templated mutablePixels(), so assume /* a.mutablePixels<T>() calls non-templated mutablePixels(), so assume
there it will blow up correctly as well (can't test because it asserts 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<std::size_t>, Math::Vector3<std::size_t>>{{3, 16, 32}, {8, 4, 6}})); (std::pair<Math::Vector3<std::size_t>, Math::Vector3<std::size_t>>{{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<char>{NoInit, 336}};
CORRADE_COMPARE(image.compressedDataProperties(),
(std::pair<Math::Vector3<std::size_t>, Math::Vector3<std::size_t>>{{16, 24, 192}, {3, 2, 3}}));
}
void ImageDataTest::release() { void ImageDataTest::release() {
char data[] = {'b', 'e', 'e', 'r'}; char data[] = {'b', 'e', 'e', 'r'};
Trade::ImageData2D a{PixelFormat::RGBA8Unorm, {1, 1}, Containers::Array<char>{data, 4}}; Trade::ImageData2D a{PixelFormat::RGBA8Unorm, {1, 1}, Containers::Array<char>{data, 4}};

Loading…
Cancel
Save