Browse Source

Pixel storage support, part 6: accepting only sized data arrays in images.

Makes it far easier to detect pixel storage misconfigurations and
improperly sized data arrays. Data owning classes (Image,
Trade::ImageData) accept Containers::Array<char> while wrappers
(ImageView, BufferImage) accept Containers::ArrayView<const void>.
ImageView reinterprets the passed array as const char to enable pointer
arithmetic on the data.

The old way (constructor/setData() call accepting void*) is now marked as
deprecated and will be removed in some future release. Because decay of
fixed-size arrays to void* is preferred to calling Containers::ArrayView
constructor, there are two more overloads to have proper handling of
const T(&)[n] and std::nullptr_t arguments.

Currently the TgaImporter and TgaImageConverter fail on images with row
length not aligned to 4, will fix that in followup commits.
pull/107/head
Vladimír Vondruš 11 years ago
parent
commit
3fd3ed36df
  1. 12
      src/Magnum/AbstractFramebuffer.cpp
  2. 18
      src/Magnum/AbstractTexture.cpp
  3. 10
      src/Magnum/BufferImage.cpp
  4. 48
      src/Magnum/BufferImage.h
  5. 18
      src/Magnum/CubeMapTexture.cpp
  6. 10
      src/Magnum/Image.cpp
  7. 85
      src/Magnum/Image.h
  8. 65
      src/Magnum/ImageView.h
  9. 7
      src/Magnum/PixelStorage.h
  10. 18
      src/Magnum/Test/ImageTest.cpp
  11. 2
      src/Magnum/Test/ImageViewTest.cpp
  12. 4
      src/Magnum/Trade/ImageData.cpp
  13. 33
      src/Magnum/Trade/ImageData.h
  14. 2
      src/Magnum/Trade/Test/AbstractImageConverterTest.cpp
  15. 16
      src/Magnum/Trade/Test/ImageDataTest.cpp
  16. 2
      src/MagnumPlugins/TgaImageConverter/TgaImageConverter.cpp
  17. 8
      src/MagnumPlugins/TgaImporter/TgaImporter.cpp

12
src/Magnum/AbstractFramebuffer.cpp

