From 913a08e26792eaf986a68ca47c2fbd91a36e72d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 23 Jul 2015 17:00:51 +0200 Subject: [PATCH] Compressed image support, part 6: Compressed* image classes. Added (now empty) AbstractCompressedImage class that inherits (now also empty) AbstractImage class. Added CompressedBufferImage, CompressedImage and CompressedImageView classes, which are just copies of BufferImage, Image and ImageView classes with format/type pair replaced by just format, but they additionally need data size parameter. Because of different use cases in Trade, the Trade::ImageData class now handles both uncompressed and compressed format, checking the API usage with runtime assertions. The reason for this is that a material could just reference a image file by ID and we need to be able to extract image of that ID without prior knowledge whether it is compressed or not. Requiring prior knowledge of image format from the user would make both the API and the usage far more complicated than having Trade::ImageData which handles both cases. On the other hand, the Image*/CompressedImage* distinction is done for easier usage and type-safe APIs in all other cases. --- src/Magnum/AbstractImage.h | 17 ++- src/Magnum/BufferImage.cpp | 17 +++ src/Magnum/BufferImage.h | 114 ++++++++++++++++- src/Magnum/CMakeLists.txt | 1 + src/Magnum/Image.cpp | 10 ++ src/Magnum/Image.h | 156 ++++++++++++++++++++++- src/Magnum/ImageView.h | 82 ++++++++++++- src/Magnum/Magnum.h | 17 ++- src/Magnum/Test/BufferImageGLTest.cpp | 97 ++++++++++++++- src/Magnum/Test/ImageTest.cpp | 98 ++++++++++++++- src/Magnum/Test/ImageViewTest.cpp | 28 ++++- src/Magnum/Trade/ImageData.cpp | 83 +++++++++++++ src/Magnum/Trade/ImageData.h | 157 +++++++++++++++++------- src/Magnum/Trade/Test/ImageDataTest.cpp | 85 ++++++++++++- 14 files changed, 907 insertions(+), 55 deletions(-) create mode 100644 src/Magnum/Trade/ImageData.cpp 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);