Browse Source

Trade: mutable access in ImageData consistent with Animation/MeshData.

pull/371/head
Vladimír Vondruš 7 years ago
parent
commit
6ed0df26c6
  1. 10
      doc/changelog.dox
  2. 14
      doc/snippets/MagnumTrade.cpp
  3. 4
      src/Magnum/Image.cpp
  4. 2
      src/Magnum/ImageView.cpp
  5. 4
      src/Magnum/Implementation/ImageProperties.h
  6. 7
      src/Magnum/Trade/Data.h
  7. 88
      src/Magnum/Trade/ImageData.cpp
  8. 277
      src/Magnum/Trade/ImageData.h
  9. 376
      src/Magnum/Trade/Test/ImageDataTest.cpp

10
doc/changelog.dox

@ -171,8 +171,10 @@ See also:
- The @ref Trade::AnimationData class received support for mutable data - The @ref Trade::AnimationData class received support for mutable data
access with new constructors and the access with new constructors and the
@ref Trade::AnimationData::mutableData() "mutableData()" and @ref Trade::AnimationData::mutableData() "mutableData()" and
@ref Trade::AnimationData::mutableTrack() "mutableTrack()" accessors. See @ref Trade::AnimationData::mutableTrack() "mutableTrack()" accessors.
@ref Trade-AnimationData-usage-mutable for more information. Equivalent APIs are exposed in both @ref Trade::ImageData and
@ref Trade::MeshData as well. See @ref Trade-AnimationData-usage-mutable
for more information.
@subsubsection changelog-latest-new-vk Vk library @subsubsection changelog-latest-new-vk Vk library
@ -405,6 +407,10 @@ See also:
- The @ref Magnum/Math/FunctionsBatch.h header is no longer included from - The @ref Magnum/Math/FunctionsBatch.h header is no longer included from
@ref Magnum/Math/Functions.h for backwards compatibility in order to speed @ref Magnum/Math/Functions.h for backwards compatibility in order to speed
up compile times. up compile times.
- Non-const @ref Trade::ImageData::data() and @ref Trade::ImageData::pixels()
were renamed to @ref Trade::ImageData::mutableData() and
@ref Trade::ImageData::mutablePixels() to follow the new
@ref Trade::MeshData API and similar changes in @ref Trade::AnimationData.
- @ref Platform::GlfwApplication::setMinWindowSize() / - @ref Platform::GlfwApplication::setMinWindowSize() /
@ref Platform::GlfwApplication::setMaxWindowSize() and equivalent APIs in @ref Platform::GlfwApplication::setMaxWindowSize() and equivalent APIs in
@ref Platform::Sdl2Application now premultiply the value with @ref Platform::Sdl2Application now premultiply the value with

14
doc/snippets/MagnumTrade.cpp