@ -280,10 +280,9 @@ AbstractFramebuffer& AbstractFramebuffer::clear(const FramebufferClearMask mask)
void AbstractFramebuffer::read(const Range2Di& rectangle, Image2D& image) {
bindInternal(FramebufferTarget::Read);
const std::size_t dataSize = Implementation::imageDataSizeFor(image, rectangle.size());
char* const data = new char[dataSize];
(Context::current()->state().framebuffer->readImplementation)(rectangle, image.format(), image.type(), dataSize, data);
image.setData(image.storage(), image.format(), image.type(), rectangle.size(), data);
Containers::Array<char> data{Implementation::imageDataSizeFor(image, rectangle.size())};
(Context::current()->state().framebuffer->readImplementation)(rectangle, image.format(), image.type(), data.size(), data);
image.setData(image.storage(), image.format(), image.type(), rectangle.size(), std::move(data));
}
Image2D AbstractFramebuffer::read(const Range2Di& rectangle, Image2D&& image) {
@ -296,11 +295,12 @@ void AbstractFramebuffer::read(const Range2Di& rectangle, BufferImage2D& image,
bindInternal(FramebufferTarget::Read);
/* If the buffer doesn't have sufficient size, resize it */
/** @todo Explicitly reset also when buffer usage changes */
const std::size_t dataSize = Implementation::imageDataSizeFor(image, rectangle.size());
if(image.size() != rectangle.size())
image.setData(image.storage(), image.format(), image.type(), rectangle.size(), nullptr, usage);
image.setData(image.storage(), image.format(), image.type(), rectangle.size(), {nullptr, dataSize}, usage);
image.buffer().bindInternal(Buffer::TargetHint::PixelPack);
(Context::current()->state().framebuffer->readImplementation)(rectangle, image.format(), image.type(), Implementation::imageDataSizeFor(image, rectangle.size()), nullptr);
(Context::current()->state().framebuffer->readImplementation)(rectangle, image.format(), image.type(), dataSize, nullptr);
}
BufferImage2D AbstractFramebuffer::read(const Range2Di& rectangle, BufferImage2D&& image, BufferUsage usage) {

18
src/Magnum/AbstractTexture.cpp

@ -1494,12 +1494,11 @@ void AbstractTexture::invalidateSubImageImplementationARB(GLint level, const Vec
#ifndef MAGNUM_TARGET_GLES
template<UnsignedInt dimensions> void AbstractTexture::image(GLint level, Image<dimensions>& image) {
const Math::Vector<dimensions, Int> size = DataHelper<dimensions>::imageSize(*this, level);
const std::size_t dataSize = Implementation::imageDataSizeFor(image, size);
char* data = new char[dataSize];
Containers::Array<char> data{Implementation::imageDataSizeFor(image, size)};
Buffer::unbindInternal(Buffer::TargetHint::PixelPack);
(this->*Context::current()->state().texture->getImageImplementation)(level, image.format(), image.type(), dataSize, data);
image.setData(image.storage(), image.format(), image.type(), size, data);
(this->*Context::current()->state().texture->getImageImplementation)(level, image.format(), image.type(), data.size(), data);
image.setData(image.storage(), image.format(), image.type(), size, std::move(data));
}
template void MAGNUM_EXPORT AbstractTexture::image<1>(GLint, Image<1>&);
@ -1510,7 +1509,7 @@ template<UnsignedInt dimensions> void AbstractTexture::image(GLint level, Buffer
const Math::Vector<dimensions, Int> size = DataHelper<dimensions>::imageSize(*this, level);
const std::size_t dataSize = Implementation::imageDataSizeFor(image, size);
if(image.size() != size)
image.setData(image.storage(), image.format(), image.type(), size, nullptr, usage);
image.setData(image.storage(), image.format(), image.type(), size, {nullptr, dataSize}, usage);
image.buffer().bindInternal(Buffer::TargetHint::PixelPack);
(this->*Context::current()->state().texture->getImageImplementation)(level, image.format(), image.type(), dataSize, nullptr);
@ -1560,12 +1559,11 @@ template<UnsignedInt dimensions> void AbstractTexture::subImage(const GLint leve
const Math::Vector<dimensions, Int> size = range.size();
const Vector3i paddedOffset = Vector3i::pad(range.min());
const Vector3i paddedSize = Vector3i::pad(size, 1);
const std::size_t dataSize = Implementation::imageDataSizeFor(image, size);
char* data = new char[dataSize];
Containers::Array<char> data{Implementation::imageDataSizeFor(image, size)};
Buffer::unbindInternal(Buffer::TargetHint::PixelPack);
glGetTextureSubImage(_id, level, paddedOffset.x(), paddedOffset.y(), paddedOffset.z(), paddedSize.x(), paddedSize.y(), paddedSize.z(), GLenum(image.format()), GLenum(image.type()), dataSize, data);
image.setData(image.storage(), image.format(), image.type(), size, data);
glGetTextureSubImage(_id, level, paddedOffset.x(), paddedOffset.y(), paddedOffset.z(), paddedSize.x(), paddedSize.y(), paddedSize.z(), GLenum(image.format()), GLenum(image.type()), data.size(), data);
image.setData(image.storage(), image.format(), image.type(), size, std::move(data));
}
template void MAGNUM_EXPORT AbstractTexture::subImage<1>(GLint, const Range1Di&, Image<1>&);
@ -1580,7 +1578,7 @@ template<UnsignedInt dimensions> void AbstractTexture::subImage(const GLint leve
const Vector3i paddedOffset = Vector3i::pad(range.min());
const Vector3i paddedSize = Vector3i::pad(size, 1);
if(image.size() != size)
image.setData(image.storage(), image.format(), image.type(), size, nullptr, usage);
image.setData(image.storage(), image.format(), image.type(), size, {nullptr, dataSize}, usage);
image.buffer().bindInternal(Buffer::TargetHint::PixelPack);
glGetTextureSubImage(_id, level, paddedOffset.x(), paddedOffset.y(), paddedOffset.z(), paddedSize.x(), paddedSize.y(), paddedSize.z(), GLenum(image.format()), GLenum(image.type()), dataSize, nullptr);

10
src/Magnum/BufferImage.cpp

@ -28,18 +28,20 @@
namespace Magnum {
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> BufferImage<dimensions>::BufferImage(const PixelStorage storage, const PixelFormat format, const PixelType type, const VectorTypeFor<dimensions, Int>& size, const void* const data, const BufferUsage usage): _storage{storage}, _format{format}, _type{type}, _size{size}, _buffer{Buffer::TargetHint::PixelPack} {
_buffer.setData({data, Implementation::imageDataSizeFor(*this, size)}, usage);
template<UnsignedInt dimensions> BufferImage<dimensions>::BufferImage(const PixelStorage storage, const PixelFormat format, const PixelType type, const VectorTypeFor<dimensions, Int>& size, Containers::ArrayView<const void> const data, const BufferUsage usage): _storage{storage}, _format{format}, _type{type}, _size{size}, _buffer{Buffer::TargetHint::PixelPack} {
CORRADE_ASSERT(Implementation::imageDataSize(*this) <= data.size(), "BufferImage::BufferImage(): bad image data size, got" << data.size() << "but expected at least" << Implementation::imageDataSize(*this), );
_buffer.setData(data, usage);
}
template<UnsignedInt dimensions> BufferImage<dimensions>::BufferImage(const PixelStorage storage, const PixelFormat format, const PixelType type): _storage{storage}, _format{format}, _type{type}, _buffer{Buffer::TargetHint::PixelPack} {}
template<UnsignedInt dimensions> void BufferImage<dimensions>::setData(const PixelStorage storage, const PixelFormat format, const PixelType type, const VectorTypeFor<dimensions, Int>& size, const void* const data, const BufferUsage usage) {
template<UnsignedInt dimensions> void BufferImage<dimensions>::setData(const PixelStorage storage, const PixelFormat format, const PixelType type, const VectorTypeFor<dimensions, Int>& size, Containers::ArrayView<const void> const data, const BufferUsage usage) {
_storage = storage;
_format = format;
_type = type;
_size = size;
_buffer.setData({data, Implementation::imageDataSizeFor(*this, size)}, usage);
CORRADE_ASSERT(Implementation::imageDataSize(*this) <= data.size(), "BufferImage::setData(): bad image data size, got" << data.size() << "but expected at least" << Implementation::imageDataSize(*this), );
_buffer.setData(data, usage);
}
template<UnsignedInt dimensions> CompressedBufferImage<dimensions>::CompressedBufferImage(

48
src/Magnum/BufferImage.h

@ -64,16 +64,30 @@ template<UnsignedInt dimensions> class BufferImage {
* @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 BufferImage(PixelStorage storage, PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, const void* data, BufferUsage usage);
explicit BufferImage(PixelStorage storage, PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, Containers::ArrayView<const void> data, BufferUsage usage);
/** @overload
* Similar to the above, but uses default @ref PixelStorage parameters.
*/
explicit BufferImage(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, const void* data, BufferUsage usage): BufferImage{{}, format, type, size, data, usage} {}
explicit BufferImage(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, Containers::ArrayView<const void> data, BufferUsage usage): BufferImage{{}, format, type, size, data, usage} {}
#ifdef MAGNUM_BUILD_DEPRECATED
/** @copybrief BufferImage(PixelFormat, PixelType, const VectorTypeFor<dimensions, Int>&, Containers::ArrayView<const void>, BufferUsage)
* @deprecated Use @ref BufferImage(PixelFormat, PixelType, const VectorTypeFor<dimensions, Int>&, Containers::ArrayView<const void>, BufferUsage)
* instead.
*/
explicit CORRADE_DEPRECATED("use BufferImage(PixelFormat, PixelType, const VectorTypeFor&, Containers::ArrayView, BufferUsage) instead") BufferImage(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, const void* data, BufferUsage usage): BufferImage{{}, format, type, size, {data, Implementation::imageDataSizeFor(format, type, size)}, usage} {}
#ifndef DOXYGEN_GENERATING_OUTPUT
/* To avoid decay of sized arrays and nullptr to const void* and
unwanted use of deprecated function */
template<class T, std::size_t dataSize> explicit BufferImage(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, const T(&data)[dataSize], BufferUsage usage): BufferImage{{}, format, type, size, Containers::ArrayView<const void>{data}, usage} {}
explicit BufferImage(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, std::nullptr_t, BufferUsage usage): BufferImage{{}, format, type, size, Containers::ArrayView<const void>{nullptr}, usage} {}
#endif
#endif
/**
* @brief Constructor
@ -146,21 +160,41 @@ template<UnsignedInt dimensions> class BufferImage {
* @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.
* Updates the image buffer with given data.
* @see @ref Buffer::setData()
* @todo Make it more flexible (usable with
* @extension{ARB,buffer_storage}, avoiding relocations...)
*/
void setData(PixelStorage storage, PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, const void* data, BufferUsage usage);
void setData(PixelStorage storage, PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, Containers::ArrayView<const void> data, BufferUsage usage);
/** @overload
* Similar to the above, but uses default @ref PixelStorage parameters.
*/
void setData(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, const void* data, BufferUsage usage) {
void setData(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, Containers::ArrayView<const void> data, BufferUsage usage) {
setData({}, format, type, size, data, usage);
}
#ifdef MAGNUM_BUILD_DEPRECATED
/** @copybrief setData(PixelFormat, PixelType, const VectorTypeFor<dimensions, Int>&, Containers::ArrayView<const void>, BufferUsage)
* @deprecated Use @ref setData(PixelFormat, PixelType, const VectorTypeFor<dimensions, Int>&, Containers::ArrayView<const void>, BufferUsage)
* instead.
*/
void CORRADE_DEPRECATED("use setData(PixelFormat, PixelType, const VectorTypeFor&, Containers::ArrayView, BufferUsage) instead") setData(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, const void* data, BufferUsage usage) {
setData({}, format, type, size, {data, Implementation::imageDataSizeFor(format, type, size)}, usage);
}
#ifndef DOXYGEN_GENERATING_OUTPUT
/* To avoid decay of sized char arrays and nullptr to const void* and
unwanted use of deprecated function */
template<class T, std::size_t dataSize> void setData(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, const T(&data)[dataSize], BufferUsage usage) {
setData({}, format, type, size, Containers::ArrayView<const void>{data}, usage);
}
void setData(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, std::nullptr_t, BufferUsage usage) {
setData({}, format, type, size, Containers::ArrayView<const void>{nullptr}, usage);
}
#endif
#endif
private:
PixelStorage _storage;
PixelFormat _format;

18
src/Magnum/CubeMapTexture.cpp

@ -60,11 +60,10 @@ void CubeMapTexture::image(const Int level, Image3D& image) {
createIfNotAlready();
const Vector3i size{imageSize(level), 6};
const std::size_t dataSize = Implementation::imageDataSizeFor(image, size);
char* data = new char[dataSize];
Containers::Array<char> data{Implementation::imageDataSizeFor(image, size)};
Buffer::unbindInternal(Buffer::TargetHint::PixelPack);
glGetTextureImage(_id, level, GLenum(image.format()), GLenum(image.type()), dataSize, data);
image.setData(image.storage(), image.format(), image.type(), size, data);
glGetTextureImage(_id, level, GLenum(image.format()), GLenum(image.type()), data.size(), data);
image.setData(image.storage(), image.format(), image.type(), size, std::move(data));
}
Image3D CubeMapTexture::image(const Int level, Image3D&& image) {
@ -78,7 +77,7 @@ void CubeMapTexture::image(const Int level, BufferImage3D& image, const BufferUs
const Vector3i size{imageSize(level), 6};
const std::size_t dataSize = Implementation::imageDataSizeFor(image, size);
if(image.size() != size)
image.setData(image.storage(), image.format(), image.type(), size, nullptr, usage);
image.setData(image.storage(), image.format(), image.type(), size, {nullptr, dataSize}, usage);
image.buffer().bindInternal(Buffer::TargetHint::PixelPack);
glGetTextureImage(_id, level, GLenum(image.format()), GLenum(image.type()), dataSize, nullptr);
@ -132,12 +131,11 @@ CompressedBufferImage3D CubeMapTexture::compressedImage(const Int level, Compres
void CubeMapTexture::image(const Coordinate coordinate, const Int level, Image2D& image) {
const Vector2i size = imageSize(level);
const std::size_t dataSize = Implementation::imageDataSizeFor(image, size);
char* data = new char[dataSize];
Containers::Array<char> data{Implementation::imageDataSizeFor(image, size)};
Buffer::unbindInternal(Buffer::TargetHint::PixelPack);
(this->*Context::current()->state().texture->getCubeImageImplementation)(coordinate, level, size, image.format(), image.type(), dataSize, data);
image.setData(image.storage(), image.format(), image.type(), size, data);
(this->*Context::current()->state().texture->getCubeImageImplementation)(coordinate, level, size, image.format(), image.type(), data.size(), data);
image.setData(image.storage(), image.format(), image.type(), size, std::move(data));
}
Image2D CubeMapTexture::image(const Coordinate coordinate, const Int level, Image2D&& image) {
@ -149,7 +147,7 @@ void CubeMapTexture::image(const Coordinate coordinate, const Int level, BufferI
const Vector2i size = imageSize(level);
const std::size_t dataSize = Implementation::imageDataSizeFor(image, size);
if(image.size() != size)
image.setData(image.storage(), image.format(), image.type(), size, nullptr, usage);
image.setData(image.storage(), image.format(), image.type(), size, {nullptr, dataSize}, usage);
image.buffer().bindInternal(Buffer::TargetHint::PixelPack);
(this->*Context::current()->state().texture->getCubeImageImplementation)(coordinate, level, size, image.format(), image.type(), dataSize, nullptr);

10
src/Magnum/Image.cpp

@ -27,13 +27,17 @@
namespace Magnum {
template<UnsignedInt dimensions> void Image<dimensions>::setData(PixelStorage storage, PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, void* data) {
delete[] _data;
template<UnsignedInt dimensions> Image<dimensions>::Image(PixelStorage storage, PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data): _storage{storage}, _format{format}, _type{type}, _size{size}, _data{std::move(data)} {
CORRADE_ASSERT(Implementation::imageDataSize(*this) <= _data.size(), "Image::Image(): bad image data size, got" << _data.size() << "but expected at least" << Implementation::imageDataSize(*this), );
}
template<UnsignedInt dimensions> void Image<dimensions>::setData(PixelStorage storage, PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data) {
_storage = storage;
_format = format;
_type = type;
_size = size;
_data = reinterpret_cast<char*>(data);
CORRADE_ASSERT(Implementation::imageDataSize(*this) <= data.size(), "Image::setData(): bad image data size, got" << data.size() << "but expected at least" << Implementation::imageDataSize(*this), );
_data = std::move(data);
}
template<UnsignedInt dimensions> void CompressedImage<dimensions>::setData(

85
src/Magnum/Image.h

@ -56,15 +56,23 @@ template<UnsignedInt dimensions> class Image {
* @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.
* The data are expected to be of proper size for given @p storage
* parameters.
*/
explicit Image(PixelStorage storage, PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, void* data): _storage{storage}, _format{format}, _type{type}, _size{size}, _data{reinterpret_cast<char*>(data)} {}
explicit Image(PixelStorage storage, PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data);
/** @overload
* Similar to the above, but uses default @ref PixelStorage parameters.
*/
explicit Image(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, void* data): Image{{}, format, type, size, data} {}
explicit Image(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data): Image{{}, format, type, size, std::move(data)} {}
#ifdef MAGNUM_BUILD_DEPRECATED
/** @copybrief Image(PixelFormat, PixelType, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&)
* @deprecated Use @ref Image(PixelFormat, PixelType, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&)
* instead.
*/
explicit CORRADE_DEPRECATED("use Image(PixelFormat, PixelType, const VectorTypeFor&, Containers::Array&&) instead") Image(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, void* data): Image{{}, format, type, size, Containers::Array<char>{reinterpret_cast<char*>(data), Implementation::imageDataSizeFor(format, type, size)}} {}
#endif
/**
* @brief Constructor
@ -98,9 +106,6 @@ template<UnsignedInt dimensions> class Image {
/** @brief Move assignment */
Image<dimensions>& operator=(Image<dimensions>&& other) noexcept;
/** @brief Destructor */
~Image() { delete[] _data; }
/** @brief Conversion to view */
/*implicit*/ operator ImageView<dimensions>()
#ifndef CORRADE_GCC47_COMPATIBILITY
@ -143,17 +148,23 @@ template<UnsignedInt dimensions> class Image {
}
/**
* @brief Pointer to raw data
* @brief Raw data
*
* @see @ref release()
*/
Containers::ArrayView<char> data() { return _data; }
/** @overload */
Containers::ArrayView<const char> data() const { return _data; }
/** @overload */
template<class T = char> T* data() {
return reinterpret_cast<T*>(_data);
return reinterpret_cast<T*>(_data.data());
}
/** @overload */
template<class T = char> const T* data() const {
return reinterpret_cast<const T*>(_data);
return reinterpret_cast<const T*>(_data.data());
}
/**
@ -164,34 +175,44 @@ template<UnsignedInt dimensions> class Image {
* @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.
* Deletes previous data and replaces them with new. The data are
* expected to be of proper size for given @p storage parameters.
* @see @ref release()
*/
void setData(PixelStorage storage, PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, void* data);
void setData(PixelStorage storage, PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data);
/** @overload
* Similar to the above, but uses default @ref PixelStorage parameters.
*/
void setData(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, void* data) {
setData({}, format, type, size, data);
void setData(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data) {
setData({}, format, type, size, std::move(data));
}
#ifdef MAGNUM_BUILD_DEPRECATED
/** @copybrief setData(PixelFormat, PixelType, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&)
* @deprecated Use @ref setData(PixelFormat, PixelType, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&)
* instead.
*/
void CORRADE_DEPRECATED("use setData(PixelFormat, PixelType, const VectorTypeFor&, Containers::ArrayView) instead") setData(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, void* data) {
setData({}, format, type, size, Containers::Array<char>{reinterpret_cast<char*>(data), Implementation::imageDataSizeFor(format, type, size)});
}
#endif
/**
* @brief Release data storage
*
* Releases the ownership of the data pointer and resets internal state
* to default. Deleting the returned array is then user responsibility.
* @see @ref setData()
* Releases the ownership of the data array and resets internal state
* to default.
* @see @ref data()
*/
char* release();
Containers::Array<char> release();
private:
PixelStorage _storage;
PixelFormat _format;
PixelType _type;
Math::Vector<Dimensions, Int> _size;
char* _data;
Containers::Array<char> _data;
};
/** @brief One-dimensional image */
@ -327,17 +348,17 @@ template<UnsignedInt dimensions> class CompressedImage {
}
#endif
/** @brief Raw data */
/**
* @brief Raw data
*
* @see @ref release()
*/
Containers::ArrayView<char> data() { return _data; }
/** @overload */
Containers::ArrayView<const char> data() const { return _data; }
/**
* @brief Pointer to raw data
*
* @see @ref release()
*/
/** @overload */
template<class T> T* data() {
return reinterpret_cast<T*>(_data.data());
}
@ -379,7 +400,7 @@ template<UnsignedInt dimensions> class CompressedImage {
/**
* @brief Release data storage
*
* Releases the ownership of the data pointer and resets internal state
* Releases the ownership of the data array and resets internal state
* to default.
* @see @ref setData()
*/
@ -405,7 +426,6 @@ typedef CompressedImage<3> CompressedImage3D;
template<UnsignedInt dimensions> inline Image<dimensions>::Image(Image<dimensions>&& other) noexcept: _storage{std::move(other._storage)}, _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<UnsignedInt dimensions> inline CompressedImage<dimensions>::CompressedImage(CompressedImage<dimensions>&& other) noexcept:
@ -415,7 +435,6 @@ template<UnsignedInt dimensions> inline CompressedImage<dimensions>::CompressedI
_format{std::move(other._format)}, _size{std::move(other._size)}, _data{std::move(other._data)}
{
other._size = {};
other._data = nullptr;
}
template<UnsignedInt dimensions> inline Image<dimensions>& Image<dimensions>::operator=(Image<dimensions>&& other) noexcept {
@ -463,19 +482,15 @@ const
_format, _size, _data};
}
template<UnsignedInt dimensions> inline char* Image<dimensions>::release() {
/** @todo I need `std::exchange` NOW. */
char* const data = _data;
template<UnsignedInt dimensions> inline Containers::Array<char> Image<dimensions>::release() {
Containers::Array<char> data{std::move(_data)};
_size = {};
_data = nullptr;
return data;
}
template<UnsignedInt dimensions> inline Containers::Array<char> CompressedImage<dimensions>::release() {
/** @todo I need `std::exchange` NOW. */
Containers::Array<char> data{std::move(_data)};
_size = {};
_data = nullptr;
return data;
}

65
src/Magnum/ImageView.h

@ -66,13 +66,32 @@ template<UnsignedInt dimensions> class ImageView {
* @param type Data type of pixel data
* @param size Image size
* @param data Image data
*
* The data are expected to be of proper size for given @p storage
* parameters.
*/
constexpr explicit ImageView(PixelStorage storage, PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, const void* data) noexcept: _storage{storage}, _format{format}, _type{type}, _size{size}, _data{reinterpret_cast<const char*>(data)} {}
explicit ImageView(PixelStorage storage, PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, Containers::ArrayView<const void> data) noexcept: _storage{storage}, _format{format}, _type{type}, _size{size}, _data{reinterpret_cast<const char*>(data.data()), data.size()} {
CORRADE_ASSERT(Implementation::imageDataSize(*this) <= _data.size(), "ImageView::ImageView(): bad image data size, got" << _data.size() << "but expected at least" << Implementation::imageDataSize(*this), );
}
/** @overload
* Similar to the above, but uses default @ref PixelStorage parameters.
*/
constexpr explicit ImageView(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, const void* data) noexcept: ImageView{{}, format, type, size, data} {}
explicit ImageView(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, Containers::ArrayView<const void> data) noexcept: ImageView{{}, format, type, size, data} {}
#ifdef MAGNUM_BUILD_DEPRECATED
/** @copybrief ImageView(PixelFormat, PixelType, const VectorTypeFor<dimensions, Int>&, Containers::ArrayView<const void>)
* @deprecated Use @ref ImageView(PixelFormat, PixelType, const VectorTypeFor<dimensions, Int>&, Containers::ArrayView<const void>) instead.
*/
explicit CORRADE_DEPRECATED("use ImageView(PixelFormat, PixelType, const VectorTypeFor&, Containers::ArrayView) instead") ImageView(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, const void* data) noexcept: ImageView{{}, format, type, size, {reinterpret_cast<const char*>(data), Implementation::imageDataSizeFor(format, type, size)}} {}
#ifndef DOXYGEN_GENERATING_OUTPUT
/* To avoid decay of sized arrays and nullptr to const void* and
unwanted use of deprecated function */
template<class T, std::size_t dataSize> explicit ImageView(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, const T(&data)[dataSize]): ImageView{{}, format, type, size, Containers::ArrayView<const void>{data}} {}
explicit ImageView(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, std::nullptr_t): ImageView{{}, format, type, size, Containers::ArrayView<const void>{nullptr}} {}
#endif
#endif
/**
* @brief Constructor
@ -119,32 +138,54 @@ template<UnsignedInt dimensions> class ImageView {
return Implementation::imageDataProperties<dimensions>(*this);
}
/** @brief Pointer to raw data */
constexpr const char* data() const { return _data; }
/** @brief Raw data */
constexpr Containers::ArrayView<const char> data() const { return _data; }
/** @overload */
template<class T = char> const T* data() const {
return reinterpret_cast<const T*>(_data);
template<class T> const T* data() const {
return reinterpret_cast<const T*>(_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.
* Storage parameters, pixel format, type and size remain the same as
* passed in constructor. The data are expected to be of proper size
* for given @p storage parameters.
*/
void setData(Containers::ArrayView<const void> data) {
CORRADE_ASSERT(Implementation::imageDataSize(*this) <= data.size(), "ImageView::setData(): bad image data size, got" << data.size() << "but expected at least" << Implementation::imageDataSize(*this), );
_data = {reinterpret_cast<const char*>(data.data()), data.size()};
}
#ifdef MAGNUM_BUILD_DEPRECATED
/** @copybrief setData(Containers::ArrayView<const void>)
* @deprecated Use @ref setData(Containers::ArrayView<const void>)
* instead.
*/
void setData(const void* data) {
_data = reinterpret_cast<const char*>(data);
void CORRADE_DEPRECATED("use setData(Containers::ArrayView) instead") setData(const void* data) {
setData({reinterpret_cast<const char*>(data), Implementation::imageDataSize(*this)});
}
#ifndef DOXYGEN_GENERATING_OUTPUT
/* To avoid decay of sized arrays and nullptr to const void* and
unwanted use of deprecated function */
template<class T, std::size_t size> void setData(const T(&data)[size]) {
setData(Containers::ArrayView<const void>{data});
}
void setData(std::nullptr_t) {
setData(Containers::ArrayView<const void>{nullptr});
}
#endif
#endif
private:
PixelStorage _storage;
PixelFormat _format;
PixelType _type;
Math::Vector<Dimensions, Int> _size;
const char* _data;
Containers::ArrayView<const char> _data;
};
/** @brief One-dimensional image view */

7
src/Magnum/PixelStorage.h

@ -376,6 +376,13 @@ namespace Implementation {
return imageDataSizeFor(image, image.size());
}
#ifdef MAGNUM_BUILD_DEPRECATED
/* Uses default pixel storage */
template<std::size_t dimensions> std::size_t imageDataSizeFor(PixelFormat format, PixelType type, const Math::Vector<dimensions, Int>& size) {
return std::get<1>(PixelStorage{}.dataProperties(format, type, Vector3i::pad(size, 1))).product();
}
#endif
#ifndef MAGNUM_TARGET_GLES
/* Used in image query functions */
template<std::size_t dimensions, class T> std::size_t compressedImageDataSizeFor(const T& image, const Math::Vector<dimensions, Int>& size, std::size_t dataSize) {

18
src/Magnum/Test/ImageTest.cpp

@ -67,7 +67,7 @@ ImageTest::ImageTest() {
void ImageTest::construct() {
auto data = new char[3];
Image2D a{PixelStorage{}.setAlignment(1),
PixelFormat::Red, PixelType::UnsignedByte, {1, 3}, data};
PixelFormat::Red, PixelType::UnsignedByte, {1, 3}, Containers::Array<char>{data, 3}};
CORRADE_COMPARE(a.storage().alignment(), 1);
CORRADE_COMPARE(a.format(), PixelFormat::Red);
@ -106,7 +106,7 @@ void ImageTest::constructCopyCompressed() {
void ImageTest::constructMove() {
auto data = new char[3];
Image2D a{PixelStorage{}.setAlignment(1),
PixelFormat::Red, PixelType::UnsignedByte, {1, 3}, data};
PixelFormat::Red, PixelType::UnsignedByte, {1, 3}, Containers::Array<char>{data, 3}};
Image2D b(std::move(a));
CORRADE_COMPARE(a.data(), nullptr);
@ -119,7 +119,7 @@ void ImageTest::constructMove() {
CORRADE_COMPARE(b.data(), data);
auto data2 = new char[12*4*2];
Image2D c(PixelFormat::RGBA, PixelType::UnsignedShort, {2, 6}, data2);
Image2D c{PixelFormat::RGBA, PixelType::UnsignedShort, {2, 6}, Containers::Array<char>{data2, 12*4*2}};
c = std::move(b);
CORRADE_COMPARE(b.data(), data2);
@ -172,9 +172,9 @@ void ImageTest::constructMoveCompressed() {
void ImageTest::setData() {
auto data = new char[3];
Image2D a{PixelStorage{}.setAlignment(1),
PixelFormat::Red, PixelType::UnsignedByte, {1, 3}, data};
auto data2 = new char[2*4];
a.setData(PixelFormat::RGBA, PixelType::UnsignedShort, {2, 1}, data2);
PixelFormat::Red, PixelType::UnsignedByte, {1, 3}, Containers::Array<char>{data, 3}};
auto data2 = new char[2*2*4];
a.setData(PixelFormat::RGBA, PixelType::UnsignedShort, {2, 1}, Containers::Array<char>{data2, 2*2*4});
CORRADE_COMPARE(a.storage().alignment(), 4);
CORRADE_COMPARE(a.format(), PixelFormat::RGBA);
@ -205,7 +205,7 @@ void ImageTest::setDataCompressed() {
void ImageTest::toReference() {
auto data = new char[3];
const Image2D a{PixelStorage{}.setAlignment(1),
PixelFormat::Red, PixelType::UnsignedByte, {1, 3}, data};
PixelFormat::Red, PixelType::UnsignedByte, {1, 3}, Containers::Array<char>{data, 3}};
ImageView2D b = a;
CORRADE_COMPARE(b.storage().alignment(), 1);
@ -253,8 +253,8 @@ void ImageTest::toReferenceCommpressed() {
void ImageTest::release() {
char data[] = {'c', 'a', 'f', 'e'};
Image2D a(PixelFormat::Red, PixelType::UnsignedByte, {1, 4}, data);
const char* const pointer = a.release();
Image2D a(PixelFormat::Red, PixelType::UnsignedByte, {4, 1}, Containers::Array<char>{data, 4});
const char* const pointer = a.release().release();
CORRADE_COMPARE(pointer, data);
CORRADE_COMPARE(a.data(), nullptr);

2
src/Magnum/Test/ImageViewTest.cpp

@ -80,7 +80,7 @@ void ImageViewTest::setData() {
const char data[3]{};
ImageView2D a{PixelStorage{}.setAlignment(1),
PixelFormat::Red, PixelType::UnsignedByte, {1, 3}, data};
const char data2[8]{};
const char data2[3]{};
a.setData(data2);
CORRADE_COMPARE(a.storage().alignment(), 1);

4
src/Magnum/Trade/ImageData.cpp

@ -27,6 +27,10 @@
namespace Magnum { namespace Trade {
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const PixelStorage storage, const PixelFormat format, const PixelType type, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data): _compressed{false}, _storage{storage}, _format{format}, _type{type}, _size{size}, _data{std::move(data)} {
CORRADE_ASSERT(Implementation::imageDataSize(*this) <= _data.size(), "Trade::ImageDat::ImageData(): bad image data size, got" << _data.size() << "but expected at least" << Implementation::imageDataSize(*this), );
}
template<UnsignedInt dimensions> PixelStorage ImageData<dimensions>::storage() const {
CORRADE_ASSERT(!_compressed, "Trade::ImageData::storage(): the image is compressed", {});
return _storage;

33
src/Magnum/Trade/ImageData.h

@ -64,15 +64,22 @@ template<UnsignedInt dimensions> class ImageData {
* @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.
* The data are expected to be of proper size for given @p storage
* parameters.
*/
explicit ImageData(PixelStorage storage, PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, void* data): _compressed{false}, _storage{storage}, _format{format}, _type{type}, _size{size}, _data{reinterpret_cast<char*>(data), Implementation::imageDataSizeFor(*this, size)} {}
explicit ImageData(PixelStorage storage, PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data);
/** @overload
* Similar to the above, but uses default @ref PixelStorage parameters.
*/
explicit ImageData(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, void* data): ImageData{{}, format, type, size, data} {}
explicit ImageData(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data): ImageData{{}, format, type, size, std::move(data)} {}
#ifdef MAGNUM_BUILD_DEPRECATED
/** @copybrief ImageData(PixelFormat, PixelType, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&)
* @deprecated Use @ref ImageData(PixelFormat, PixelType, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&) instead.
*/
explicit CORRADE_DEPRECATED("use ImageData(PixelFormat, PixelType, const VectorTypeFor&, Containers::Array&&) instead") ImageData(PixelFormat format, PixelType type, const VectorTypeFor<dimensions, Int>& size, void* data): ImageData{format, type, size, Containers::Array<char>{reinterpret_cast<char*>(data), Magnum::Implementation::imageDataSizeFor(format, type, size)}} {}
#endif
#ifndef MAGNUM_TARGET_GLES
/**
@ -222,17 +229,17 @@ template<UnsignedInt dimensions> class ImageData {
are not setting any block size pixel storage properties to avoid
needless state changes -- thus the calculation can't be done */
/** @brief Raw data */
/**
* @brief Raw data
*
* @see @ref release()
*/
Containers::ArrayView<char> data() { return _data; }
/** @overload */
Containers::ArrayView<const char> data() const { return _data; }
/**
* @brief Pointer to raw data
*
* @see @ref release()
*/
/** @overload */
template<class T> T* data() {
return reinterpret_cast<T*>(_data.data());
}
@ -245,8 +252,8 @@ template<UnsignedInt dimensions> class ImageData {
/**
* @brief Release data storage
*
* Releases the ownership of the data pointer and resets internal state
* to default. Deleting the returned array is then user responsibility.
* Releases the ownership of the data array and resets internal state
* to default.
* @see @ref data()
*/
Containers::Array<char> release();
@ -305,7 +312,6 @@ template<UnsignedInt dimensions> inline ImageData<dimensions>::ImageData(ImageDa
}
other._size = {};
other._data = nullptr;
}
template<UnsignedInt dimensions> inline ImageData<dimensions>& ImageData<dimensions>::operator=(ImageData<dimensions>&& other) noexcept {
@ -330,7 +336,6 @@ template<UnsignedInt dimensions> inline ImageData<dimensions>& ImageData<dimensi
template<UnsignedInt dimensions> inline Containers::Array<char> ImageData<dimensions>::release() {
Containers::Array<char> data{std::move(_data)};
_size = {};
_data = nullptr;
return data;
}

2
src/Magnum/Trade/Test/AbstractImageConverterTest.cpp

@ -62,7 +62,7 @@ void AbstractImageConverterTest::exportToFile() {
/* doExportToFile() should call doExportToData() */
DataExporter exporter;
ImageView2D image(PixelFormat::RGBA, PixelType::UnsignedByte, {0xfe, 0xed}, nullptr);
ImageView2D image(PixelFormat::RGBA, PixelType::UnsignedByte, {0xfe, 0xed}, {nullptr, 0xfe*0xed*4});
CORRADE_VERIFY(exporter.exportToFile(image, Utility::Directory::join(TRADE_TEST_OUTPUT_DIR, "image.out")));
CORRADE_COMPARE_AS(Utility::Directory::join(TRADE_TEST_OUTPUT_DIR, "image.out"),
"\xFE\xED", TestSuite::Compare::FileToString);

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

@ -62,7 +62,7 @@ ImageDataTest::ImageDataTest() {
void ImageDataTest::construct() {
auto data = new char[3];
Trade::ImageData2D a{PixelStorage{}.setAlignment(1),
PixelFormat::Red, PixelType::UnsignedByte, {1, 3}, data};
PixelFormat::Red, PixelType::UnsignedByte, {1, 3}, Containers::Array<char>{data, 3}};
CORRADE_VERIFY(!a.isCompressed());
CORRADE_COMPARE(a.storage().alignment(), 1);
@ -98,7 +98,7 @@ void ImageDataTest::constructCopy() {
void ImageDataTest::constructMove() {
auto data = new char[3];
Trade::ImageData2D a{PixelStorage{}.setAlignment(1),
PixelFormat::Red, PixelType::UnsignedByte, {1, 3}, data};
PixelFormat::Red, PixelType::UnsignedByte, {1, 3}, Containers::Array<char>{data, 3}};
Trade::ImageData2D b(std::move(a));
CORRADE_COMPARE(a.data(), nullptr);
@ -111,8 +111,8 @@ void ImageDataTest::constructMove() {
CORRADE_COMPARE(b.size(), Vector2i(1, 3));
CORRADE_COMPARE(b.data(), data);
auto data2 = new char[3];
Trade::ImageData2D c(PixelFormat::RGBA, PixelType::UnsignedShort, {2, 6}, data2);
auto data2 = new char[2*2*6*4];
Trade::ImageData2D c{PixelFormat::RGBA, PixelType::UnsignedShort, {2, 6}, Containers::Array<char>{data2, 2*2*6*4}};
c = std::move(b);
CORRADE_COMPARE(b.data(), data2);
@ -166,13 +166,13 @@ void ImageDataTest::constructMoveCompressed() {
}
void ImageDataTest::toReference() {
auto data = new char[3];
const Trade::ImageData2D a(PixelFormat::Red, PixelType::UnsignedByte, {1, 3}, data);
auto data = new char[4];
const Trade::ImageData2D a{PixelFormat::Red, PixelType::UnsignedByte, {4, 1}, Containers::Array<char>{data, 4}};
ImageView2D b = a;
CORRADE_COMPARE(b.format(), PixelFormat::Red);
CORRADE_COMPARE(b.type(), PixelType::UnsignedByte);
CORRADE_COMPARE(b.size(), Vector2i(1, 3));
CORRADE_COMPARE(b.size(), Vector2i(4, 1));
CORRADE_COMPARE(b.data(), data);
CORRADE_VERIFY((std::is_convertible<const Trade::ImageData2D&, ImageView2D>::value));
@ -207,7 +207,7 @@ void ImageDataTest::toReferenceCompressed() {
void ImageDataTest::release() {
char data[] = {'b', 'e', 'e', 'r'};
Trade::ImageData2D a(PixelFormat::Red, PixelType::UnsignedByte, {1, 4}, data);
Trade::ImageData2D a{PixelFormat::Red, PixelType::UnsignedByte, {4, 1}, Containers::Array<char>{data, 4}};
const char* const pointer = a.release().release();
CORRADE_COMPARE(pointer, data);

2
src/MagnumPlugins/TgaImageConverter/TgaImageConverter.cpp

@ -91,7 +91,7 @@ Containers::Array<char> TgaImageConverter::doExportToData(const ImageView2D& ima
header->height = UnsignedShort(Utility::Endianness::littleEndian(image.size().y()));
/* Fill data */
std::copy(image.data(), image.data()+pixelSize*image.size().product(), data.begin()+sizeof(TgaHeader));
std::copy(image.data().data(), image.data()+pixelSize*image.size().product(), data.begin()+sizeof(TgaHeader));
if(image.format() == PixelFormat::RGB) {
auto pixels = reinterpret_cast<Math::Vector3<UnsignedByte>*>(data.begin()+sizeof(TgaHeader));

8
src/MagnumPlugins/TgaImporter/TgaImporter.cpp

@ -133,22 +133,22 @@ std::optional<ImageData2D> TgaImporter::doImage2D(UnsignedInt) {
}
const std::size_t dataSize = header.width*header.height*header.bpp/8;
char* const data = new char[dataSize];
Containers::Array<char> data{dataSize};
in->read(data, dataSize);
Vector2i size(header.width, header.height);
if(format == PixelFormat::RGB) {
auto pixels = reinterpret_cast<Math::Vector3<UnsignedByte>*>(data);
auto pixels = reinterpret_cast<Math::Vector3<UnsignedByte>*>(data.data());
std::transform(pixels, pixels + size.product(), pixels,
[](Math::Vector3<UnsignedByte> pixel) { return Math::swizzle<'b', 'g', 'r'>(pixel); });
} else if(format == PixelFormat::RGBA) {
auto pixels = reinterpret_cast<Math::Vector4<UnsignedByte>*>(data);
auto pixels = reinterpret_cast<Math::Vector4<UnsignedByte>*>(data.data());
std::transform(pixels, pixels + size.product(), pixels,
[](Math::Vector4<UnsignedByte> pixel) { return Math::swizzle<'b', 'g', 'r', 'a'>(pixel); });
}
return ImageData2D(format, PixelType::UnsignedByte, size, data);
return ImageData2D{format, PixelType::UnsignedByte, size, std::move(data)};
}
}}

Loading…
Cancel
Save