diff --git a/src/Magnum/AbstractImage.h b/src/Magnum/AbstractImage.h index a9b99be8b..7f76718d5 100644 --- a/src/Magnum/AbstractImage.h +++ b/src/Magnum/AbstractImage.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::AbstractImage + * @brief Class @ref Magnum::AbstractImage, @ref Magnum::AbstractCompressedImage */ #include @@ -43,10 +43,11 @@ namespace Implementation { } /** -@brief Non-templated base for one-, two- or three-dimensional images +@brief Non-templated base for one-, two- or three-dimensional uncompressed images See @ref Image, @ref ImageView, @ref BufferImage and @ref Trade::ImageData documentation for more information. +@see @ref AbstractCompressedImage @todo Where to put glClampColor() and glPixelStore() encapsulation? It is needed in AbstractFramebuffer::read(), Texture::setImage() etc (i.e. all functions operating with images). It also possibly needs to be "stackable" @@ -66,6 +67,18 @@ class AbstractImage { Int _dummy; }; +/** +@brief Non-templated base for one-, two- or three-dimensional compressed images + +See @ref CompressedImage, @ref CompressedImageView, @ref CompressedBufferImage +and @ref Trade::ImageData documentation for more information. +@see @ref AbstractImage +*/ +class AbstractCompressedImage: public AbstractImage { + protected: + ~AbstractCompressedImage() = default; +}; + } #endif diff --git a/src/Magnum/BufferImage.cpp b/src/Magnum/BufferImage.cpp index 2c59369c9..716036202 100644 --- a/src/Magnum/BufferImage.cpp +++ b/src/Magnum/BufferImage.cpp @@ -41,10 +41,27 @@ template void BufferImage::setData(ColorForm _buffer.setData({data, dataSize(size)}, usage); } +template CompressedBufferImage::CompressedBufferImage(CompressedColorFormat format, const VectorTypeFor& size, Containers::ArrayView data, BufferUsage usage): _format{format}, _size{size}, _buffer{Buffer::TargetHint::PixelPack}, _dataSize{data.size()} { + _buffer.setData(data, usage); +} + +template CompressedBufferImage::CompressedBufferImage(): _format{}, _buffer{Buffer::TargetHint::PixelPack}, _dataSize{} {} + +template void CompressedBufferImage::setData(CompressedColorFormat format, const VectorTypeFor& size, Containers::ArrayView data, BufferUsage usage) { + _format = format; + _size = size; + _buffer.setData(data, usage); + _dataSize = data.size(); +} + #ifndef DOXYGEN_GENERATING_OUTPUT template class MAGNUM_EXPORT BufferImage<1>; template class MAGNUM_EXPORT BufferImage<2>; template class MAGNUM_EXPORT BufferImage<3>; + +template class MAGNUM_EXPORT CompressedBufferImage<1>; +template class MAGNUM_EXPORT CompressedBufferImage<2>; +template class MAGNUM_EXPORT CompressedBufferImage<3>; #endif #endif diff --git a/src/Magnum/BufferImage.h b/src/Magnum/BufferImage.h index 31728a2f7..d6b9dc908 100644 --- a/src/Magnum/BufferImage.h +++ b/src/Magnum/BufferImage.h @@ -27,7 +27,7 @@ #ifndef MAGNUM_TARGET_GLES2 /** @file - * @brief Class @ref Magnum::BufferImage, typedef @ref Magnum::BufferImage1D, @ref Magnum::BufferImage2D, @ref Magnum::BufferImage3D + * @brief Class @ref Magnum::BufferImage, @ref Magnum::CompressedBufferImage, typedef @ref Magnum::BufferImage1D, @ref Magnum::BufferImage2D, @ref Magnum::BufferImage3D, @ref Magnum::CompressedBufferImage1D, @ref Magnum::CompressedBufferImage2D, @ref Magnum::CompressedBufferImage3D */ #endif @@ -44,7 +44,8 @@ namespace Magnum { Stores image data in GPU memory. Interchangeable with @ref Image, @ref ImageView or @ref Trade::ImageData. -@see @ref BufferImage1D, @ref BufferImage2D, @ref BufferImage3D, @ref Buffer +@see @ref BufferImage1D, @ref BufferImage2D, @ref BufferImage3D, + @ref CompressedBufferImage, @ref Buffer @requires_gles30 Pixel buffer objects are not available in OpenGL ES 2.0. @requires_webgl20 Pixel buffer objects are not available in WebGL 1.0. */ @@ -142,10 +143,109 @@ typedef BufferImage<2> BufferImage2D; /** @brief Three-dimensional buffer image */ typedef BufferImage<3> BufferImage3D; +/** +@brief Compressed buffer image + +Stores image data in GPU memory. + +See @ref BufferImage for more information. Interchangeable with @ref CompressedImage, +@ref CompressedImageView or @ref Trade::ImageData. +@see @ref CompressedBufferImage1D, @ref CompressedBufferImage2D, + @ref CompressedBufferImage3D +@requires_gles30 Pixel buffer objects are not available in OpenGL ES 2.0. +@requires_webgl20 Pixel buffer objects are not available in WebGL 1.0. +*/ +template class CompressedBufferImage: public AbstractCompressedImage { + public: + enum: UnsignedInt { + Dimensions = dimensions /**< Image dimension count */ + }; + + /** + * @brief Constructor + * @param format Format of compressed data + * @param size Image size + * @param data Image data + * @param usage Image buffer usage + * + * The data are *not* deleted after filling the buffer. + * @todo Make it more flexible (usable with + * @extension{ARB,buffer_storage}, avoiding relocations...) + */ + explicit CompressedBufferImage(CompressedColorFormat format, const VectorTypeFor& size, Containers::ArrayView data, BufferUsage usage); + + /** + * @brief Constructor + * + * Format is undefined, size is zero and buffer is empty, call + * @ref setData() to fill the image with data. + */ + /*implicit*/ CompressedBufferImage(); + + /** @brief Copying is not allowed */ + CompressedBufferImage(const CompressedBufferImage&) = delete; + + /** @brief Move constructor */ + CompressedBufferImage(CompressedBufferImage&& other) noexcept; + + /** @brief Copying is not allowed */ + CompressedBufferImage& operator=(const CompressedBufferImage&) = delete; + + /** @brief Move assignment */ + CompressedBufferImage& operator=(CompressedBufferImage&& other) noexcept; + + /** @brief Format of compressed data */ + CompressedColorFormat format() const { return _format; } + + /** @brief Image size */ + VectorTypeFor size() const { return _size; } + + /** @brief Image buffer */ + Buffer& buffer() { return _buffer; } + + /** @brief Raw data size */ + std::size_t dataSize() const { return _dataSize; } + + /** + * @brief Set image data + * @param format Format of compressed data + * @param size Image size + * @param data Image data + * @param usage Image buffer usage + * + * Updates the image buffer with given data. The data are *not* deleted + * after filling the buffer. + * @see @ref Buffer::setData() + * @todo Make it more flexible (usable with + * @extension{ARB,buffer_storage}, avoiding relocations...) + */ + void setData(CompressedColorFormat format, const VectorTypeFor& size, Containers::ArrayView data, BufferUsage usage); + + private: + CompressedColorFormat _format; + Math::Vector _size; + Buffer _buffer; + std::size_t _dataSize; +}; + +/** @brief One-dimensional compressed buffer image */ +typedef CompressedBufferImage<1> CompressedBufferImage1D; + +/** @brief Two-dimensional compressed buffer image */ +typedef CompressedBufferImage<2> CompressedBufferImage2D; + +/** @brief Three-dimensional compressed buffer image */ +typedef CompressedBufferImage<3> CompressedBufferImage3D; + template inline BufferImage::BufferImage(BufferImage&& other) noexcept: AbstractImage{std::move(other)}, _format{std::move(other._format)}, _type{std::move(other._type)}, _size{std::move(other._size)}, _buffer{std::move(other._buffer)} { other._size = {}; } +template inline CompressedBufferImage::CompressedBufferImage(CompressedBufferImage&& other) noexcept: AbstractCompressedImage{std::move(other)}, _format{std::move(other._format)}, _size{std::move(other._size)}, _buffer{std::move(other._buffer)}, _dataSize{std::move(other._dataSize)} { + other._size = {}; + other._dataSize = {}; +} + template inline BufferImage& BufferImage::operator=(BufferImage&& other) noexcept { AbstractImage::operator=(std::move(other)); using std::swap; @@ -155,6 +255,16 @@ template inline BufferImage& BufferImage inline CompressedBufferImage& CompressedBufferImage::operator=(CompressedBufferImage&& other) noexcept { + AbstractCompressedImage::operator=(std::move(other)); + using std::swap; + swap(_format, other._format); + swap(_size, other._size); + swap(_buffer, other._buffer); + swap(_dataSize, other._dataSize); + return *this; +} #else #error this header is not available in OpenGL ES 2.0 build #endif diff --git a/src/Magnum/CMakeLists.txt b/src/Magnum/CMakeLists.txt index 5e9825a97..2a7b2bb68 100644 --- a/src/Magnum/CMakeLists.txt +++ b/src/Magnum/CMakeLists.txt @@ -66,6 +66,7 @@ set(Magnum_SRCS Trade/AbstractImageConverter.cpp Trade/AbstractImporter.cpp Trade/AbstractMaterialData.cpp + Trade/ImageData.cpp Trade/MeshData2D.cpp Trade/MeshData3D.cpp Trade/MeshObjectData2D.cpp diff --git a/src/Magnum/Image.cpp b/src/Magnum/Image.cpp index adecf4b47..9ded95209 100644 --- a/src/Magnum/Image.cpp +++ b/src/Magnum/Image.cpp @@ -35,10 +35,20 @@ template void Image::setData(ColorFormat for _data = reinterpret_cast(data); } +template void CompressedImage::setData(CompressedColorFormat format, const VectorTypeFor& size, Containers::Array&& data) { + _format = format; + _size = size; + _data = std::move(data); +} + #ifndef DOXYGEN_GENERATING_OUTPUT template class MAGNUM_EXPORT Image<1>; template class MAGNUM_EXPORT Image<2>; template class MAGNUM_EXPORT Image<3>; + +template class MAGNUM_EXPORT CompressedImage<1>; +template class MAGNUM_EXPORT CompressedImage<2>; +template class MAGNUM_EXPORT CompressedImage<3>; #endif } diff --git a/src/Magnum/Image.h b/src/Magnum/Image.h index 6f2fda56e..4402b958a 100644 --- a/src/Magnum/Image.h +++ b/src/Magnum/Image.h @@ -26,9 +26,11 @@ */ /** @file - * @brief Class @ref Magnum::Image, typedef @ref Magnum::Image1D, @ref Magnum::Image2D, @ref Magnum::Image3D + * @brief Class @ref Magnum::Image, @ref Magnum::CompressedImage typedef @ref Magnum::Image1D, @ref Magnum::Image2D, @ref Magnum::Image3D, @ref Magnum::CompressedImage1D, @ref Magnum::CompressedImage2D, @ref Magnum::CompressedImage3D */ +#include + #include "Magnum/ImageView.h" namespace Magnum { @@ -38,7 +40,7 @@ namespace Magnum { Stores image data on client memory. Interchangeable with @ref ImageView, @ref BufferImage or @ref Trade::ImageData. -@see @ref Image1D, @ref Image2D, @ref Image3D +@see @ref Image1D, @ref Image2D, @ref Image3D, @ref CompressedImage */ template class Image: public AbstractImage { public: @@ -171,11 +173,134 @@ typedef Image<2> Image2D; /** @brief Three-dimensional image */ typedef Image<3> Image3D; +/** +@brief Compressed image + +Stores image data in client memory. + +See @ref Image for more information. Interchangeable with +@ref CompressedImageView, @ref CompressedBufferImage or @ref Trade::ImageData. +@see @ref CompressedImage1D, @ref CompressedImage2D, @ref CompressedImage3D +*/ +template class CompressedImage: public AbstractCompressedImage { + public: + enum: UnsignedInt { + Dimensions = dimensions /**< Image dimension count */ + }; + + /** + * @brief Constructor + * @param format Format of compressed data + * @param size Image size + * @param data Image data + */ + explicit CompressedImage(CompressedColorFormat format, const VectorTypeFor& size, Containers::Array&& data): _format{format}, _size{size}, _data{std::move(data)} {} + + /** + * @brief Constructor + * + * Format is undefined, size is zero and data are empty, call + * @ref setData() to fill the image with data. + */ + /*implicit*/ CompressedImage(): _format{} {} + + /** @brief Copying is not allowed */ + CompressedImage(const CompressedImage&) = delete; + + /** @brief Move constructor */ + CompressedImage(CompressedImage&& other) noexcept; + + /** @brief Copying is not allowed */ + CompressedImage& operator=(const CompressedImage&) = delete; + + /** @brief Move assignment */ + CompressedImage& operator=(CompressedImage&& other) noexcept; + + /** @brief Conversion to view */ + /*implicit*/ operator CompressedImageView() + #ifndef CORRADE_GCC47_COMPATIBILITY + const &; + #else + const; + #endif + + #ifndef CORRADE_GCC47_COMPATIBILITY + /** @overload */ + /*implicit*/ operator CompressedImageView() const && = delete; + #endif + + /** @brief Format of compressed data */ + CompressedColorFormat format() const { return _format; } + + /** @brief Image size */ + VectorTypeFor size() const { return _size; } + + /** @brief Raw data */ + Containers::ArrayView data() { return _data; } + + /** @overload */ + Containers::ArrayView data() const { return _data; } + + /** + * @brief Pointer to raw data + * + * @see @ref release() + */ + template T* data() { + return reinterpret_cast(_data.data()); + } + + /** @overload */ + template const T* data() const { + return reinterpret_cast(_data.data()); + } + + /** + * @brief Set image data + * @param format Format of compressed data + * @param size Image size + * @param data Image data + * + * Deletes previous data and replaces them with new. Note that the + * data are not copied, but they are deleted on destruction. + * @see @ref release() + */ + void setData(CompressedColorFormat format, const VectorTypeFor& size, Containers::Array&& data); + + /** + * @brief Release data storage + * + * Releases the ownership of the data pointer and resets internal state + * to default. + * @see @ref setData() + */ + Containers::Array release(); + + private: + CompressedColorFormat _format; + Math::Vector _size; + Containers::Array _data; +}; + +/** @brief One-dimensional compressed image */ +typedef CompressedImage<1> CompressedImage1D; + +/** @brief Two-dimensional compressed image */ +typedef CompressedImage<2> CompressedImage2D; + +/** @brief Three-dimensional compressed image */ +typedef CompressedImage<3> CompressedImage3D; + template inline Image::Image(Image&& other) noexcept: AbstractImage{std::move(other)}, _format{std::move(other._format)}, _type{std::move(other._type)}, _size{std::move(other._size)}, _data{std::move(other._data)} { other._size = {}; other._data = nullptr; } +template inline CompressedImage::CompressedImage(CompressedImage&& other) noexcept: AbstractCompressedImage{std::move(other)}, _format{std::move(other._format)}, _size{std::move(other._size)}, _data{std::move(other._data)} { + other._size = {}; + other._data = nullptr; +} + template inline Image& Image::operator=(Image&& other) noexcept { AbstractImage::operator=(std::move(other)); using std::swap; @@ -186,6 +311,15 @@ template inline Image& Image::op return *this; } +template inline CompressedImage& CompressedImage::operator=(CompressedImage&& other) noexcept { + AbstractCompressedImage::operator=(std::move(other)); + using std::swap; + swap(_format, other._format); + swap(_size, other._size); + swap(_data, other._data); + return *this; +} + template inline Image::operator ImageView() #ifndef CORRADE_GCC47_COMPATIBILITY const & @@ -196,6 +330,16 @@ const return ImageView{_format, _type, _size, _data}; } +template inline CompressedImage::operator CompressedImageView() +#ifndef CORRADE_GCC47_COMPATIBILITY +const & +#else +const +#endif +{ + return CompressedImageView{_format, _size, _data}; +} + template inline char* Image::release() { /** @todo I need `std::exchange` NOW. */ char* const data = _data; @@ -204,6 +348,14 @@ template inline char* Image::release() { return data; } +template inline Containers::Array CompressedImage::release() { + /** @todo I need `std::exchange` NOW. */ + Containers::Array data{std::move(_data)}; + _size = {}; + _data = nullptr; + return data; +} + } #endif diff --git a/src/Magnum/ImageView.h b/src/Magnum/ImageView.h index 6c51d22b5..e7fc96d72 100644 --- a/src/Magnum/ImageView.h +++ b/src/Magnum/ImageView.h @@ -26,9 +26,11 @@ */ /** @file - * @brief Class @ref Magnum::ImageView, typedef @ref Magnum::ImageView1D, @ref Magnum::ImageView2D, @ref Magnum::ImageView3D + * @brief Class @ref Magnum::ImageView, @ref Magnum::CompressedImageView, typedef @ref Magnum::ImageView1D, @ref Magnum::ImageView2D, @ref Magnum::ImageView3D, @ref Magnum::CompressedImageView1D, @ref Magnum::CompressedImageView2D, @ref Magnum::CompressedImageView3D */ +#include + #include "Magnum/Math/Vector3.h" #include "Magnum/AbstractImage.h" #include "Magnum/DimensionTraits.h" @@ -48,7 +50,8 @@ same properties for each frame, such as video stream. Thus it is not possible to change image properties, only data pointer. Interchangeable with @ref Image, @ref BufferImage or @ref Trade::ImageData. -@see @ref ImageView1D, @ref ImageView2D, @ref ImageView3D +@see @ref ImageView1D, @ref ImageView2D, @ref ImageView3D, + @ref CompressedImageView */ template class ImageView: public AbstractImage { public: @@ -129,6 +132,81 @@ typedef ImageView<2> ImageView2D; /** @brief Three-dimensional image view */ typedef ImageView<3> ImageView3D; +/** +@brief Compressed image view + +Adds information about dimensions and compression type to some data in memory. + +See @ref ImageView for more information. Interchangeable with +@ref CompressedImage, @ref CompressedBufferImage or @ref Trade::ImageData. +@see @ref CompressedImageView1D, @ref CompressedImageView2D, + @ref CompressedImageView3D +*/ +template class CompressedImageView: public AbstractCompressedImage { + public: + enum: UnsignedInt { + Dimensions = dimensions /**< Image dimension count */ + }; + + /** + * @brief Constructor + * @param format Format of compressed data + * @param size Image size + * @param data Image data + */ + constexpr explicit CompressedImageView(CompressedColorFormat format, const VectorTypeFor& size, Containers::ArrayView data): _format{format}, _size{size}, _data{reinterpret_cast(data.data()), data.size()} {} + + /** + * @brief Constructor + * @param format Format of compressed data + * @param size Image size + * + * Data pointer is set to `nullptr`, call @ref setData() to fill the + * image with data. + */ + constexpr explicit CompressedImageView(CompressedColorFormat format, const VectorTypeFor& size): _format{format}, _size{size} {} + + /** @brief Format of compressed data */ + CompressedColorFormat format() const { return _format; } + + /** @brief Image size */ + constexpr VectorTypeFor size() const { return _size; } + + /** @brief Raw data */ + constexpr Containers::ArrayView data() const { return _data; } + + /** @overload */ + template const T* data() const { + return reinterpret_cast(_data.data()); + } + + /** + * @brief Set image data + * @param data Image data + * + * Dimensions, color compnents and data type remains the same as + * passed in constructor. The data are not copied nor deleted on + * destruction. + */ + void setData(Containers::ArrayView data) { + _data = {reinterpret_cast(data.data()), data.size()}; + } + + private: + CompressedColorFormat _format; + Math::Vector _size; + Containers::ArrayView _data; +}; + +/** @brief One-dimensional compressed image view */ +typedef CompressedImageView<1> CompressedImageView1D; + +/** @brief Two-dimensional compressed image view */ +typedef CompressedImageView<2> CompressedImageView2D; + +/** @brief Three-dimensional compressed image view */ +typedef CompressedImageView<3> CompressedImageView3D; + } #endif diff --git a/src/Magnum/Magnum.h b/src/Magnum/Magnum.h index 10b2bf1a3..7864cbb9a 100644 --- a/src/Magnum/Magnum.h +++ b/src/Magnum/Magnum.h @@ -443,6 +443,11 @@ template class BufferImage; typedef BufferImage<1> BufferImage1D; typedef BufferImage<2> BufferImage2D; typedef BufferImage<3> BufferImage3D; + +template class CompressedBufferImage; +typedef CompressedBufferImage<1> CompressedBufferImage1D; +typedef CompressedBufferImage<2> CompressedBufferImage2D; +typedef CompressedBufferImage<3> CompressedBufferImage3D; #endif #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) @@ -480,18 +485,28 @@ typedef Image<1> Image1D; typedef Image<2> Image2D; typedef Image<3> Image3D; +template class CompressedImage; +typedef CompressedImage<1> CompressedImage1D; +typedef CompressedImage<2> CompressedImage2D; +typedef CompressedImage<3> CompressedImage3D; + template class ImageView; typedef ImageView<1> ImageView1D; typedef ImageView<2> ImageView2D; typedef ImageView<3> ImageView3D; #ifdef MAGNUM_BUILD_DEPRECATED -template using CORRADE_DEPRECATED("use ImageView instead") ImageReference = ImageView; +template using ImageReference CORRADE_DEPRECATED("use ImageView instead") = ImageView; typedef CORRADE_DEPRECATED("use ImageView1D instead") ImageView1D ImageReference1D; typedef CORRADE_DEPRECATED("use ImageView2D instead") ImageView2D ImageReference2D; typedef CORRADE_DEPRECATED("use ImageView3D instead") ImageView3D ImageReference3D; #endif +template class CompressedImageView; +typedef CompressedImageView<1> CompressedImageView1D; +typedef CompressedImageView<2> CompressedImageView2D; +typedef CompressedImageView<3> CompressedImageView3D; + enum class MeshPrimitive: GLenum; class Mesh; diff --git a/src/Magnum/Test/BufferImageGLTest.cpp b/src/Magnum/Test/BufferImageGLTest.cpp index 9312f7030..7eedf22b2 100644 --- a/src/Magnum/Test/BufferImageGLTest.cpp +++ b/src/Magnum/Test/BufferImageGLTest.cpp @@ -35,18 +35,26 @@ struct BufferImageGLTest: AbstractOpenGLTester { explicit BufferImageGLTest(); void construct(); + void constructCompressed(); void constructCopy(); + void constructCopyCompressed(); void constructMove(); + void constructMoveCompressed(); void setData(); + void setDataCompressed(); }; BufferImageGLTest::BufferImageGLTest() { addTests({&BufferImageGLTest::construct, + &BufferImageGLTest::constructCompressed, &BufferImageGLTest::constructCopy, + &BufferImageGLTest::constructCopyCompressed, &BufferImageGLTest::constructMove, + &BufferImageGLTest::constructMoveCompressed, - &BufferImageGLTest::setData}); + &BufferImageGLTest::setData, + &BufferImageGLTest::setDataCompressed}); } void BufferImageGLTest::construct() { @@ -70,11 +78,37 @@ void BufferImageGLTest::construct() { #endif } +void BufferImageGLTest::constructCompressed() { + const char data[] = { 'a', 0, 0, 0, 'b', 0, 0, 0 }; + CompressedBufferImage2D a{CompressedColorFormat::RGBAS3tcDxt1, {4, 4}, data, BufferUsage::StaticDraw}; + + #ifndef MAGNUM_TARGET_GLES + const auto imageData = a.buffer().data(); + #endif + + MAGNUM_VERIFY_NO_ERROR(); + + CORRADE_COMPARE(a.format(), CompressedColorFormat::RGBAS3tcDxt1); + CORRADE_COMPARE(a.size(), Vector2i(4, 4)); + CORRADE_COMPARE(a.dataSize(), 8); + + /** @todo How to verify the contents in ES? */ + #ifndef MAGNUM_TARGET_GLES + CORRADE_COMPARE_AS(imageData, Containers::ArrayView{data}, + TestSuite::Compare::Container); + #endif +} + void BufferImageGLTest::constructCopy() { CORRADE_VERIFY(!(std::is_constructible{})); CORRADE_VERIFY(!(std::is_assignable{})); } +void BufferImageGLTest::constructCopyCompressed() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + void BufferImageGLTest::constructMove() { const char data[4] = { 'a', 'b', 'c', 'd' }; BufferImage2D a(ColorFormat::Red, ColorType::UnsignedByte, {4, 1}, data, BufferUsage::StaticDraw); @@ -110,6 +144,43 @@ void BufferImageGLTest::constructMove() { CORRADE_COMPARE(c.buffer().id(), id); } +void BufferImageGLTest::constructMoveCompressed() { + const char data[] = { 'a', 0, 0, 0, 'b', 0, 0, 0 }; + CompressedBufferImage2D a{CompressedColorFormat::RGBAS3tcDxt1, {4, 4}, data, BufferUsage::StaticDraw}; + const Int id = a.buffer().id(); + + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_VERIFY(id > 0); + + CompressedBufferImage2D b{std::move(a)}; + + CORRADE_COMPARE(a.buffer().id(), 0); + CORRADE_COMPARE(a.size(), Vector2i()); + CORRADE_COMPARE(a.dataSize(), 0); + + CORRADE_COMPARE(b.format(), CompressedColorFormat::RGBAS3tcDxt1); + CORRADE_COMPARE(b.size(), Vector2i(4, 4)); + CORRADE_COMPARE(b.dataSize(), 8); + CORRADE_COMPARE(b.buffer().id(), id); + + const unsigned char data2[] = { 'a', 0, 0, 0, 'b', 0, 0, 0, 'c', 0, 0, 0, 'd', 0, 0, 0 }; + CompressedBufferImage2D c{CompressedColorFormat::RGBAS3tcDxt1, {8, 4}, data2, BufferUsage::StaticDraw}; + const Int cId = c.buffer().id(); + c = std::move(b); + + MAGNUM_VERIFY_NO_ERROR(); + + CORRADE_VERIFY(cId > 0); + CORRADE_COMPARE(b.buffer().id(), cId); + CORRADE_COMPARE(b.size(), Vector2i(8, 4)); + CORRADE_COMPARE(b.dataSize(), 16); + + CORRADE_COMPARE(c.format(), CompressedColorFormat::RGBAS3tcDxt1); + CORRADE_COMPARE(c.size(), Vector2i(4, 4)); + CORRADE_COMPARE(c.dataSize(), 8); + CORRADE_COMPARE(c.buffer().id(), id); +} + void BufferImageGLTest::setData() { const char data[4] = { 'a', 'b', 'c', 'd' }; BufferImage2D a(ColorFormat::Red, ColorType::UnsignedByte, {4, 1}, data, BufferUsage::StaticDraw); @@ -127,6 +198,30 @@ void BufferImageGLTest::setData() { CORRADE_COMPARE(a.type(), ColorType::UnsignedShort); CORRADE_COMPARE(a.size(), Vector2i(1, 2)); + /** @todo How to verify the contents in ES? */ + #ifndef MAGNUM_TARGET_GLES + CORRADE_COMPARE_AS(imageData, Containers::ArrayView{data2}, + TestSuite::Compare::Container); + #endif +} + +void BufferImageGLTest::setDataCompressed() { + const char data[] = { 'a', 0, 0, 0, 'b', 0, 0, 0 }; + CompressedBufferImage2D a{CompressedColorFormat::RGBAS3tcDxt1, {4, 4}, data, BufferUsage::StaticDraw}; + + const char data2[] = { 'a', 0, 0, 0, 'b', 0, 0, 0, 'c', 0, 0, 0, 'd', 0, 0, 0 }; + a.setData(CompressedColorFormat::RGBAS3tcDxt3, {8, 4}, data2, BufferUsage::StaticDraw); + + #ifndef MAGNUM_TARGET_GLES + const auto imageData = a.buffer().data(); + #endif + + MAGNUM_VERIFY_NO_ERROR(); + + CORRADE_COMPARE(a.format(), CompressedColorFormat::RGBAS3tcDxt3); + CORRADE_COMPARE(a.size(), Vector2i(8, 4)); + CORRADE_COMPARE(a.dataSize(), 16); + /** @todo How to verify the contents in ES? */ #ifndef MAGNUM_TARGET_GLES CORRADE_COMPARE_AS(imageData, Containers::ArrayView{data2}, diff --git a/src/Magnum/Test/ImageTest.cpp b/src/Magnum/Test/ImageTest.cpp index 56eab6fdb..d818c9ae4 100644 --- a/src/Magnum/Test/ImageTest.cpp +++ b/src/Magnum/Test/ImageTest.cpp @@ -34,22 +34,34 @@ struct ImageTest: TestSuite::Tester { explicit ImageTest(); void construct(); + void constructCompressed(); void constructCopy(); + void constructCopyCompressed(); void constructMove(); + void constructMoveCompressed(); void setData(); + void setDataCompressed(); void toReference(); + void toReferenceCommpressed(); void release(); + void releaseCompressed(); }; ImageTest::ImageTest() { addTests({&ImageTest::construct, + &ImageTest::constructCompressed, &ImageTest::constructCopy, + &ImageTest::constructCopyCompressed, &ImageTest::constructMove, + &ImageTest::constructMoveCompressed, &ImageTest::setData, + &ImageTest::setDataCompressed, &ImageTest::toReference, - &ImageTest::release}); + &ImageTest::toReferenceCommpressed, + &ImageTest::release, + &ImageTest::releaseCompressed}); } void ImageTest::construct() { @@ -62,11 +74,26 @@ void ImageTest::construct() { CORRADE_COMPARE(a.data(), data); } +void ImageTest::constructCompressed() { + auto data = new char[8]; + CompressedImage2D a{CompressedColorFormat::RGBAS3tcDxt1, {4, 4}, Containers::Array{data, 8}}; + + CORRADE_COMPARE(a.format(), CompressedColorFormat::RGBAS3tcDxt1); + CORRADE_COMPARE(a.size(), Vector2i(4, 4)); + CORRADE_COMPARE(a.data(), data); + CORRADE_COMPARE(a.data().size(), 8); +} + void ImageTest::constructCopy() { CORRADE_VERIFY(!(std::is_constructible{})); CORRADE_VERIFY(!(std::is_assignable{})); } +void ImageTest::constructCopyCompressed() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + void ImageTest::constructMove() { auto data = new char[3]; Image2D a(ColorFormat::Red, ColorType::UnsignedByte, {1, 3}, data); @@ -93,6 +120,33 @@ void ImageTest::constructMove() { CORRADE_COMPARE(c.data(), data); } +void ImageTest::constructMoveCompressed() { + auto data = new char[8]; + CompressedImage2D a{CompressedColorFormat::RGBAS3tcDxt1, {4, 4}, Containers::Array{data, 8}}; + CompressedImage2D b{std::move(a)}; + + CORRADE_COMPARE(a.data(), nullptr); + CORRADE_COMPARE(a.size(), Vector2i()); + + CORRADE_COMPARE(b.format(), CompressedColorFormat::RGBAS3tcDxt1); + CORRADE_COMPARE(b.size(), Vector2i(4, 4)); + CORRADE_COMPARE(b.data(), data); + CORRADE_COMPARE(b.data().size(), 8); + + auto data2 = new char[16]; + CompressedImage2D c{CompressedColorFormat::RGBAS3tcDxt3, {8, 4}, Containers::Array{data2, 16}}; + c = std::move(b); + + CORRADE_COMPARE_AS(b.data(), data2, char*); + CORRADE_COMPARE(b.data().size(), 16); + CORRADE_COMPARE(b.size(), Vector2i(8, 4)); + + CORRADE_COMPARE(c.format(), CompressedColorFormat::RGBAS3tcDxt1); + CORRADE_COMPARE(c.size(), Vector2i(4, 4)); + CORRADE_COMPARE(c.data(), data); + CORRADE_COMPARE(c.data().size(), 8); +} + void ImageTest::setData() { auto data = new char[3]; Image2D a(ColorFormat::Red, ColorType::UnsignedByte, {1, 3}, data); @@ -105,6 +159,18 @@ void ImageTest::setData() { CORRADE_COMPARE(a.data(), data2); } +void ImageTest::setDataCompressed() { + auto data = new char[8]; + CompressedImage2D a{CompressedColorFormat::RGBAS3tcDxt1, {4, 4}, Containers::Array{data, 8}}; + auto data2 = new char[16]; + a.setData(CompressedColorFormat::RGBAS3tcDxt3, {8, 4}, Containers::Array{data2, 16}); + + CORRADE_COMPARE(a.format(), CompressedColorFormat::RGBAS3tcDxt3); + CORRADE_COMPARE(a.size(), Vector2i(8, 4)); + CORRADE_COMPARE(a.data(), data2); + CORRADE_COMPARE(a.data().size(), 16); +} + void ImageTest::toReference() { auto data = new char[3]; const Image2D a(ColorFormat::Red, ColorType::UnsignedByte, {1, 3}, data); @@ -125,6 +191,26 @@ void ImageTest::toReference() { } } +void ImageTest::toReferenceCommpressed() { + auto data = new char[8]; + const CompressedImage2D a{CompressedColorFormat::RGBAS3tcDxt1, {4, 4}, Containers::Array{data, 8}}; + CompressedImageView2D b = a; + + CORRADE_COMPARE(b.format(), CompressedColorFormat::RGBAS3tcDxt1); + CORRADE_COMPARE(b.size(), Vector2i(4, 4)); + CORRADE_COMPARE(b.data(), data); + CORRADE_COMPARE(b.data().size(), 8); + + CORRADE_VERIFY((std::is_convertible::value)); + { + #ifdef CORRADE_GCC47_COMPATIBILITY + CORRADE_EXPECT_FAIL("Rvalue references for *this are not supported in GCC < 4.8.1."); + #endif + CORRADE_VERIFY(!(std::is_convertible::value)); + CORRADE_VERIFY(!(std::is_convertible::value)); + } +} + void ImageTest::release() { char data[] = {'c', 'a', 'f', 'e'}; Image2D a(ColorFormat::Red, ColorType::UnsignedByte, {1, 4}, data); @@ -135,6 +221,16 @@ void ImageTest::release() { CORRADE_COMPARE(a.size(), Vector2i()); } +void ImageTest::releaseCompressed() { + char data[8]; + CompressedImage2D a{CompressedColorFormat::RGBAS3tcDxt1, {4, 4}, Containers::Array{data, 8}}; + const char* const pointer = a.release().release(); + + CORRADE_COMPARE(pointer, data); + CORRADE_COMPARE(a.data(), nullptr); + CORRADE_COMPARE(a.size(), Vector2i()); +} + }} CORRADE_TEST_MAIN(Magnum::Test::ImageTest) diff --git a/src/Magnum/Test/ImageViewTest.cpp b/src/Magnum/Test/ImageViewTest.cpp index aaeee1575..fcef7c7a2 100644 --- a/src/Magnum/Test/ImageViewTest.cpp +++ b/src/Magnum/Test/ImageViewTest.cpp @@ -34,12 +34,18 @@ struct ImageViewTest: TestSuite::Tester { explicit ImageViewTest(); void construct(); + void constructCompressed(); + void setData(); + void setDataCompressed(); }; ImageViewTest::ImageViewTest() { addTests({&ImageViewTest::construct, - &ImageViewTest::setData}); + &ImageViewTest::constructCompressed, + + &ImageViewTest::setData, + &ImageViewTest::setDataCompressed}); } void ImageViewTest::construct() { @@ -52,6 +58,15 @@ void ImageViewTest::construct() { CORRADE_COMPARE(a.data(), data); } +void ImageViewTest::constructCompressed() { + const char data[8]{}; + CompressedImageView2D a{CompressedColorFormat::RGBAS3tcDxt1, {4, 4}, data}; + + CORRADE_COMPARE(a.format(), CompressedColorFormat::RGBAS3tcDxt1); + CORRADE_COMPARE(a.size(), Vector2i(4, 4)); + CORRADE_COMPARE(a.data(), data); +} + void ImageViewTest::setData() { const char data[3]{}; ImageView2D a(ColorFormat::Red, ColorType::UnsignedByte, {1, 3}, data); @@ -64,6 +79,17 @@ void ImageViewTest::setData() { CORRADE_COMPARE(a.data(), data2); } +void ImageViewTest::setDataCompressed() { + const char data[8]{}; + CompressedImageView2D a{CompressedColorFormat::RGBAS3tcDxt1, {4, 4}, data}; + const char data2[16]{}; + a.setData(data2); + + CORRADE_COMPARE(a.format(), CompressedColorFormat::RGBAS3tcDxt1); + CORRADE_COMPARE(a.size(), Vector2i(4, 4)); + CORRADE_COMPARE(a.data(), data2); +} + }} CORRADE_TEST_MAIN(Magnum::Test::ImageViewTest) diff --git a/src/Magnum/Trade/ImageData.cpp b/src/Magnum/Trade/ImageData.cpp new file mode 100644 index 000000000..b0bc02027 --- /dev/null +++ b/src/Magnum/Trade/ImageData.cpp @@ -0,0 +1,83 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "ImageData.h" + +namespace Magnum { namespace Trade { + +template ColorFormat ImageData::format() const { + CORRADE_ASSERT(!_compressed, "Trade::ImageData::format(): the image is compressed", {}); + return _format; +} + +template ColorType ImageData::type() const { + CORRADE_ASSERT(!_compressed, "Trade::ImageData::type(): the image is compressed", {}); + return _type; +} + +template CompressedColorFormat ImageData::compressedFormat() const { + CORRADE_ASSERT(_compressed, "Trade::ImageData::format(): the image is not compressed", {}); + return _compressedFormat; +} + +template std::size_t ImageData::pixelSize() const { + CORRADE_ASSERT(!_compressed, "Trade::ImageData::pixelSize(): the image is compressed", {}); + return Implementation::imagePixelSize(_format, _type); +} + +template std::size_t ImageData::dataSize(const VectorTypeFor< dimensions, Int >& size) const { + CORRADE_ASSERT(!_compressed, "Trade::ImageData::dataSize(): the image is compressed", {}); + return Implementation::imageDataSize(*this, _format, _type, size); +} + +template ImageData::operator ImageView() +#ifndef CORRADE_GCC47_COMPATIBILITY +const & +#else +const +#endif +{ + CORRADE_ASSERT(!_compressed, "Trade::ImageData::type(): the image is compressed", (ImageView{_format, _type, _size})); + return ImageView{_format, _type, _size, _data}; +} + +template ImageData::operator CompressedImageView() +#ifndef CORRADE_GCC47_COMPATIBILITY +const & +#else +const +#endif +{ + CORRADE_ASSERT(_compressed, "Trade::ImageData::type(): the image is not compressed", (CompressedImageView{_compressedFormat, _size})); + return CompressedImageView{_compressedFormat, _size, _data}; +} + +#ifndef DOXYGEN_GENERATING_OUTPUT +template class MAGNUM_EXPORT ImageData<1>; +template class MAGNUM_EXPORT ImageData<2>; +template class MAGNUM_EXPORT ImageData<3>; +#endif + +}} diff --git a/src/Magnum/Trade/ImageData.h b/src/Magnum/Trade/ImageData.h index 26be30561..584943ca3 100644 --- a/src/Magnum/Trade/ImageData.h +++ b/src/Magnum/Trade/ImageData.h @@ -29,6 +29,8 @@ * @brief Class @ref Magnum::Trade::ImageData, typedef @ref Magnum::Trade::ImageData1D, @ref Magnum::Trade::ImageData2D, @ref Magnum::Trade::ImageData3D */ +#include + #include "Magnum/ImageView.h" namespace Magnum { namespace Trade { @@ -36,18 +38,26 @@ namespace Magnum { namespace Trade { /** @brief Image data -Access to image data provided by @ref AbstractImporter subclasses. -Interchangeable with @ref Image, @ref ImageView or @ref BufferImage. +Access to either uncompressed or compressed image data provided by +@ref AbstractImporter subclasses, the compression state is distinguished with +@ref isCompressed(). Uncompressed images have @ref format(), @ref type(), +@ref pixelSize() and @ref dataSize() properties and are convertible to +@ref ImageView. Compressed images have just the @ref compressedFormat() +property and are convertible to @ref CompressedImageView. + +Uncompressed image is interchangeable with @ref Image, @ref ImageView or +@ref BufferImage, compressed with @ref CompressedImage, @ref CompressedImageView +or @ref CompressedBufferImage. @see @ref ImageData1D, @ref ImageData2D, @ref ImageData3D */ -template class ImageData: public AbstractImage { +template class ImageData: public AbstractCompressedImage { public: enum: UnsignedInt { Dimensions = dimensions /**< Image dimension count */ }; /** - * @brief Constructor + * @brief Construct uncompressed image data * @param format Format of pixel data * @param type Data type of pixel data * @param size Image size @@ -56,7 +66,18 @@ template class ImageData: public AbstractImage { * Note that the image data are not copied on construction, but they * are deleted on class destruction. */ - explicit ImageData(ColorFormat format, ColorType type, const VectorTypeFor& size, void* data): _format{format}, _type{type}, _size{size}, _data{reinterpret_cast(data)} {} + explicit ImageData(ColorFormat format, ColorType type, const VectorTypeFor& size, void* data): _compressed{false}, _format{format}, _type{type}, _size{size}, _data{reinterpret_cast(data), dataSize(size)} {} + + /** + * @brief Construct compressed image data + * @param format Format of compressed data + * @param size Image size + * @param data Image data + * + * Note that the image data are not copied on construction, but they + * are deleted on class destruction. + */ + explicit ImageData(CompressedColorFormat format, const VectorTypeFor& size, Containers::Array&& data): _compressed{true}, _compressedFormat{format}, _size{size}, _data{std::move(data)} {} /** @brief Copying is not allowed */ ImageData(const ImageData&) = delete; @@ -70,10 +91,15 @@ template class ImageData: public AbstractImage { /** @brief Move assignment */ ImageData& operator=(ImageData&& other) noexcept; - /** @brief Destructor */ - ~ImageData() { delete[] _data; } + /** @brief Whether the image is compressed */ + bool isCompressed() const { return _compressed; } - /** @brief Conversion to view */ + /** + * @brief Conversion to view + * + * The image is expected to be uncompressed. + * @see @ref isCompressed() + */ /*implicit*/ operator ImageView() #ifndef CORRADE_GCC47_COMPATIBILITY const &; @@ -86,35 +112,86 @@ template class ImageData: public AbstractImage { /*implicit*/ operator ImageView() const && = delete; #endif - /** @brief Format of pixel data */ - ColorFormat format() const { return _format; } + /** + * @brief Conversion to compressed view + * + * The image is expected to be compressed. + * @see @ref isCompressed() + */ + /*implicit*/ operator CompressedImageView() + #ifndef CORRADE_GCC47_COMPATIBILITY + const &; + #else + const; + #endif - /** @brief Data type of pixel data */ - ColorType type() const { return _type; } + #ifndef CORRADE_GCC47_COMPATIBILITY + /** @overload */ + /*implicit*/ operator CompressedImageView() const && = delete; + #endif - /** @brief Pixel size (in bytes) */ - std::size_t pixelSize() const { return Implementation::imagePixelSize(_format, _type); } + /** + * @brief Format of pixel data + * + * The image is expected to be uncompressed. + * @see @ref isCompressed() + */ + ColorFormat format() const; + + /** + * @brief Data type of pixel data + * + * The image is expected to be uncompressed. + * @see @ref isCompressed() + */ + ColorType type() const; + + /** + * @brief Format of compressed data + * + * The image is expected to be compressed. + * @see @ref isCompressed() + */ + CompressedColorFormat compressedFormat() const; + + /** + * @brief Pixel size (in bytes) + * + * The image is expected to be uncompressed. + * @see @ref isCompressed() + */ + std::size_t pixelSize() const; /** @brief Image size */ VectorTypeFor size() const { return _size; } - /** @copydoc Image::dataSize() */ - std::size_t dataSize(const VectorTypeFor& size) const { - return Implementation::imageDataSize(*this, _format, _type, size); - } + /** + * @brief Size of data required to store uncompressed image of given size + * + * The image is expected to be uncompressed. Takes color format, type + * and row alignment of this image into account. + * @see @ref isCompressed(), @ref pixelSize() + */ + std::size_t dataSize(const VectorTypeFor& size) const; + + /** @brief Raw data */ + Containers::ArrayView data() { return _data; } + + /** @overload */ + Containers::ArrayView data() const { return _data; } /** * @brief Pointer to raw data * * @see @ref release() */ - template T* data() { - return reinterpret_cast(_data); + template T* data() { + return reinterpret_cast(_data.data()); } /** @overload */ - template const T* data() const { - return reinterpret_cast(_data); + template const T* data() const { + return reinterpret_cast(_data.data()); } /** @@ -124,13 +201,17 @@ template class ImageData: public AbstractImage { * to default. Deleting the returned array is then user responsibility. * @see @ref data() */ - char* release(); + Containers::Array release(); private: - ColorFormat _format; + bool _compressed; + union { + ColorFormat _format; + CompressedColorFormat _compressedFormat; + }; ColorType _type; Math::Vector _size; - char* _data; + Containers::Array _data; }; /** @brief One-dimensional image */ @@ -142,34 +223,28 @@ typedef ImageData<2> ImageData2D; /** @brief Three-dimensional image */ typedef ImageData<3> ImageData3D; -template inline ImageData::ImageData(ImageData&& other) noexcept: AbstractImage{std::move(other)}, _format{std::move(other._format)}, _type{std::move(other._type)}, _size{std::move(other._size)}, _data{std::move(other._data)} { +template inline ImageData::ImageData(ImageData&& other) noexcept: AbstractCompressedImage{std::move(other)}, _compressed{std::move(other._compressed)}, _type{std::move(other._type)}, _size{std::move(other._size)}, _data{std::move(other._data)} { + if(_compressed) _compressedFormat = std::move(other._compressedFormat); + else _format = std::move(other._format); + other._size = {}; other._data = nullptr; } template inline ImageData& ImageData::operator=(ImageData&& other) noexcept { - AbstractImage::operator=(std::move(other)); + AbstractCompressedImage::operator=(std::move(other)); using std::swap; - swap(_format, other._format); + swap(_compressed, other._compressed); + if(_compressed) swap(_compressedFormat, other._compressedFormat); + else swap(_format, other._format); swap(_type, other._type); swap(_size, other._size); swap(_data, other._data); return *this; } -template inline ImageData::operator ImageView() -#ifndef CORRADE_GCC47_COMPATIBILITY -const & -#else -const -#endif -{ - return ImageView(_format, _type, _size, _data); -} - -template inline char* ImageData::release() { - /** @todo I need `std::exchange` NOW. */ - char* const data = _data; +template inline Containers::Array ImageData::release() { + Containers::Array data{std::move(_data)}; _size = {}; _data = nullptr; return data; diff --git a/src/Magnum/Trade/Test/ImageDataTest.cpp b/src/Magnum/Trade/Test/ImageDataTest.cpp index 257b03c92..ca2140249 100644 --- a/src/Magnum/Trade/Test/ImageDataTest.cpp +++ b/src/Magnum/Trade/Test/ImageDataTest.cpp @@ -35,32 +35,52 @@ class ImageDataTest: public TestSuite::Tester { explicit ImageDataTest(); void construct(); + void constructCompressed(); void constructCopy(); void constructMove(); + void constructMoveCompressed(); void toReference(); + void toReferenceCompressed(); void release(); + void releaseCompressed(); }; ImageDataTest::ImageDataTest() { addTests({&ImageDataTest::construct, + &ImageDataTest::constructCompressed, &ImageDataTest::constructCopy, &ImageDataTest::constructMove, + &ImageDataTest::constructMoveCompressed, &ImageDataTest::toReference, - &ImageDataTest::release}); + &ImageDataTest::toReferenceCompressed, + &ImageDataTest::release, + &ImageDataTest::releaseCompressed}); } void ImageDataTest::construct() { auto data = new char[3]; Trade::ImageData2D a(ColorFormat::Red, ColorType::UnsignedByte, {1, 3}, data); + CORRADE_VERIFY(!a.isCompressed()); CORRADE_COMPARE(a.format(), ColorFormat::Red); CORRADE_COMPARE(a.type(), ColorType::UnsignedByte); CORRADE_COMPARE(a.size(), Vector2i(1, 3)); CORRADE_COMPARE(a.data(), data); } +void ImageDataTest::constructCompressed() { + auto data = new char[8]; + Trade::ImageData2D a{CompressedColorFormat::RGBAS3tcDxt1, {4, 4}, Containers::Array{data, 8}}; + + CORRADE_VERIFY(a.isCompressed()); + CORRADE_COMPARE(a.compressedFormat(), CompressedColorFormat::RGBAS3tcDxt1); + CORRADE_COMPARE(a.size(), Vector2i(4, 4)); + CORRADE_COMPARE(a.data(), data); + CORRADE_COMPARE(a.data().size(), 8); +} + void ImageDataTest::constructCopy() { CORRADE_VERIFY(!(std::is_constructible{})); CORRADE_VERIFY(!(std::is_assignable{})); @@ -74,6 +94,7 @@ void ImageDataTest::constructMove() { CORRADE_COMPARE(a.data(), nullptr); CORRADE_COMPARE(a.size(), Vector2i()); + CORRADE_VERIFY(!b.isCompressed()); CORRADE_COMPARE(b.format(), ColorFormat::Red); CORRADE_COMPARE(b.type(), ColorType::UnsignedByte); CORRADE_COMPARE(b.size(), Vector2i(1, 3)); @@ -86,12 +107,42 @@ void ImageDataTest::constructMove() { CORRADE_COMPARE(b.data(), data2); CORRADE_COMPARE(b.size(), Vector2i(2, 6)); + CORRADE_VERIFY(!c.isCompressed()); CORRADE_COMPARE(c.format(), ColorFormat::Red); CORRADE_COMPARE(c.type(), ColorType::UnsignedByte); CORRADE_COMPARE(c.size(), Vector2i(1, 3)); CORRADE_COMPARE(c.data(), data); } +void ImageDataTest::constructMoveCompressed() { + auto data = new char[8]; + Trade::ImageData2D a{CompressedColorFormat::RGBAS3tcDxt1, {4, 4}, Containers::Array{data, 8}}; + Trade::ImageData2D b{std::move(a)}; + + CORRADE_COMPARE(a.data(), nullptr); + CORRADE_COMPARE(a.size(), Vector2i()); + + CORRADE_VERIFY(b.isCompressed()); + CORRADE_COMPARE(b.compressedFormat(), CompressedColorFormat::RGBAS3tcDxt1); + CORRADE_COMPARE(b.size(), Vector2i(4, 4)); + CORRADE_COMPARE(b.data(), data); + CORRADE_COMPARE(b.data().size(), 8); + + auto data2 = new char[16]; + Trade::ImageData2D c{CompressedColorFormat::RGBAS3tcDxt3, {8, 4}, Containers::Array{data2, 16}}; + c = std::move(b); + + CORRADE_COMPARE_AS(b.data(), data2, char*); + CORRADE_COMPARE(b.data().size(), 16); + CORRADE_COMPARE(b.size(), Vector2i(8, 4)); + + CORRADE_VERIFY(c.isCompressed()); + CORRADE_COMPARE(c.compressedFormat(), CompressedColorFormat::RGBAS3tcDxt1); + CORRADE_COMPARE(c.size(), Vector2i(4, 4)); + CORRADE_COMPARE(c.data(), data); + CORRADE_COMPARE(c.data().size(), 8); +} + void ImageDataTest::toReference() { auto data = new char[3]; const Trade::ImageData2D a(ColorFormat::Red, ColorType::UnsignedByte, {1, 3}, data); @@ -112,10 +163,40 @@ void ImageDataTest::toReference() { } } +void ImageDataTest::toReferenceCompressed() { + auto data = new char[8]; + const Trade::ImageData2D a{CompressedColorFormat::RGBAS3tcDxt1, {4, 4}, Containers::Array{data, 8}}; + CompressedImageView2D b = a; + + CORRADE_COMPARE(b.format(), CompressedColorFormat::RGBAS3tcDxt1); + CORRADE_COMPARE(b.size(), Vector2i(4, 4)); + CORRADE_COMPARE(b.data(), data); + CORRADE_COMPARE(b.data().size(), 8); + + CORRADE_VERIFY((std::is_convertible::value)); + { + #ifdef CORRADE_GCC47_COMPATIBILITY + CORRADE_EXPECT_FAIL("Rvalue references for *this are not supported in GCC < 4.8.1."); + #endif + CORRADE_VERIFY(!(std::is_convertible::value)); + CORRADE_VERIFY(!(std::is_convertible::value)); + } +} + void ImageDataTest::release() { char data[] = {'b', 'e', 'e', 'r'}; Trade::ImageData2D a(ColorFormat::Red, ColorType::UnsignedByte, {1, 4}, data); - const char* const pointer = a.release(); + const char* const pointer = a.release().release(); + + CORRADE_COMPARE(pointer, data); + CORRADE_COMPARE(a.data(), nullptr); + CORRADE_COMPARE(a.size(), Vector2i()); +} + +void ImageDataTest::releaseCompressed() { + char data[8]; + Trade::ImageData2D a{CompressedColorFormat::RGBAS3tcDxt1, {4, 4}, Containers::Array{data, 8}}; + const char* const pointer = a.release().release(); CORRADE_COMPARE(pointer, data); CORRADE_COMPARE(a.data(), nullptr);