@ -223,6 +223,20 @@ else
} }
#endif #endif
{
Trade::ImageData2D data{PixelFormat::RGB8Unorm, {}, nullptr};
/* [ImageData-usage-mutable] */
if(data.isCompressed() ||
data.format() != PixelFormat::RGB8Unorm ||
!(data.dataFlags() & Trade::DataFlag::Mutable))
Fatal{} << ":(";
for(auto&& row: data.mutablePixels<Color3ub>())
for(Color3ub& pixel: row)
pixel = Math::gather<'b', 'g', 'r'>(pixel);
/* [ImageData-usage-mutable] */
}
#ifdef MAGNUM_TARGET_GL #ifdef MAGNUM_TARGET_GL
{ {
Trade::MeshData data{MeshPrimitive::Points, 0}; Trade::MeshData data{MeshPrimitive::Points, 0};

4
src/Magnum/Image.cpp

@ -73,11 +73,11 @@ template<UnsignedInt dimensions> std::pair<VectorTypeFor<dimensions, std::size_t
} }
template<UnsignedInt dimensions> Containers::StridedArrayView<dimensions + 1, char> Image<dimensions>::pixels() { template<UnsignedInt dimensions> Containers::StridedArrayView<dimensions + 1, char> Image<dimensions>::pixels() {
return Implementation::imagePixelView<dimensions, char>(*this); return Implementation::imagePixelView<dimensions, char>(*this, data());
} }
template<UnsignedInt dimensions> Containers::StridedArrayView<dimensions + 1, const char> Image<dimensions>::pixels() const { template<UnsignedInt dimensions> Containers::StridedArrayView<dimensions + 1, const char> Image<dimensions>::pixels() const {
return Implementation::imagePixelView<dimensions, const char>(*this); return Implementation::imagePixelView<dimensions, const char>(*this, data());
} }
template<UnsignedInt dimensions> Containers::Array<char> Image<dimensions>::release() { template<UnsignedInt dimensions> Containers::Array<char> Image<dimensions>::release() {

2
src/Magnum/ImageView.cpp

@ -63,7 +63,7 @@ template<UnsignedInt dimensions, class T> void ImageView<dimensions, T>::setData
template<UnsignedInt dimensions, class T> auto ImageView<dimensions, T>::pixels() const -> Containers::StridedArrayView<dimensions + 1, Type> { template<UnsignedInt dimensions, class T> auto ImageView<dimensions, T>::pixels() const -> Containers::StridedArrayView<dimensions + 1, Type> {
if(!_data && !_data.size()) return {}; if(!_data && !_data.size()) return {};
return Implementation::imagePixelView<dimensions, Type>(*this); return Implementation::imagePixelView<dimensions, Type>(*this, data());
} }
template<UnsignedInt dimensions, class T> CompressedImageView<dimensions, T>::CompressedImageView(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const Containers::ArrayView<ErasedType> data) noexcept: _storage{storage}, _format{format}, _size{size}, _data{reinterpret_cast<Type*>(data.data()), data.size()} {} template<UnsignedInt dimensions, class T> CompressedImageView<dimensions, T>::CompressedImageView(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const Containers::ArrayView<ErasedType> data) noexcept: _storage{storage}, _format{format}, _size{size}, _data{reinterpret_cast<Type*>(data.data()), data.size()} {}

4
src/Magnum/Implementation/ImageProperties.h

@ -45,7 +45,7 @@ template<std::size_t dimensions, class T> std::pair<Math::Vector<dimensions, std
return std::make_pair(Math::Vector<dimensions, std::size_t>::pad(dataProperties.first), Math::Vector<dimensions, std::size_t>::pad(dataProperties.second)); return std::make_pair(Math::Vector<dimensions, std::size_t>::pad(dataProperties.first), Math::Vector<dimensions, std::size_t>::pad(dataProperties.second));
} }
template<UnsignedInt dimensions, class T, class Image> Containers::StridedArrayView<dimensions + 1, T> imagePixelView(Image& image) { template<UnsignedInt dimensions, class T, class Image, class Data> Containers::StridedArrayView<dimensions + 1, T> imagePixelView(Image& image, const Data data) {
const std::pair<VectorTypeFor<dimensions, std::size_t>, VectorTypeFor<dimensions, std::size_t>> properties = image.dataProperties(); const std::pair<VectorTypeFor<dimensions, std::size_t>, VectorTypeFor<dimensions, std::size_t>> properties = image.dataProperties();
/* Size in the last dimension is byte size of the pixel, the remaining /* Size in the last dimension is byte size of the pixel, the remaining
@ -70,7 +70,7 @@ template<UnsignedInt dimensions, class T, class Image> Containers::StridedArrayV
static_assert(sizeof(decltype(image.data().front())) == 1, static_assert(sizeof(decltype(image.data().front())) == 1,
"pointer arithmetic expects image data type to have 1 byte"); "pointer arithmetic expects image data type to have 1 byte");
return {image.data().suffix(properties.first[dimensions - 1]), image.data() + properties.first.sum(), size, stride}; return {data.suffix(properties.first[dimensions - 1]), data + properties.first.sum(), size, stride};
} }
}} }}

7
src/Magnum/Trade/Data.h

@ -42,7 +42,8 @@ namespace Magnum { namespace Trade {
@m_since_latest @m_since_latest
@see @ref DataFlags, @ref AnimationData::dataFlags(), @see @ref DataFlags, @ref AnimationData::dataFlags(),
@ref MeshData::indexDataFlags(), @ref MeshData::vertexDataFlags() @ref ImageData::dataFlags(), @ref MeshData::indexDataFlags(),
@ref MeshData::vertexDataFlags()
*/ */
enum class DataFlag: UnsignedByte { enum class DataFlag: UnsignedByte {
/** /**
@ -71,8 +72,8 @@ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, DataFlag value);
@brief Data flags @brief Data flags
@m_since_latest @m_since_latest
@see @ref AnimationData::dataFlags(), @ref MeshData::indexDataFlags(), @see @ref AnimationData::dataFlags(), @ref ImageData::dataFlags(),
@ref MeshData::vertexDataFlags() @ref MeshData::indexDataFlags(), @ref MeshData::vertexDataFlags()
*/ */
typedef Containers::EnumSet<DataFlag> DataFlags; typedef Containers::EnumSet<DataFlag> DataFlags;

88
src/Magnum/Trade/ImageData.cpp

@ -35,17 +35,59 @@ namespace Magnum { namespace Trade {
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const PixelStorage storage, const PixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* const importerState) noexcept: ImageData{storage, format, {}, Magnum::pixelSize(format), size, std::move(data), importerState} {} template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const PixelStorage storage, const PixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* const importerState) noexcept: ImageData{storage, format, {}, Magnum::pixelSize(format), size, std::move(data), importerState} {}
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const PixelStorage storage, const PixelFormat format, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const void* const importerState) noexcept: ImageData{storage, format, size, Containers::Array<char>{const_cast<char*>(static_cast<const char*>(data.data())), data.size(), Implementation::nonOwnedArrayDeleter}, importerState} {
CORRADE_ASSERT(!(dataFlags & DataFlag::Owned),
"Trade::ImageData: can't construct a non-owned instance with" << dataFlags, );
_dataFlags = dataFlags;
}
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const PixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* const importerState) noexcept: ImageData{{}, format, size, std::move(data), importerState} {}
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const PixelFormat format, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const void* const importerState) noexcept: ImageData{format, size, Containers::Array<char>{const_cast<char*>(static_cast<const char*>(data.data())), data.size(), Implementation::nonOwnedArrayDeleter}, importerState} {
CORRADE_ASSERT(!(dataFlags & DataFlag::Owned),
"Trade::ImageData: can't construct a non-owned instance with" << dataFlags, );
_dataFlags = dataFlags;
}
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const PixelStorage storage, const UnsignedInt format, const UnsignedInt formatExtra, const UnsignedInt pixelSize, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* const importerState) noexcept: ImageData{storage, pixelFormatWrap(format), formatExtra, pixelSize, size, std::move(data), importerState} {} template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const PixelStorage storage, const UnsignedInt format, const UnsignedInt formatExtra, const UnsignedInt pixelSize, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* const importerState) noexcept: ImageData{storage, pixelFormatWrap(format), formatExtra, pixelSize, size, std::move(data), importerState} {}
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const PixelStorage storage, const PixelFormat format, const UnsignedInt formatExtra, const UnsignedInt pixelSize, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* const importerState) noexcept: _compressed{false}, _storage{storage}, _format{format}, _formatExtra{formatExtra}, _pixelSize{pixelSize}, _size{size}, _data{std::move(data)}, _importerState{importerState} { template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const PixelStorage storage, const PixelFormat format, const UnsignedInt formatExtra, const UnsignedInt pixelSize, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* const importerState) noexcept: _dataFlags{DataFlag::Owned|DataFlag::Mutable}, _compressed{false}, _storage{storage}, _format{format}, _formatExtra{formatExtra}, _pixelSize{pixelSize}, _size{size}, _data{std::move(data)}, _importerState{importerState} {
CORRADE_ASSERT(Magnum::Implementation::imageDataSize(*this) <= _data.size(), "Trade::ImageData: data too small, got" << _data.size() << "but expected at least" << Magnum::Implementation::imageDataSize(*this) << "bytes", ); CORRADE_ASSERT(Magnum::Implementation::imageDataSize(*this) <= _data.size(), "Trade::ImageData: data too small, got" << _data.size() << "but expected at least" << Magnum::Implementation::imageDataSize(*this) << "bytes", );
} }
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* const importerState) noexcept: _compressed{true}, _compressedStorage{storage}, _compressedFormat{format}, _size{size}, _data{std::move(data)}, _importerState{importerState} {} template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const PixelStorage storage, const UnsignedInt format, const UnsignedInt formatExtra, const UnsignedInt pixelSize, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const void* const importerState) noexcept: ImageData{storage, pixelFormatWrap(format), formatExtra, pixelSize, size, dataFlags, data, importerState} {}
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const PixelStorage storage, const PixelFormat format, const UnsignedInt formatExtra, const UnsignedInt pixelSize, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, Containers::ArrayView<const void> data, const void* const importerState) noexcept: ImageData{storage, format, formatExtra, pixelSize, size, Containers::Array<char>{const_cast<char*>(static_cast<const char*>(data.data())), data.size(), Implementation::nonOwnedArrayDeleter}, importerState} {
CORRADE_ASSERT(!(dataFlags & DataFlag::Owned),
"Trade::ImageData: can't construct a non-owned instance with" << dataFlags, );
_dataFlags = dataFlags;
}
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* const importerState) noexcept: _dataFlags{DataFlag::Owned|DataFlag::Mutable}, _compressed{true}, _compressedStorage{storage}, _compressedFormat{format}, _size{size}, _data{std::move(data)}, _importerState{importerState} {}
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const void* const importerState) noexcept: ImageData{storage, format, size, Containers::Array<char>{const_cast<char*>(static_cast<const char*>(data.data())), data.size(), Implementation::nonOwnedArrayDeleter}, importerState} {
CORRADE_ASSERT(!(dataFlags & DataFlag::Owned),
"Trade::ImageData: can't construct a non-owned instance with" << dataFlags, );
_dataFlags = dataFlags;
}
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* const importerState) noexcept: ImageData{{}, format, size, std::move(data), importerState} {}
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const void* const importerState) noexcept: ImageData{format, size, Containers::Array<char>{const_cast<char*>(static_cast<const char*>(data.data())), data.size(), Implementation::nonOwnedArrayDeleter}, importerState} {
CORRADE_ASSERT(!(dataFlags & DataFlag::Owned),
"Trade::ImageData: can't construct a non-owned instance with" << dataFlags, );
_dataFlags = dataFlags;
}
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const UnsignedInt format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* const importerState) noexcept: ImageData{storage, compressedPixelFormatWrap(format), size, std::move(data), importerState} {} template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const UnsignedInt format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* const importerState) noexcept: ImageData{storage, compressedPixelFormatWrap(format), size, std::move(data), importerState} {}
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(ImageData<dimensions>&& other) noexcept: _compressed{std::move(other._compressed)}, _size{std::move(other._size)}, _data{std::move(other._data)}, _importerState{std::move(other._importerState)} { template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const UnsignedInt format, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const void* const importerState) noexcept: ImageData{storage, format, size, Containers::Array<char>{const_cast<char*>(static_cast<const char*>(data.data())), data.size(), Implementation::nonOwnedArrayDeleter}, importerState} {
CORRADE_ASSERT(!(dataFlags & DataFlag::Owned),
"Trade::ImageData: can't construct a non-owned instance with" << dataFlags, );
_dataFlags = dataFlags;
}
template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(ImageData<dimensions>&& other) noexcept: _dataFlags{other._dataFlags}, _compressed{std::move(other._compressed)}, _size{std::move(other._size)}, _data{std::move(other._data)}, _importerState{std::move(other._importerState)} {
if(_compressed) { if(_compressed) {
new(&_compressedStorage) CompressedPixelStorage{std::move(other._compressedStorage)}; new(&_compressedStorage) CompressedPixelStorage{std::move(other._compressedStorage)};
_compressedFormat = std::move(other._compressedFormat); _compressedFormat = std::move(other._compressedFormat);
@ -66,6 +108,7 @@ template<UnsignedInt dimensions> ImageData<dimensions>::ImageData(ImageData<dime
template<UnsignedInt dimensions> ImageData<dimensions>& ImageData<dimensions>::operator=(ImageData<dimensions>&& other) noexcept { template<UnsignedInt dimensions> ImageData<dimensions>& ImageData<dimensions>::operator=(ImageData<dimensions>&& other) noexcept {
using std::swap; using std::swap;
swap(_dataFlags, other._dataFlags);
swap(_compressed, other._compressed); swap(_compressed, other._compressed);
if(_compressed) { if(_compressed) {
swap(_compressedStorage, other._compressedStorage); swap(_compressedStorage, other._compressedStorage);
@ -115,22 +158,25 @@ template<UnsignedInt dimensions> UnsignedInt ImageData<dimensions>::pixelSize()
template<UnsignedInt dimensions> std::pair<VectorTypeFor<dimensions, std::size_t>, VectorTypeFor<dimensions, std::size_t>> ImageData<dimensions>::dataProperties() const { template<UnsignedInt dimensions> std::pair<VectorTypeFor<dimensions, std::size_t>, VectorTypeFor<dimensions, std::size_t>> ImageData<dimensions>::dataProperties() const {
CORRADE_ASSERT(!_compressed, "Trade::ImageData::dataProperties(): the image is compressed", {}); CORRADE_ASSERT(!_compressed, "Trade::ImageData::dataProperties(): the image is compressed", {});
return Implementation::imageDataProperties<dimensions>(*this); return Magnum::Implementation::imageDataProperties<dimensions>(*this);
} }
template<UnsignedInt dimensions> Containers::StridedArrayView<dimensions + 1, char> ImageData<dimensions>::pixels() { template<UnsignedInt dimensions> Containers::ArrayView<char> ImageData<dimensions>::mutableData() & {
CORRADE_ASSERT(!_compressed, "Trade::ImageData::pixels(): the image is compressed", {}); CORRADE_ASSERT(_dataFlags & DataFlag::Mutable,
return Implementation::imagePixelView<dimensions, char>(*this); "Trade::ImageData::mutableData(): the image is not mutable", {});
return _data;
} }
template<UnsignedInt dimensions> Containers::StridedArrayView<dimensions + 1, const char> ImageData<dimensions>::pixels() const { template<UnsignedInt dimensions> Containers::StridedArrayView<dimensions + 1, const char> ImageData<dimensions>::pixels() const {
CORRADE_ASSERT(!_compressed, "Trade::ImageData::pixels(): the image is compressed", {}); CORRADE_ASSERT(!_compressed, "Trade::ImageData::pixels(): the image is compressed", {});
return Implementation::imagePixelView<dimensions, const char>(*this); return Magnum::Implementation::imagePixelView<dimensions, const char>(*this, data());
} }
template<UnsignedInt dimensions> ImageData<dimensions>::operator BasicMutableImageView<dimensions>() { template<UnsignedInt dimensions> Containers::StridedArrayView<dimensions + 1, char> ImageData<dimensions>::mutablePixels() {
CORRADE_ASSERT(!_compressed, "Trade::ImageData: the image is compressed", (BasicMutableImageView<dimensions>{_storage, _format, _formatExtra, _pixelSize, _size})); CORRADE_ASSERT(_dataFlags & DataFlag::Mutable,
return BasicMutableImageView<dimensions>{_storage, _format, _formatExtra, _pixelSize, _size, _data}; "Trade::ImageData::mutablePixels(): the image is not mutable", {});
CORRADE_ASSERT(!_compressed, "Trade::ImageData::mutablePixels(): the image is compressed", {});
return Magnum::Implementation::imagePixelView<dimensions, char>(*this, mutableData());
} }
template<UnsignedInt dimensions> ImageData<dimensions>::operator BasicImageView<dimensions>() const { template<UnsignedInt dimensions> ImageData<dimensions>::operator BasicImageView<dimensions>() const {
@ -138,11 +184,11 @@ template<UnsignedInt dimensions> ImageData<dimensions>::operator BasicImageView<
return BasicImageView<dimensions>{_storage, _format, _formatExtra, _pixelSize, _size, _data}; return BasicImageView<dimensions>{_storage, _format, _formatExtra, _pixelSize, _size, _data};
} }
template<UnsignedInt dimensions> ImageData<dimensions>::operator BasicMutableCompressedImageView<dimensions>() { template<UnsignedInt dimensions> ImageData<dimensions>::operator BasicMutableImageView<dimensions>() {
CORRADE_ASSERT(_compressed, "Trade::ImageData: the image is not compressed", (BasicMutableCompressedImageView<dimensions>{_compressedStorage, _compressedFormat, _size})); CORRADE_ASSERT(_dataFlags & DataFlag::Mutable,
return BasicMutableCompressedImageView<dimensions>{ "Trade::ImageData: the image is not mutable", (BasicMutableImageView<dimensions>{_storage, _format, _formatExtra, _pixelSize, _size}));
_compressedStorage, CORRADE_ASSERT(!_compressed, "Trade::ImageData: the image is compressed", (BasicMutableImageView<dimensions>{_storage, _format, _formatExtra, _pixelSize, _size}));
_compressedFormat, _size, _data}; return BasicMutableImageView<dimensions>{_storage, _format, _formatExtra, _pixelSize, _size, _data};
} }
template<UnsignedInt dimensions> ImageData<dimensions>::operator BasicCompressedImageView<dimensions>() const { template<UnsignedInt dimensions> ImageData<dimensions>::operator BasicCompressedImageView<dimensions>() const {
@ -152,6 +198,16 @@ template<UnsignedInt dimensions> ImageData<dimensions>::operator BasicCompressed
_compressedFormat, _size, _data}; _compressedFormat, _size, _data};
} }
template<UnsignedInt dimensions> ImageData<dimensions>::operator BasicMutableCompressedImageView<dimensions>() {
CORRADE_ASSERT(_dataFlags & DataFlag::Mutable,
"Trade::ImageData: the image is not mutable",
(BasicMutableCompressedImageView<dimensions>{_compressedStorage, _compressedFormat, _size}));
CORRADE_ASSERT(_compressed, "Trade::ImageData: the image is not compressed", (BasicMutableCompressedImageView<dimensions>{_compressedStorage, _compressedFormat, _size}));
return BasicMutableCompressedImageView<dimensions>{
_compressedStorage,
_compressedFormat, _size, _data};
}
template<UnsignedInt dimensions> Containers::Array<char> ImageData<dimensions>::release() { template<UnsignedInt dimensions> Containers::Array<char> ImageData<dimensions>::release() {
Containers::Array<char> data{std::move(_data)}; Containers::Array<char> data{std::move(_data)};
_size = {}; _size = {};

277
src/Magnum/Trade/ImageData.h

@ -33,6 +33,7 @@
#include "Magnum/DimensionTraits.h" #include "Magnum/DimensionTraits.h"
#include "Magnum/PixelStorage.h" #include "Magnum/PixelStorage.h"
#include "Magnum/Trade/Data.h"
#include "Magnum/Trade/Trade.h" #include "Magnum/Trade/Trade.h"
#include "Magnum/Trade/visibility.h" #include "Magnum/Trade/visibility.h"
@ -87,6 +88,18 @@ compressed properties through @ref compressedStorage() and
@snippet MagnumTrade.cpp ImageData-usage @snippet MagnumTrade.cpp ImageData-usage
@section Trade-ImageData-usage-mutable Mutable data access
The interfaces implicitly provide @cpp const @ce views on the contained
pixel data through the @ref data() and @ref pixels() accessors. This is done
because in general case the data can also refer to a memory-mapped file or
constant memory. In cases when it's desirable to modify the data in-place,
there's the @ref mutableData() and @ref mutablePixels() set of functions. To
use these, you need to check that the data are mutable using @ref dataFlags()
first. The following snippet flips the R and B channels of the imported image:
@snippet MagnumTrade.cpp ImageData-usage-mutable
@see @ref ImageData1D, @ref ImageData2D, @ref ImageData3D, @see @ref ImageData1D, @ref ImageData2D, @ref ImageData3D,
@ref Image-pixel-views @ref Image-pixel-views
*/ */
@ -97,7 +110,7 @@ template<UnsignedInt dimensions> class ImageData {
}; };
/** /**
* @brief Construct uncompressed image data * @brief Construct an uncompressed image data
* @param storage Storage of pixel data * @param storage Storage of pixel data
* @param format Format of pixel data * @param format Format of pixel data
* @param size Image size * @param size Image size
@ -106,11 +119,34 @@ template<UnsignedInt dimensions> class ImageData {
* *
* The @p data array is expected to be of proper size for given * The @p data array is expected to be of proper size for given
* parameters. * parameters.
*
* The @ref dataFlags() are implicitly set to a combination of
* @ref DataFlag::Owned and @ref DataFlag::Mutable. For non-owned data
* use the @ref ImageData(PixelStorage, PixelFormat, const VectorTypeFor<dimensions, Int>&, DataFlags, Containers::ArrayView<const void>, const void*)
* constructor instead.
*/ */
explicit ImageData(PixelStorage storage, PixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept; explicit ImageData(PixelStorage storage, PixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept;
/** /**
* @brief Construct uncompressed image data * @brief Construct a non-owned uncompressed image data
* @param storage Storage of pixel data
* @param format Format of pixel data
* @param size Image size
* @param dataFlags Data flags
* @param data View on image data
* @param importerState Importer-specific state
* @m_since_latest
*
* Compared to @ref ImageData(PixelStorage, PixelFormat, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, const void*)
* creates an instance that doesn't own the passed data. The
* @p dataFlags parameter can contain @ref DataFlag::Mutable to
* indicate the external data can be modified, and is expected to *not*
* have @ref DataFlag::Owned set.
*/
explicit ImageData(PixelStorage storage, PixelFormat format, const VectorTypeFor<dimensions, Int>& size, DataFlags dataFlags, Containers::ArrayView<const void> data, const void* importerState = nullptr) noexcept;
/**
* @brief Construct an uncompressed image data
* @param format Format of pixel data * @param format Format of pixel data
* @param size Image size * @param size Image size
* @param data Image data * @param data Image data
@ -119,10 +155,27 @@ template<UnsignedInt dimensions> class ImageData {
* Equivalent to calling @ref ImageData(PixelStorage, PixelFormat, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, const void*) * Equivalent to calling @ref ImageData(PixelStorage, PixelFormat, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, const void*)
* with default-constructed @ref PixelStorage. * with default-constructed @ref PixelStorage.
*/ */
explicit ImageData(PixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept: ImageData{{}, format, size, std::move(data), importerState} {} explicit ImageData(PixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept;
/**
* @brief Construct a non-owned uncompressed image data
* @param format Format of pixel data
* @param size Image size
* @param dataFlags Data flags
* @param data View on image data
* @param importerState Importer-specific state
* @m_since_latest
*
* Compared to @ref ImageData(PixelFormat, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, const void*)
* creates an instance that doesn't own the passed data. The
* @p dataFlags parameter can contain @ref DataFlag::Mutable to
* indicate the external data can be modified, and is expected to *not*
* have @ref DataFlag::Owned set.
*/
explicit ImageData(PixelFormat format, const VectorTypeFor<dimensions, Int>& size, DataFlags dataFlags, Containers::ArrayView<const void> data, const void* importerState = nullptr) noexcept;
/** /**
* @brief Construct uncompressed image data with implementation-specific pixel format * @brief Construct an uncompressed image data with implementation-specific pixel format
* @param storage Storage of pixel data * @param storage Storage of pixel data
* @param format Format of pixel data * @param format Format of pixel data
* @param formatExtra Additional pixel format specifier * @param formatExtra Additional pixel format specifier
@ -139,7 +192,10 @@ template<UnsignedInt dimensions> class ImageData {
* @ref Magnum::PixelFormat "PixelFormat". * @ref Magnum::PixelFormat "PixelFormat".
* *
* The @p data array is expected to be of proper size for given * The @p data array is expected to be of proper size for given
* parameters. * parameters. The @ref dataFlags() are implicitly set to a combination
* of @ref DataFlag::Owned and @ref DataFlag::Mutable. For non-owned
* data use the @ref ImageData(PixelStorage, UnsignedInt, UnsignedInt, UnsignedInt, const VectorTypeFor<dimensions, Int>&, DataFlags, Containers::ArrayView<const void>, const void*)
* constructor instead.
*/ */
explicit ImageData(PixelStorage storage, UnsignedInt format, UnsignedInt formatExtra, UnsignedInt pixelSize, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept; explicit ImageData(PixelStorage storage, UnsignedInt format, UnsignedInt formatExtra, UnsignedInt pixelSize, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept;
@ -151,7 +207,35 @@ template<UnsignedInt dimensions> class ImageData {
explicit ImageData(PixelStorage storage, PixelFormat format, UnsignedInt formatExtra, UnsignedInt pixelSize, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept; explicit ImageData(PixelStorage storage, PixelFormat format, UnsignedInt formatExtra, UnsignedInt pixelSize, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept;
/** /**
* @brief Construct uncompressed image data with implementation-specific pixel format * @brief Construct a non-owned uncompressed image data with implementation-specific pixel format
* @param storage Storage of pixel data
* @param format Format of pixel data
* @param formatExtra Additional pixel format specifier
* @param pixelSize Size of a pixel in given format
* @param size Image size
* @param dataFlags Data flags
* @param data View on image data
* @param importerState Importer-specific state
* @m_since_latest
*
* Compared to @ref ImageData(PixelStorage, UnsignedInt, UnsignedInt, UnsignedInt, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, const void*)
* creates an instance that doesn't own the passed data. The
* @p dataFlags parameter can contain @ref DataFlag::Mutable to
* indicate the external data can be modified, and is expected to *not*
* have @ref DataFlag::Owned set.
*/
explicit ImageData(PixelStorage storage, UnsignedInt format, UnsignedInt formatExtra, UnsignedInt pixelSize, const VectorTypeFor<dimensions, Int>& size, DataFlags dataFlags, Containers::ArrayView<const void> data, const void* importerState = nullptr) noexcept;
/** @overload
* @m_since_latest
*
* Equivalent to the above for @p format already wrapped with
* @ref pixelFormatWrap().
*/
explicit ImageData(PixelStorage storage, PixelFormat format, UnsignedInt formatExtra, UnsignedInt pixelSize, const VectorTypeFor<dimensions, Int>& size, DataFlags dataFlags, Containers::ArrayView<const void> data, const void* importerState = nullptr) noexcept;
/**
* @brief Construct an uncompressed image data with implementation-specific pixel format
* @param storage Storage of pixel data * @param storage Storage of pixel data
* @param format Format of pixel data * @param format Format of pixel data
* @param formatExtra Additional pixel format specifier * @param formatExtra Additional pixel format specifier
@ -166,7 +250,26 @@ template<UnsignedInt dimensions> class ImageData {
template<class T, class U> explicit ImageData(PixelStorage storage, T format, U formatExtra, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept; template<class T, class U> explicit ImageData(PixelStorage storage, T format, U formatExtra, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept;
/** /**
* @brief Construct uncompressed image data with implementation-specific pixel format * @brief Construct a non-owned uncompressed image data with implementation-specific pixel format
* @param storage Storage of pixel data
* @param format Format of pixel data
* @param formatExtra Additional pixel format specifier
* @param size Image size
* @param dataFlags Data flags
* @param data View on image data
* @param importerState Importer-specific state
* @m_since_latest
*
* Compared to @ref ImageData(PixelStorage, T, U, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, const void*)
* creates an instance that doesn't own the passed data. The
* @p dataFlags parameter can contain @ref DataFlag::Mutable to
* indicate the external data can be modified, and is expected to *not*
* have @ref DataFlag::Owned set.
*/
template<class T, class U> explicit ImageData(PixelStorage storage, T format, U formatExtra, const VectorTypeFor<dimensions, Int>& size, DataFlags dataFlags, Containers::ArrayView<const void> data, const void* importerState = nullptr) noexcept;
/**
* @brief Construct an uncompressed image data with implementation-specific pixel format
* @param storage Storage of pixel data * @param storage Storage of pixel data
* @param format Format of pixel data * @param format Format of pixel data
* @param size Image size * @param size Image size
@ -180,7 +283,25 @@ template<UnsignedInt dimensions> class ImageData {
template<class T> explicit ImageData(PixelStorage storage, T format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept; template<class T> explicit ImageData(PixelStorage storage, T format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept;
/** /**
* @brief Construct compressed image data * @brief Construct a non-owned uncompressed image data with implementation-specific pixel format
* @param storage Storage of pixel data
* @param format Format of pixel data
* @param size Image size
* @param dataFlags Data flags
* @param data view on image data
* @param importerState Importer-specific state
* @m_since_latest
*
* Compared to @ref ImageData(PixelStorage, T, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, const void*)
* creates an instance that doesn't own the passed data. The
* @p dataFlags parameter can contain @ref DataFlag::Mutable to
* indicate the external data can be modified, and is expected to *not*
* have @ref DataFlag::Owned set.
*/
template<class T> explicit ImageData(PixelStorage storage, T format, const VectorTypeFor<dimensions, Int>& size, DataFlags dataFlags, Containers::ArrayView<const void> data, const void* importerState = nullptr) noexcept;
/**
* @brief Construct a compressed image data
* @param storage Storage of compressed pixel data * @param storage Storage of compressed pixel data
* @param format Format of compressed pixel data * @param format Format of compressed pixel data
* @param size Image size * @param size Image size
@ -190,7 +311,25 @@ template<UnsignedInt dimensions> class ImageData {
explicit ImageData(CompressedPixelStorage storage, CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept; explicit ImageData(CompressedPixelStorage storage, CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept;
/** /**
* @brief Construct compressed image data * @brief Construct a non-owned compressed image data
* @param storage Storage of compressed pixel data
* @param format Format of compressed pixel data
* @param size Image size
* @param dataFlags Data flags
* @param data View on image data
* @param importerState Importer-specific state
* @m_since_latest
*
* Compared to @ref ImageData(CompressedPixelStorage, CompressedPixelFormat, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, const void*)
* creates an instance that doesn't own the passed data. The
* @p dataFlags parameter can contain @ref DataFlag::Mutable to
* indicate the external data can be modified, and is expected to *not*
* have @ref DataFlag::Owned set.
*/
explicit ImageData(CompressedPixelStorage storage, CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, DataFlags dataFlags, Containers::ArrayView<const void> data, const void* importerState = nullptr) noexcept;
/**
* @brief Construct a compressed image data
* @param format Format of compressed pixel data * @param format Format of compressed pixel data
* @param size Image size * @param size Image size
* @param data Image data * @param data Image data
@ -199,10 +338,27 @@ template<UnsignedInt dimensions> class ImageData {
* Equivalent to calling @ref ImageData(CompressedPixelStorage, CompressedPixelFormat, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, const void*) * Equivalent to calling @ref ImageData(CompressedPixelStorage, CompressedPixelFormat, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, const void*)
* with default-constructed @ref CompressedPixelStorage. * with default-constructed @ref CompressedPixelStorage.
*/ */
explicit ImageData(CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept: ImageData{{}, format, size, std::move(data), importerState} {} explicit ImageData(CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept;
/**
* @brief Construct a non-owned compressed image data
* @param format Format of compressed pixel data
* @param size Image size
* @param dataFlags Data flags
* @param data View on image data
* @param importerState Importer-specific state
* @m_since_latest
*
* Compared to @ref ImageData(CompressedPixelFormat, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, const void*)
* creates an instance that doesn't own the passed data. The
* @p dataFlags parameter can contain @ref DataFlag::Mutable to
* indicate the external data can be modified, and is expected to *not*
* have @ref DataFlag::Owned set.
*/
explicit ImageData(CompressedPixelFormat format, const VectorTypeFor<dimensions, Int>& size, DataFlags dataFlags, Containers::ArrayView<const void> data, const void* importerState = nullptr) noexcept;
/** /**
* @brief Construct compressed image data * @brief Construct a compressed image data
* @param storage Storage of compressed pixel data * @param storage Storage of compressed pixel data
* @param format Format of compressed pixel data * @param format Format of compressed pixel data
* @param size Image size * @param size Image size
@ -214,6 +370,24 @@ template<UnsignedInt dimensions> class ImageData {
*/ */
template<class T> explicit ImageData(CompressedPixelStorage storage, T format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept; template<class T> explicit ImageData(CompressedPixelStorage storage, T format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept;
/**
* @brief Construct a non-owned compressed image data
* @param storage Storage of compressed pixel data
* @param format Format of compressed pixel data
* @param size Image size
* @param dataFlags Data flags
* @param data View on image data
* @param importerState Importer-specific state
* @m_since_latest
*
* Compared to @ref ImageData(CompressedPixelStorage, T, const VectorTypeFor<dimensions, Int>&, Containers::Array<char>&&, const void*)
* creates an instance that doesn't own the passed data. The
* @p dataFlags parameter can contain @ref DataFlag::Mutable to
* indicate the external data can be modified, and is expected to *not*
* have @ref DataFlag::Owned set.
*/
template<class T> explicit ImageData(CompressedPixelStorage storage, T format, const VectorTypeFor<dimensions, Int>& size, DataFlags dataFlags, Containers::ArrayView<const void> data, const void* importerState = nullptr) noexcept;
/** /**
* @brief Construct from existing data with attached importer state * @brief Construct from existing data with attached importer state
* *
@ -237,6 +411,12 @@ template<UnsignedInt dimensions> class ImageData {
/** @brief Move assignment */ /** @brief Move assignment */
ImageData<dimensions>& operator=(ImageData<dimensions>&& other) noexcept; ImageData<dimensions>& operator=(ImageData<dimensions>&& other) noexcept;
/**
* @brief Data flags
* @m_since_latest
*/
DataFlags dataFlags() const { return _dataFlags; }
/** @brief Whether the image is compressed */ /** @brief Whether the image is compressed */
bool isCompressed() const { return _compressed; } bool isCompressed() const { return _compressed; }
@ -253,6 +433,9 @@ template<UnsignedInt dimensions> class ImageData {
/** /**
* @brief Conversion to a mutable view * @brief Conversion to a mutable view
* @m_since{2019,10} * @m_since{2019,10}
*
* The image is expected to be uncompressed and mutable.
* @see @ref isCompressed(), @ref dataFlags()
*/ */
/*implicit*/ operator BasicMutableImageView<dimensions>(); /*implicit*/ operator BasicMutableImageView<dimensions>();
@ -269,6 +452,9 @@ template<UnsignedInt dimensions> class ImageData {
/** /**
* @brief Conversion to a mutable compressed view * @brief Conversion to a mutable compressed view
* @m_since{2019,10} * @m_since{2019,10}
*
* The image is expected to be compressed and mutable.
* @see @ref isCompressed(), @ref dataFlags()
*/ */
/*implicit*/ operator BasicMutableCompressedImageView<dimensions>(); /*implicit*/ operator BasicMutableCompressedImageView<dimensions>();
@ -358,9 +544,6 @@ template<UnsignedInt dimensions> class ImageData {
* *
* @see @ref release(), @ref pixels() * @see @ref release(), @ref pixels()
*/ */
Containers::ArrayView<char> data() & { return _data; }
/** @overload */
Containers::ArrayView<const char> data() const & { return _data; } Containers::ArrayView<const char> data() const & { return _data; }
/** /**
@ -369,7 +552,9 @@ template<UnsignedInt dimensions> class ImageData {
* *
* Unlike @ref data(), which returns a view, this is equivalent to * Unlike @ref data(), which returns a view, this is equivalent to
* @ref release() to avoid a dangling view when the temporary instance * @ref release() to avoid a dangling view when the temporary instance
* goes out of scope. * goes out of scope. Note that the returned array has a custom no-op
* deleter when the data are not owned by the image, and while the
* returned array type is mutable, the actual memory might be not.
* @todoc stupid doxygen can't link to & overloads ffs * @todoc stupid doxygen can't link to & overloads ffs
*/ */
Containers::Array<char> data() && { return release(); } Containers::Array<char> data() && { return release(); }
@ -380,6 +565,16 @@ template<UnsignedInt dimensions> class ImageData {
*/ */
Containers::Array<char> data() const && = delete; Containers::Array<char> data() const && = delete;
/**
* @brief Mutable image data
* @m_since_latest
*
* Like @ref data(), but returns a non-const view. Expects that the
* image is mutable.
* @see @ref dataFlags()
*/
Containers::ArrayView<char> mutableData() &;
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
/** /**
* @brief Image data in a particular type * @brief Image data in a particular type
@ -414,8 +609,17 @@ template<UnsignedInt dimensions> class ImageData {
* @see @ref isCompressed(), * @see @ref isCompressed(),
* @ref Corrade::Containers::StridedArrayView::isContiguous() * @ref Corrade::Containers::StridedArrayView::isContiguous()
*/ */
Containers::StridedArrayView<dimensions + 1, char> pixels(); Containers::StridedArrayView<dimensions + 1, const char> pixels() const;
Containers::StridedArrayView<dimensions + 1, const char> pixels() const; /**< @overload */
/**
* @brief Mutable view on pixel data
* @m_since_latest
*
* Like @ref pixels() const, but returns a non-const view. Expects that
* the image is mutable.
* @see @ref dataFlags()
*/
Containers::StridedArrayView<dimensions + 1, char> mutablePixels();
/** /**
* @brief View on pixel data with a concrete pixel type * @brief View on pixel data with a concrete pixel type
@ -426,26 +630,35 @@ template<UnsignedInt dimensions> class ImageData {
* correct type for given @ref format() --- checking it on the library * correct type for given @ref format() --- checking it on the library
* side is not possible for the general case. * side is not possible for the general case.
*/ */
template<class T> Containers::StridedArrayView<dimensions, T> pixels() { template<class T> Containers::StridedArrayView<dimensions, const T> pixels() const {
/* Deliberately not adding a StridedArrayView include, it should /* Deliberately not adding a StridedArrayView include, it should
work without since this is a templated function and we declare work without since this is a templated function and we declare
arrayCast() above to satisfy two-phase lookup. */ arrayCast() above to satisfy two-phase lookup. */
return Containers::arrayCast<dimensions, T>(pixels()); return Containers::arrayCast<dimensions, const T>(pixels());
} }
/** /**
* @overload * @brief Mutable view on pixel data with a concrete pixel type
* @m_since{2019,10} * @m_since{2019,10}
*
* Like @ref pixels() const, but returns a non-const view. Expects that
* the image is mutable.
* @see @ref dataFlags()
*/ */
template<class T> Containers::StridedArrayView<dimensions, const T> pixels() const { template<class T> Containers::StridedArrayView<dimensions, T> mutablePixels() {
return Containers::arrayCast<dimensions, const T>(pixels()); /* Deliberately not adding a StridedArrayView include, it should
work without since this is a templated function */
return Containers::arrayCast<dimensions, T>(mutablePixels());
} }
/** /**
* @brief Release data storage * @brief Release data storage
* *
* Releases the ownership of the data array and resets internal state * Releases the ownership of the data array and resets internal state
* to default. * to default. The image then behaves like it's empty. Note that
* the returned array has a custom no-op deleter when the data are not
* owned by the image, and while the returned array type is mutable,
* the actual memory might be not.
* @see @ref data() * @see @ref data()
*/ */
Containers::Array<char> release(); Containers::Array<char> release();
@ -465,6 +678,9 @@ template<UnsignedInt dimensions> class ImageData {
explicit ImageData(CompressedPixelStorage storage, UnsignedInt format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept; explicit ImageData(CompressedPixelStorage storage, UnsignedInt format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* importerState = nullptr) noexcept;
explicit ImageData(CompressedPixelStorage storage, UnsignedInt format, const VectorTypeFor<dimensions, Int>& size, DataFlags dataFlags, Containers::ArrayView<const void> data, const void* importerState = nullptr) noexcept;
DataFlags _dataFlags;
bool _compressed; bool _compressed;
union { union {
PixelStorage _storage; PixelStorage _storage;
@ -495,16 +711,31 @@ template<UnsignedInt dimensions> template<class T, class U> ImageData<dimensions
"format types larger than 32bits are not supported"); "format types larger than 32bits are not supported");
} }
template<UnsignedInt dimensions> template<class T, class U> ImageData<dimensions>::ImageData(const PixelStorage storage, const T format, const U formatExtra, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const void* const importerState) noexcept: ImageData{storage, UnsignedInt(format), UnsignedInt(formatExtra), Magnum::Implementation::pixelSizeAdl(format, formatExtra), size, dataFlags, data, importerState} {
static_assert(sizeof(T) <= 4 && sizeof(U) <= 4,
"format types larger than 32bits are not supported");
}
template<UnsignedInt dimensions> template<class T> ImageData<dimensions>::ImageData(const PixelStorage storage, const T format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* const importerState) noexcept: ImageData{storage, UnsignedInt(format), {}, Magnum::Implementation::pixelSizeAdl(format), size, std::move(data), importerState} { template<UnsignedInt dimensions> template<class T> ImageData<dimensions>::ImageData(const PixelStorage storage, const T format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* const importerState) noexcept: ImageData{storage, UnsignedInt(format), {}, Magnum::Implementation::pixelSizeAdl(format), size, std::move(data), importerState} {
static_assert(sizeof(T) <= 4, static_assert(sizeof(T) <= 4,
"format types larger than 32bits are not supported"); "format types larger than 32bits are not supported");
} }
template<UnsignedInt dimensions> template<class T> ImageData<dimensions>::ImageData(const PixelStorage storage, const T format, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const void* const importerState) noexcept: ImageData{storage, UnsignedInt(format), {}, Magnum::Implementation::pixelSizeAdl(format), size, dataFlags, data, importerState} {
static_assert(sizeof(T) <= 4,
"format types larger than 32bits are not supported");
}
template<UnsignedInt dimensions> template<class T> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const T format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* const importerState) noexcept: ImageData{storage, UnsignedInt(format), size, std::move(data), importerState} { template<UnsignedInt dimensions> template<class T> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const T format, const VectorTypeFor<dimensions, Int>& size, Containers::Array<char>&& data, const void* const importerState) noexcept: ImageData{storage, UnsignedInt(format), size, std::move(data), importerState} {
static_assert(sizeof(T) <= 4, static_assert(sizeof(T) <= 4,
"format types larger than 32bits are not supported"); "format types larger than 32bits are not supported");
} }
template<UnsignedInt dimensions> template<class T> ImageData<dimensions>::ImageData(const CompressedPixelStorage storage, const T format, const VectorTypeFor<dimensions, Int>& size, const DataFlags dataFlags, const Containers::ArrayView<const void> data, const void* const importerState) noexcept: ImageData{storage, UnsignedInt(format), size, dataFlags, data, importerState} {
static_assert(sizeof(T) <= 4,
"format types larger than 32bits are not supported");
}
}} }}
#endif #endif

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

@ -47,6 +47,15 @@ struct ImageDataTest: TestSuite::Tester {
void constructCompressedGeneric(); void constructCompressedGeneric();
void constructCompressedImplementationSpecific(); void constructCompressedImplementationSpecific();
void constructGenericNotOwned();
void constructImplementationSpecificNotOwned();
void constructCompressedGenericNotOwned();
void constructCompressedImplementationSpecificNotOwned();
void constructGenericNotOwnedFlagOwned();
void constructImplementationSpecificNotOwnedFlagOwned();
void constructCompressedGenericNotOwnedFlagOwned();
void constructCompressedImplementationSpecificNotOwnedFlagOwned();
void constructInvalidSize(); void constructInvalidSize();
void constructCompressedInvalidSize(); void constructCompressedInvalidSize();
@ -67,6 +76,7 @@ struct ImageDataTest: TestSuite::Tester {
void data(); void data();
void dataRvalue(); void dataRvalue();
void mutableAccessNotAllowed();
void dataProperties(); void dataProperties();
@ -91,11 +101,30 @@ template<> struct MutabilityTraits<char> {
static const char* name() { return "MutableImageView"; } static const char* name() { return "MutableImageView"; }
}; };
struct {
const char* name;
DataFlags dataFlags;
} NotOwnedData[] {
{"", {}},
{"mutable", DataFlag::Mutable},
};
ImageDataTest::ImageDataTest() { ImageDataTest::ImageDataTest() {
addTests({&ImageDataTest::constructGeneric, addTests({&ImageDataTest::constructGeneric,
&ImageDataTest::constructImplementationSpecific, &ImageDataTest::constructImplementationSpecific,
&ImageDataTest::constructCompressedGeneric, &ImageDataTest::constructCompressedGeneric,
&ImageDataTest::constructCompressedImplementationSpecific, &ImageDataTest::constructCompressedImplementationSpecific});
addInstancedTests({&ImageDataTest::constructGenericNotOwned,
&ImageDataTest::constructImplementationSpecificNotOwned,
&ImageDataTest::constructCompressedGenericNotOwned,
&ImageDataTest::constructCompressedImplementationSpecificNotOwned},
Containers::arraySize(NotOwnedData));
addTests({&ImageDataTest::constructGenericNotOwnedFlagOwned,
&ImageDataTest::constructImplementationSpecificNotOwnedFlagOwned,
&ImageDataTest::constructCompressedGenericNotOwnedFlagOwned,
&ImageDataTest::constructCompressedImplementationSpecificNotOwnedFlagOwned,
&ImageDataTest::constructInvalidSize, &ImageDataTest::constructInvalidSize,
&ImageDataTest::constructCompressedInvalidSize, &ImageDataTest::constructCompressedInvalidSize,
@ -121,6 +150,7 @@ ImageDataTest::ImageDataTest() {
&ImageDataTest::data, &ImageDataTest::data,
&ImageDataTest::dataRvalue, &ImageDataTest::dataRvalue,
&ImageDataTest::mutableAccessNotAllowed,
&ImageDataTest::dataProperties, &ImageDataTest::dataProperties,
@ -167,14 +197,19 @@ void ImageDataTest::constructGeneric() {
int state; int state;
ImageData2D a{PixelFormat::RGBA8Unorm, {1, 3}, Containers::Array<char>{data, 4*4}, &state}; ImageData2D a{PixelFormat::RGBA8Unorm, {1, 3}, Containers::Array<char>{data, 4*4}, &state};
CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(!a.isCompressed()); CORRADE_VERIFY(!a.isCompressed());
CORRADE_COMPARE(a.storage().alignment(), 4); CORRADE_COMPARE(a.storage().alignment(), 4);
CORRADE_COMPARE(a.format(), PixelFormat::RGBA8Unorm); CORRADE_COMPARE(a.format(), PixelFormat::RGBA8Unorm);
CORRADE_COMPARE(a.formatExtra(), 0); CORRADE_COMPARE(a.formatExtra(), 0);
CORRADE_COMPARE(a.pixelSize(), 4); CORRADE_COMPARE(a.pixelSize(), 4);
CORRADE_COMPARE(a.size(), (Vector2i{1, 3})); CORRADE_COMPARE(a.size(), (Vector2i{1, 3}));
CORRADE_COMPARE(a.data(), data); CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 4*4); CORRADE_COMPARE(a.data().size(), 4*4);
CORRADE_COMPARE(static_cast<const void*>(&a.pixels<Color4ub>()[0][0]), data);
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 4*4);
CORRADE_COMPARE(static_cast<const void*>(&a.mutablePixels<Color4ub>()[0][0]), data);
CORRADE_COMPARE(a.importerState(), &state); CORRADE_COMPARE(a.importerState(), &state);
} { } {
auto data = new char[3*2]; auto data = new char[3*2];
@ -182,14 +217,19 @@ void ImageDataTest::constructGeneric() {
ImageData2D a{PixelStorage{}.setAlignment(1), ImageData2D a{PixelStorage{}.setAlignment(1),
PixelFormat::R16UI, {1, 3}, Containers::Array<char>{data, 3*2}, &state}; PixelFormat::R16UI, {1, 3}, Containers::Array<char>{data, 3*2}, &state};
CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(!a.isCompressed()); CORRADE_VERIFY(!a.isCompressed());
CORRADE_COMPARE(a.storage().alignment(), 1); CORRADE_COMPARE(a.storage().alignment(), 1);
CORRADE_COMPARE(a.format(), PixelFormat::R16UI); CORRADE_COMPARE(a.format(), PixelFormat::R16UI);
CORRADE_COMPARE(a.formatExtra(), 0); CORRADE_COMPARE(a.formatExtra(), 0);
CORRADE_COMPARE(a.pixelSize(), 2); CORRADE_COMPARE(a.pixelSize(), 2);
CORRADE_COMPARE(a.size(), (Vector2i{1, 3})); CORRADE_COMPARE(a.size(), (Vector2i{1, 3}));
CORRADE_COMPARE(a.data(), data); CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 3*2); CORRADE_COMPARE(a.data().size(), 3*2);
CORRADE_COMPARE(static_cast<const void*>(&a.pixels<UnsignedShort>()[0][0]), data);
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 3*2);
CORRADE_COMPARE(static_cast<const void*>(&a.mutablePixels<UnsignedShort>()[0][0]), data);
CORRADE_COMPARE(a.importerState(), &state); CORRADE_COMPARE(a.importerState(), &state);
} }
} }
@ -202,14 +242,19 @@ void ImageDataTest::constructImplementationSpecific() {
ImageData2D a{PixelStorage{}.setAlignment(1), ImageData2D a{PixelStorage{}.setAlignment(1),
Vk::PixelFormat::R32G32B32F, {1, 3}, Containers::Array<char>{data, 3*12}, &state}; Vk::PixelFormat::R32G32B32F, {1, 3}, Containers::Array<char>{data, 3*12}, &state};
CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(!a.isCompressed()); CORRADE_VERIFY(!a.isCompressed());
CORRADE_COMPARE(a.storage().alignment(), 1); CORRADE_COMPARE(a.storage().alignment(), 1);
CORRADE_COMPARE(a.format(), pixelFormatWrap(Vk::PixelFormat::R32G32B32F)); CORRADE_COMPARE(a.format(), pixelFormatWrap(Vk::PixelFormat::R32G32B32F));
CORRADE_COMPARE(a.formatExtra(), 0); CORRADE_COMPARE(a.formatExtra(), 0);
CORRADE_COMPARE(a.pixelSize(), 12); CORRADE_COMPARE(a.pixelSize(), 12);
CORRADE_COMPARE(a.size(), (Vector2i{1, 3})); CORRADE_COMPARE(a.size(), (Vector2i{1, 3}));
CORRADE_COMPARE(a.data(), data); CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 3*12); CORRADE_COMPARE(a.data().size(), 3*12);
CORRADE_COMPARE(static_cast<const void*>(&a.pixels<Color3>()[0][0]), data);
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 3*12);
CORRADE_COMPARE(static_cast<const void*>(&a.mutablePixels<Color3>()[0][0]), data);
CORRADE_COMPARE(a.importerState(), &state); CORRADE_COMPARE(a.importerState(), &state);
} }
@ -220,13 +265,18 @@ void ImageDataTest::constructImplementationSpecific() {
ImageData2D a{PixelStorage{}.setAlignment(1), ImageData2D a{PixelStorage{}.setAlignment(1),
GL::PixelFormat::RGB, GL::PixelType::UnsignedShort, {1, 3}, Containers::Array<char>{data, 3*6}, &state}; GL::PixelFormat::RGB, GL::PixelType::UnsignedShort, {1, 3}, Containers::Array<char>{data, 3*6}, &state};
CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(!a.isCompressed()); CORRADE_VERIFY(!a.isCompressed());
CORRADE_COMPARE(a.format(), pixelFormatWrap(GL::PixelFormat::RGB)); CORRADE_COMPARE(a.format(), pixelFormatWrap(GL::PixelFormat::RGB));
CORRADE_COMPARE(a.formatExtra(), UnsignedInt(GL::PixelType::UnsignedShort)); CORRADE_COMPARE(a.formatExtra(), UnsignedInt(GL::PixelType::UnsignedShort));
CORRADE_COMPARE(a.pixelSize(), 6); CORRADE_COMPARE(a.pixelSize(), 6);
CORRADE_COMPARE(a.size(), (Vector2i{1, 3})); CORRADE_COMPARE(a.size(), (Vector2i{1, 3}));
CORRADE_COMPARE(a.data(), data); CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 3*6); CORRADE_COMPARE(a.data().size(), 3*6);
CORRADE_COMPARE(static_cast<const void*>(&a.pixels<Math::Vector3<UnsignedShort>>()[0][0]), data);
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 3*6);
CORRADE_COMPARE(static_cast<const void*>(&a.mutablePixels<Math::Vector3<UnsignedShort>>()[0][0]), data);
CORRADE_COMPARE(a.importerState(), &state); CORRADE_COMPARE(a.importerState(), &state);
} }
@ -236,14 +286,19 @@ void ImageDataTest::constructImplementationSpecific() {
int state; int state;
ImageData2D a{PixelStorage{}.setAlignment(1), 666, 1337, 6, {1, 3}, Containers::Array<char>{data, 3*6}, &state}; ImageData2D a{PixelStorage{}.setAlignment(1), 666, 1337, 6, {1, 3}, Containers::Array<char>{data, 3*6}, &state};
CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(!a.isCompressed()); CORRADE_VERIFY(!a.isCompressed());
CORRADE_COMPARE(a.storage().alignment(), 1); CORRADE_COMPARE(a.storage().alignment(), 1);
CORRADE_COMPARE(a.format(), pixelFormatWrap(GL::PixelFormat::RGB)); CORRADE_COMPARE(a.format(), pixelFormatWrap(GL::PixelFormat::RGB));
CORRADE_COMPARE(a.formatExtra(), UnsignedInt(GL::PixelType::UnsignedShort)); CORRADE_COMPARE(a.formatExtra(), UnsignedInt(GL::PixelType::UnsignedShort));
CORRADE_COMPARE(a.pixelSize(), 6); CORRADE_COMPARE(a.pixelSize(), 6);
CORRADE_COMPARE(a.size(), (Vector2i{1, 3})); CORRADE_COMPARE(a.size(), (Vector2i{1, 3}));
CORRADE_COMPARE(a.data(), data); CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 3*6); CORRADE_COMPARE(a.data().size(), 3*6);
CORRADE_COMPARE(static_cast<const void*>(&a.pixels<Math::Vector3<UnsignedShort>>()[0][0]), data);
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 3*6);
CORRADE_COMPARE(static_cast<const void*>(&a.mutablePixels<Math::Vector3<UnsignedShort>>()[0][0]), data);
CORRADE_COMPARE(a.importerState(), &state); CORRADE_COMPARE(a.importerState(), &state);
} }
} }
@ -255,12 +310,15 @@ void ImageDataTest::constructCompressedGeneric() {
ImageData2D a{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, ImageData2D a{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4},
Containers::Array<char>{data, 8}, &state}; Containers::Array<char>{data, 8}, &state};
CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(a.isCompressed()); CORRADE_VERIFY(a.isCompressed());
CORRADE_COMPARE(a.compressedStorage().compressedBlockSize(), Vector3i{0}); CORRADE_COMPARE(a.compressedStorage().compressedBlockSize(), Vector3i{0});
CORRADE_COMPARE(a.compressedFormat(), CompressedPixelFormat::Bc1RGBAUnorm); CORRADE_COMPARE(a.compressedFormat(), CompressedPixelFormat::Bc1RGBAUnorm);
CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); CORRADE_COMPARE(a.size(), (Vector2i{4, 4}));
CORRADE_COMPARE(a.data(), data); CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 8); CORRADE_COMPARE(a.data().size(), 8);
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 8);
CORRADE_COMPARE(a.importerState(), &state); CORRADE_COMPARE(a.importerState(), &state);
} { } {
auto data = new char[8]; auto data = new char[8];
@ -269,12 +327,15 @@ void ImageDataTest::constructCompressedGeneric() {
CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, CompressedPixelFormat::Bc1RGBAUnorm, {4, 4},
Containers::Array<char>{data, 8}, &state}; Containers::Array<char>{data, 8}, &state};
CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(a.isCompressed()); CORRADE_VERIFY(a.isCompressed());
CORRADE_COMPARE(a.compressedStorage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(a.compressedStorage().compressedBlockSize(), Vector3i{4});
CORRADE_COMPARE(a.compressedFormat(), CompressedPixelFormat::Bc1RGBAUnorm); CORRADE_COMPARE(a.compressedFormat(), CompressedPixelFormat::Bc1RGBAUnorm);
CORRADE_COMPARE(a.size(), Vector2i(4, 4)); CORRADE_COMPARE(a.size(), Vector2i(4, 4));
CORRADE_COMPARE(a.data(), data); CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 8); CORRADE_COMPARE(a.data().size(), 8);
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 8);
CORRADE_COMPARE(a.importerState(), &state); CORRADE_COMPARE(a.importerState(), &state);
} }
} }
@ -288,18 +349,270 @@ void ImageDataTest::constructCompressedImplementationSpecific() {
GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4},
Containers::Array<char>{data, 8}, &state}; Containers::Array<char>{data, 8}, &state};
CORRADE_COMPARE(a.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(a.isCompressed()); CORRADE_VERIFY(a.isCompressed());
CORRADE_COMPARE(a.compressedStorage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(a.compressedStorage().compressedBlockSize(), Vector3i{4});
CORRADE_COMPARE(a.compressedFormat(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(a.compressedFormat(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1));
CORRADE_COMPARE(a.size(), (Vector2i{4, 4})); CORRADE_COMPARE(a.size(), (Vector2i{4, 4}));
CORRADE_COMPARE(a.data(), data); CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 8); CORRADE_COMPARE(a.data().size(), 8);
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 8);
CORRADE_COMPARE(a.importerState(), &state); CORRADE_COMPARE(a.importerState(), &state);
} }
/* Manual properties not implemented yet */ /* Manual properties not implemented yet */
} }
void ImageDataTest::constructGenericNotOwned() {
auto&& instanceData = NotOwnedData[testCaseInstanceId()];
setTestCaseDescription(instanceData.name);
{
char data[4*4];
int state;
ImageData2D a{PixelFormat::RGBA8Unorm, {1, 3}, instanceData.dataFlags, data, &state};
CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags);
CORRADE_VERIFY(!a.isCompressed());
CORRADE_COMPARE(a.storage().alignment(), 4);
CORRADE_COMPARE(a.format(), PixelFormat::RGBA8Unorm);
CORRADE_COMPARE(a.formatExtra(), 0);
CORRADE_COMPARE(a.pixelSize(), 4);
CORRADE_COMPARE(a.size(), (Vector2i{1, 3}));
CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 4*4);
CORRADE_COMPARE(static_cast<const void*>(&a.pixels<Color4ub>()[0][0]), data);
if(instanceData.dataFlags & DataFlag::Mutable) {
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 4*4);
CORRADE_COMPARE(static_cast<const void*>(&a.mutablePixels<Color4ub>()[0][0]), data);
}
CORRADE_COMPARE(a.importerState(), &state);
} {
char data[3*2];
int state;
ImageData2D a{PixelStorage{}.setAlignment(1),
PixelFormat::R16UI, {1, 3}, instanceData.dataFlags, data, &state};
CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags);
CORRADE_VERIFY(!a.isCompressed());
CORRADE_COMPARE(a.storage().alignment(), 1);
CORRADE_COMPARE(a.format(), PixelFormat::R16UI);
CORRADE_COMPARE(a.formatExtra(), 0);
CORRADE_COMPARE(a.pixelSize(), 2);
CORRADE_COMPARE(a.size(), (Vector2i{1, 3}));
CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 3*2);
CORRADE_COMPARE(static_cast<const void*>(&a.pixels<UnsignedShort>()[0][0]), data);
if(instanceData.dataFlags & DataFlag::Mutable) {
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 3*2);
CORRADE_COMPARE(static_cast<const void*>(&a.mutablePixels<UnsignedShort>()[0][0]), data);
}
CORRADE_COMPARE(a.importerState(), &state);
}
}
void ImageDataTest::constructImplementationSpecificNotOwned() {
auto&& instanceData = NotOwnedData[testCaseInstanceId()];
setTestCaseDescription(instanceData.name);
/* Single format */
{
char data[3*12];
int state;
ImageData2D a{PixelStorage{}.setAlignment(1),
Vk::PixelFormat::R32G32B32F, {1, 3}, instanceData.dataFlags, data, &state};
CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags);
CORRADE_VERIFY(!a.isCompressed());
CORRADE_COMPARE(a.storage().alignment(), 1);
CORRADE_COMPARE(a.format(), pixelFormatWrap(Vk::PixelFormat::R32G32B32F));
CORRADE_COMPARE(a.formatExtra(), 0);
CORRADE_COMPARE(a.pixelSize(), 12);
CORRADE_COMPARE(a.size(), (Vector2i{1, 3}));
CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 3*12);
CORRADE_COMPARE(static_cast<const void*>(&a.pixels<Color3>()[0][0]), data);
if(instanceData.dataFlags & DataFlag::Mutable) {
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 3*12);
CORRADE_COMPARE(static_cast<const void*>(&a.mutablePixels<Color3>()[0][0]), data);
}
CORRADE_COMPARE(a.importerState(), &state);
}
/* Format + extra */
{
char data[3*6];
int state;
ImageData2D a{PixelStorage{}.setAlignment(1),
GL::PixelFormat::RGB, GL::PixelType::UnsignedShort, {1, 3}, instanceData.dataFlags, data, &state};
CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags);
CORRADE_VERIFY(!a.isCompressed());
CORRADE_COMPARE(a.format(), pixelFormatWrap(GL::PixelFormat::RGB));
CORRADE_COMPARE(a.formatExtra(), UnsignedInt(GL::PixelType::UnsignedShort));
CORRADE_COMPARE(a.pixelSize(), 6);
CORRADE_COMPARE(a.size(), (Vector2i{1, 3}));
CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 3*6);
CORRADE_COMPARE(static_cast<const void*>(&a.pixels<Math::Vector3<UnsignedShort>>()[0][0]), data);
if(instanceData.dataFlags & DataFlag::Mutable) {
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 3*6);
CORRADE_COMPARE(static_cast<const void*>(&a.mutablePixels<Math::Vector3<UnsignedShort>>()[0][0]), data);
}
CORRADE_COMPARE(a.importerState(), &state);
}
/* Manual pixel size */
{
char data[3*6];
int state;
ImageData2D a{PixelStorage{}.setAlignment(1), 666, 1337, 6, {1, 3}, instanceData.dataFlags, data, &state};
CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags);
CORRADE_VERIFY(!a.isCompressed());
CORRADE_COMPARE(a.storage().alignment(), 1);
CORRADE_COMPARE(a.format(), pixelFormatWrap(GL::PixelFormat::RGB));
CORRADE_COMPARE(a.formatExtra(), UnsignedInt(GL::PixelType::UnsignedShort));
CORRADE_COMPARE(a.pixelSize(), 6);
CORRADE_COMPARE(a.size(), (Vector2i{1, 3}));
CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 3*6);
CORRADE_COMPARE(static_cast<const void*>(&a.pixels<Math::Vector3<UnsignedShort>>()[0][0]), data);
if(instanceData.dataFlags & DataFlag::Mutable) {
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 3*6);
CORRADE_COMPARE(static_cast<const void*>(&a.mutablePixels<Math::Vector3<UnsignedShort>>()[0][0]), data);
}
CORRADE_COMPARE(a.importerState(), &state);
}
}
void ImageDataTest::constructCompressedGenericNotOwned() {
auto&& instanceData = NotOwnedData[testCaseInstanceId()];
setTestCaseDescription(instanceData.name);
{
char data[8];
int state;
ImageData2D a{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4},
instanceData.dataFlags, data, &state};
CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags);
CORRADE_VERIFY(a.isCompressed());
CORRADE_COMPARE(a.compressedStorage().compressedBlockSize(), Vector3i{0});
CORRADE_COMPARE(a.compressedFormat(), CompressedPixelFormat::Bc1RGBAUnorm);
CORRADE_COMPARE(a.size(), (Vector2i{4, 4}));
CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 8);
if(instanceData.dataFlags & DataFlag::Mutable) {
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 8);
}
CORRADE_COMPARE(a.importerState(), &state);
} {
char data[8];
int state;
ImageData2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}),
CompressedPixelFormat::Bc1RGBAUnorm, {4, 4},
instanceData.dataFlags, data, &state};
CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags);
CORRADE_VERIFY(a.isCompressed());
CORRADE_COMPARE(a.compressedStorage().compressedBlockSize(), Vector3i{4});
CORRADE_COMPARE(a.compressedFormat(), CompressedPixelFormat::Bc1RGBAUnorm);
CORRADE_COMPARE(a.size(), Vector2i(4, 4));
CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 8);
if(instanceData.dataFlags & DataFlag::Mutable) {
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 8);
}
CORRADE_COMPARE(a.importerState(), &state);
}
}
void ImageDataTest::constructCompressedImplementationSpecificNotOwned() {
auto&& instanceData = NotOwnedData[testCaseInstanceId()];
setTestCaseDescription(instanceData.name);
/* Format with autodetection */
{
char data[8];
int state;
ImageData2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}),
GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4},
instanceData.dataFlags, data, &state};
CORRADE_COMPARE(a.dataFlags(), instanceData.dataFlags);
CORRADE_VERIFY(a.isCompressed());
CORRADE_COMPARE(a.compressedStorage().compressedBlockSize(), Vector3i{4});
CORRADE_COMPARE(a.compressedFormat(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1));
CORRADE_COMPARE(a.size(), (Vector2i{4, 4}));
CORRADE_COMPARE(static_cast<const void*>(a.data().data()), data);
CORRADE_COMPARE(a.data().size(), 8);
if(instanceData.dataFlags & DataFlag::Mutable) {
CORRADE_COMPARE(static_cast<const void*>(a.mutableData().data()), data);
CORRADE_COMPARE(a.mutableData().size(), 8);
}
CORRADE_COMPARE(a.importerState(), &state);
}
/* Manual properties not implemented yet */
}
void ImageDataTest::constructGenericNotOwnedFlagOwned() {
char data[4*4];
std::ostringstream out;
Error redirectError{&out};
ImageData2D{PixelFormat::RGBA8Unorm, {1, 3}, DataFlag::Owned, data};
ImageData2D{PixelStorage{}.setAlignment(1), PixelFormat::R16UI, {1, 3}, DataFlag::Owned, data};
CORRADE_COMPARE(out.str(),
"Trade::ImageData: can't construct a non-owned instance with Trade::DataFlag::Owned\n"
"Trade::ImageData: can't construct a non-owned instance with Trade::DataFlag::Owned\n");
}
void ImageDataTest::constructImplementationSpecificNotOwnedFlagOwned() {
char data[3*12];
std::ostringstream out;
Error redirectError{&out};
ImageData2D{PixelStorage{}.setAlignment(1), Vk::PixelFormat::R32G32B32F, {1, 3}, DataFlag::Owned, data};
ImageData2D{PixelStorage{}.setAlignment(1), GL::PixelFormat::RGB, GL::PixelType::UnsignedShort, {1, 3}, DataFlag::Owned, data};
CORRADE_COMPARE(out.str(),
"Trade::ImageData: can't construct a non-owned instance with Trade::DataFlag::Owned\n"
"Trade::ImageData: can't construct a non-owned instance with Trade::DataFlag::Owned\n");
}
void ImageDataTest::constructCompressedGenericNotOwnedFlagOwned() {
char data[8];
std::ostringstream out;
Error redirectError{&out};
ImageData2D{CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, DataFlag::Owned, data};
ImageData2D{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}),
CompressedPixelFormat::Bc1RGBAUnorm, {4, 4}, DataFlag::Owned, data};
CORRADE_COMPARE(out.str(),
"Trade::ImageData: can't construct a non-owned instance with Trade::DataFlag::Owned\n"
"Trade::ImageData: can't construct a non-owned instance with Trade::DataFlag::Owned\n");
}
void ImageDataTest::constructCompressedImplementationSpecificNotOwnedFlagOwned() {
char data[8];
std::ostringstream out;
Error redirectError{&out};
ImageData2D a{CompressedPixelStorage{}.setCompressedBlockSize(Vector3i{4}),
GL::CompressedPixelFormat::RGBS3tcDxt1, {4, 4}, DataFlag::Owned, data};
CORRADE_COMPARE(out.str(),
"Trade::ImageData: can't construct a non-owned instance with Trade::DataFlag::Owned\n");
}
void ImageDataTest::constructInvalidSize() { void ImageDataTest::constructInvalidSize() {
std::ostringstream out; std::ostringstream out;
Error redirectError{&out}; Error redirectError{&out};
@ -343,6 +656,7 @@ void ImageDataTest::constructMoveGeneric() {
CORRADE_COMPARE(a.data(), nullptr); CORRADE_COMPARE(a.data(), nullptr);
CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(a.size(), Vector2i{});
CORRADE_COMPARE(b.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(!b.isCompressed()); CORRADE_VERIFY(!b.isCompressed());
CORRADE_COMPARE(b.storage().alignment(), 1); CORRADE_COMPARE(b.storage().alignment(), 1);
CORRADE_COMPARE(b.format(), PixelFormat::RGBA32F); CORRADE_COMPARE(b.format(), PixelFormat::RGBA32F);
@ -360,6 +674,7 @@ void ImageDataTest::constructMoveGeneric() {
CORRADE_COMPARE(b.data(), data2); CORRADE_COMPARE(b.data(), data2);
CORRADE_COMPARE(b.size(), (Vector2i{2, 6})); CORRADE_COMPARE(b.size(), (Vector2i{2, 6}));
CORRADE_COMPARE(c.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(!c.isCompressed()); CORRADE_VERIFY(!c.isCompressed());
CORRADE_COMPARE(c.storage().alignment(), 1); CORRADE_COMPARE(c.storage().alignment(), 1);
CORRADE_COMPARE(c.format(), PixelFormat::RGBA32F); CORRADE_COMPARE(c.format(), PixelFormat::RGBA32F);
@ -384,6 +699,7 @@ void ImageDataTest::constructMoveImplementationSpecific() {
CORRADE_COMPARE(a.data(), nullptr); CORRADE_COMPARE(a.data(), nullptr);
CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(a.size(), Vector2i{});
CORRADE_COMPARE(b.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(!b.isCompressed()); CORRADE_VERIFY(!b.isCompressed());
CORRADE_COMPARE(b.storage().alignment(), 1); CORRADE_COMPARE(b.storage().alignment(), 1);
CORRADE_COMPARE(b.format(), pixelFormatWrap(GL::PixelFormat::RGB)); CORRADE_COMPARE(b.format(), pixelFormatWrap(GL::PixelFormat::RGB));
@ -402,6 +718,7 @@ void ImageDataTest::constructMoveImplementationSpecific() {
CORRADE_COMPARE(b.data(), data2); CORRADE_COMPARE(b.data(), data2);
CORRADE_COMPARE(b.size(), Vector2i(2, 6)); CORRADE_COMPARE(b.size(), Vector2i(2, 6));
CORRADE_COMPARE(c.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(!c.isCompressed()); CORRADE_VERIFY(!c.isCompressed());
CORRADE_COMPARE(c.storage().alignment(), 1); CORRADE_COMPARE(c.storage().alignment(), 1);
CORRADE_COMPARE(c.format(), pixelFormatWrap(GL::PixelFormat::RGB)); CORRADE_COMPARE(c.format(), pixelFormatWrap(GL::PixelFormat::RGB));
@ -424,6 +741,7 @@ void ImageDataTest::constructMoveCompressedGeneric() {
CORRADE_COMPARE(a.data(), nullptr); CORRADE_COMPARE(a.data(), nullptr);
CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(a.size(), Vector2i{});
CORRADE_COMPARE(b.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(b.isCompressed()); CORRADE_VERIFY(b.isCompressed());
CORRADE_COMPARE(b.compressedStorage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(b.compressedStorage().compressedBlockSize(), Vector3i{4});
CORRADE_COMPARE(b.compressedFormat(), CompressedPixelFormat::Bc3RGBAUnorm); CORRADE_COMPARE(b.compressedFormat(), CompressedPixelFormat::Bc3RGBAUnorm);
@ -439,6 +757,7 @@ void ImageDataTest::constructMoveCompressedGeneric() {
CORRADE_COMPARE(b.data(), data2); CORRADE_COMPARE(b.data(), data2);
CORRADE_COMPARE(b.size(), (Vector2i{8, 4})); CORRADE_COMPARE(b.size(), (Vector2i{8, 4}));
CORRADE_COMPARE(c.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(c.isCompressed()); CORRADE_VERIFY(c.isCompressed());
CORRADE_COMPARE(c.compressedStorage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(c.compressedStorage().compressedBlockSize(), Vector3i{4});
CORRADE_COMPARE(c.compressedFormat(), CompressedPixelFormat::Bc3RGBAUnorm); CORRADE_COMPARE(c.compressedFormat(), CompressedPixelFormat::Bc3RGBAUnorm);
@ -459,6 +778,7 @@ void ImageDataTest::constructMoveCompressedImplementationSpecific() {
CORRADE_COMPARE(a.data(), nullptr); CORRADE_COMPARE(a.data(), nullptr);
CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(a.size(), Vector2i{});
CORRADE_COMPARE(b.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(b.isCompressed()); CORRADE_VERIFY(b.isCompressed());
CORRADE_COMPARE(b.compressedStorage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(b.compressedStorage().compressedBlockSize(), Vector3i{4});
CORRADE_COMPARE(b.compressedFormat(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(b.compressedFormat(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1));
@ -474,6 +794,7 @@ void ImageDataTest::constructMoveCompressedImplementationSpecific() {
CORRADE_COMPARE(b.data(), data2); CORRADE_COMPARE(b.data(), data2);
CORRADE_COMPARE(b.size(), (Vector2i{8, 4})); CORRADE_COMPARE(b.size(), (Vector2i{8, 4}));
CORRADE_COMPARE(c.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(c.isCompressed()); CORRADE_VERIFY(c.isCompressed());
CORRADE_COMPARE(c.compressedStorage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(c.compressedStorage().compressedBlockSize(), Vector3i{4});
CORRADE_COMPARE(c.compressedFormat(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(c.compressedFormat(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1));
@ -493,6 +814,7 @@ void ImageDataTest::constructMoveAttachState() {
CORRADE_COMPARE(a.data(), nullptr); CORRADE_COMPARE(a.data(), nullptr);
CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(a.size(), Vector2i{});
CORRADE_COMPARE(b.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(!b.isCompressed()); CORRADE_VERIFY(!b.isCompressed());
CORRADE_COMPARE(b.storage().alignment(), 1); CORRADE_COMPARE(b.storage().alignment(), 1);
CORRADE_COMPARE(b.format(), pixelFormatWrap(GL::PixelFormat::RGB)); CORRADE_COMPARE(b.format(), pixelFormatWrap(GL::PixelFormat::RGB));
@ -515,6 +837,7 @@ void ImageDataTest::constructMoveCompressedAttachState() {
CORRADE_COMPARE(a.data(), nullptr); CORRADE_COMPARE(a.data(), nullptr);
CORRADE_COMPARE(a.size(), Vector2i{}); CORRADE_COMPARE(a.size(), Vector2i{});
CORRADE_COMPARE(b.dataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_VERIFY(b.isCompressed()); CORRADE_VERIFY(b.isCompressed());
CORRADE_COMPARE(b.compressedStorage().compressedBlockSize(), Vector3i{4}); CORRADE_COMPARE(b.compressedStorage().compressedBlockSize(), Vector3i{4});
CORRADE_COMPARE(b.compressedFormat(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1)); CORRADE_COMPARE(b.compressedFormat(), compressedPixelFormatWrap(GL::CompressedPixelFormat::RGBS3tcDxt1));
@ -603,6 +926,31 @@ void ImageDataTest::dataRvalue() {
CORRADE_COMPARE(released.data(), data); CORRADE_COMPARE(released.data(), data);
} }
void ImageDataTest::mutableAccessNotAllowed() {
const char data[4*4]{};
ImageData2D a{PixelFormat::RGBA8Unorm, {2, 2}, DataFlags{}, data};
std::ostringstream out;
Error redirectError{&out};
a.mutableData();
a.mutablePixels();
/* Can't do just MutableImageView2D(a) because the compiler then treats it
as a function. Can't do MutableImageView2D{a} because that doesn't work
on old Clang. So it's this mess, then. Sigh. */
auto b = MutableImageView2D(a);
static_cast<void>(b);
auto c = MutableCompressedImageView2D(a);
static_cast<void>(c);
/* a.mutablePixels<T>() calls non-templated mutablePixels(), so assume
there it will blow up correctly as well (can't test because it asserts
inside arrayCast() due to zero stride) */
CORRADE_COMPARE(out.str(),
"Trade::ImageData::mutableData(): the image is not mutable\n"
"Trade::ImageData::mutablePixels(): the image is not mutable\n"
"Trade::ImageData: the image is not mutable\n"
"Trade::ImageData: the image is not mutable\n");
}
void ImageDataTest::dataProperties() { void ImageDataTest::dataProperties() {
ImageData3D image{ ImageData3D image{
PixelStorage{} PixelStorage{}
@ -646,12 +994,12 @@ void ImageDataTest::pixels1D() {
/* Full test is in ImageTest, this is just a sanity check */ /* Full test is in ImageTest, this is just a sanity check */
{ {
Containers::StridedArrayView1D<Color3ub> pixels = image.pixels<Color3ub>(); Containers::StridedArrayView1D<Color3ub> pixels = image.mutablePixels<Color3ub>();
CORRADE_COMPARE(pixels.size(), 2); CORRADE_COMPARE(pixels.size(), 2);
CORRADE_COMPARE(pixels.stride(), 3); CORRADE_COMPARE(pixels.stride(), 3);
CORRADE_COMPARE(pixels.data(), image.data() + 3*3); CORRADE_COMPARE(pixels.data(), image.data() + 3*3);
} { } {
Containers::StridedArrayView1D<const Color3ub> pixels = Containers::arrayCast<1, const Color3ub>(cimage.pixels()); Containers::StridedArrayView1D<const Color3ub> pixels = cimage.pixels<Color3ub>();
CORRADE_COMPARE(pixels.size(), 2); CORRADE_COMPARE(pixels.size(), 2);
CORRADE_COMPARE(pixels.stride(), 3); CORRADE_COMPARE(pixels.stride(), 3);
CORRADE_COMPARE(pixels.data(), cimage.data() + 3*3); CORRADE_COMPARE(pixels.data(), cimage.data() + 3*3);
@ -671,12 +1019,12 @@ void ImageDataTest::pixels2D() {
/* Full test is in ImageTest, this is just a sanity check */ /* Full test is in ImageTest, this is just a sanity check */
{ {
Containers::StridedArrayView2D<Color3ub> pixels = image.pixels<Color3ub>(); Containers::StridedArrayView2D<Color3ub> pixels = image.mutablePixels<Color3ub>();
CORRADE_COMPARE(pixels.size(), (Containers::StridedArrayView2D<Color3ub>::Size{4, 2})); CORRADE_COMPARE(pixels.size(), (Containers::StridedArrayView2D<Color3ub>::Size{4, 2}));
CORRADE_COMPARE(pixels.stride(), (Containers::StridedArrayView2D<Color3ub>::Stride{20, 3})); CORRADE_COMPARE(pixels.stride(), (Containers::StridedArrayView2D<Color3ub>::Stride{20, 3}));
CORRADE_COMPARE(pixels.data(), image.data() + 2*20 + 3*3); CORRADE_COMPARE(pixels.data(), image.data() + 2*20 + 3*3);
} { } {
Containers::StridedArrayView2D<const Color3ub> pixels = Containers::arrayCast<2, const Color3ub>(cimage.pixels()); Containers::StridedArrayView2D<const Color3ub> pixels = cimage.pixels<Color3ub>();
CORRADE_COMPARE(pixels.size(), (Containers::StridedArrayView2D<const Color3ub>::Size{4, 2})); CORRADE_COMPARE(pixels.size(), (Containers::StridedArrayView2D<const Color3ub>::Size{4, 2}));
CORRADE_COMPARE(pixels.stride(), (Containers::StridedArrayView2D<const Color3ub>::Stride{20, 3})); CORRADE_COMPARE(pixels.stride(), (Containers::StridedArrayView2D<const Color3ub>::Stride{20, 3}));
CORRADE_COMPARE(pixels.data(), cimage.data() + 2*20 + 3*3); CORRADE_COMPARE(pixels.data(), cimage.data() + 2*20 + 3*3);
@ -697,7 +1045,7 @@ void ImageDataTest::pixels3D() {
/* Full test is in ImageTest, this is just a sanity check */ /* Full test is in ImageTest, this is just a sanity check */
{ {
Containers::StridedArrayView3D<Color3ub> pixels = image.pixels<Color3ub>(); Containers::StridedArrayView3D<Color3ub> pixels = image.mutablePixels<Color3ub>();
CORRADE_COMPARE(pixels.size(), (Containers::StridedArrayView3D<Color3ub>::Size{3, 4, 2})); CORRADE_COMPARE(pixels.size(), (Containers::StridedArrayView3D<Color3ub>::Size{3, 4, 2}));
CORRADE_COMPARE(pixels.stride(), (Containers::StridedArrayView3D<Color3ub>::Stride{140, 20, 3})); CORRADE_COMPARE(pixels.stride(), (Containers::StridedArrayView3D<Color3ub>::Stride{140, 20, 3}));
CORRADE_COMPARE(pixels.data(), image.data() + 140 + 2*20 + 3*3); CORRADE_COMPARE(pixels.data(), image.data() + 140 + 2*20 + 3*3);

Loading…
Cancel
Save