Browse Source

Add templated Image{,View,Data}::pixels().

It's potentially dangerous because the user is responsible for choosing
a correct type, on the other hand forcing them to do it verbosely
through arrayCast() is both too annoying and too hard to explain.
pull/344/head
Vladimír Vondruš 7 years ago
parent
commit
e3eebd34b7
  1. 3
      doc/snippets/Magnum.cpp
  2. 36
      src/Magnum/Image.h
  3. 14
      src/Magnum/ImageView.h
  4. 12
      src/Magnum/Test/ImageTest.cpp
  5. 14
      src/Magnum/Test/ImageViewTest.cpp
  6. 19
      src/Magnum/Trade/ImageData.h
  7. 8
      src/Magnum/Trade/Test/ImageDataTest.cpp

3
doc/snippets/Magnum.cpp

@ -68,8 +68,7 @@ std::nullptr_t data{};
/* [Image-pixels] */ /* [Image-pixels] */
Image2D image{PixelFormat::RGB8Unorm, {128, 128}, data}; Image2D image{PixelFormat::RGB8Unorm, {128, 128}, data};
Containers::StridedArrayView2D<Color3ub> pixels = Containers::StridedArrayView2D<Color3ub> pixels = image.pixels<Color3ub>();
Containers::arrayCast<2, Color3ub>(image.pixels());
for(auto row: pixels.slice({48, 48}, {80, 80})) { for(auto row: pixels.slice({48, 48}, {80, 80})) {
for(Color3ub& pixel: row) pixel *= 1.1f; for(Color3ub& pixel: row) pixel *= 1.1f;
} }

36
src/Magnum/Image.h

@ -79,14 +79,15 @@ do by hand even with the help of @ref dataProperties().
The @ref pixels() accessor returns a multi-dimensional The @ref pixels() accessor returns a multi-dimensional
@ref Corrade::Containers::StridedArrayView describing layout of the data and @ref Corrade::Containers::StridedArrayView describing layout of the data and
providing easy access to particular rows, pixels and pixel channels. The providing easy access to particular rows, pixels and pixel channels. The
returned view always has one dimension more than the actual image, with the non-templated version returns a view that has one dimension more than the
last dimension being bytes in a particular pixels. The second-to-last dimension actual image, with the last dimension being bytes in a particular pixels. The
is always pixels in a row, the one before (if the image is at least 2D) is rows second-to-last dimension is always pixels in a row, the one before (if the
in an image, and for 3D images the very first dimension describes image slices. image is at least 2D) is rows in an image, and for 3D images the very first
Desired usage is casting to a concrete type based on @ref format() first using dimension describes image slices. Desired usage is casting to a concrete type
@ref Corrade::Containers::arrayCast() (which also flattens the last dimension) based on @ref format() first, either using the templated @ref pixels<T>() or
and then operating on the concretely typed array. The following example using @ref Corrade::Containers::arrayCast() and then operating on the
brightens the center 32x32 area of an image: concretely typed array. The following example brightens the center 32x32 area
of an image:
@snippet Magnum.cpp Image-pixels @snippet Magnum.cpp Image-pixels
@ -382,6 +383,25 @@ template<UnsignedInt dimensions> class Image {
Containers::StridedArrayView<dimensions + 1, char> pixels(); Containers::StridedArrayView<dimensions + 1, char> pixels();
Containers::StridedArrayView<dimensions + 1, const char> pixels() const; /**< @overload */ Containers::StridedArrayView<dimensions + 1, const char> pixels() const; /**< @overload */
/**
* @brief View on pixel data with a concrete pixel type
*
* Compared to non-templated @ref pixels() in addition casts the pixel
* data to a specified type. The user is responsible for choosing
* correct type for given @ref format() --- checking it on the library
* side is not possible for the general case.
*/
template<class T> Containers::StridedArrayView<dimensions, T> pixels() {
/* Deliberately not adding a StridedArrayView include, it should
work without since this is a templated function */
return Containers::arrayCast<dimensions, T>(pixels());
}
/** @overload */
template<class T> Containers::StridedArrayView<dimensions, const T> pixels() const {
return Containers::arrayCast<dimensions, const T>(pixels());
}
/** /**
* @brief Release data storage * @brief Release data storage
* *

14
src/Magnum/ImageView.h

@ -394,6 +394,20 @@ template<UnsignedInt dimensions> class ImageView {
*/ */
Containers::StridedArrayView<dimensions + 1, const char> pixels() const; Containers::StridedArrayView<dimensions + 1, const char> pixels() const;
/**
* @brief View on pixel data with a concrete pixel type
*
* Compared to non-templated @ref pixels() in addition casts the pixel
* data to a specified type. The user is responsible for choosing
* correct type for given @ref format() --- checking it on the library
* side is not possible for the general case.
*/
template<class T> Containers::StridedArrayView<dimensions, const T> pixels() const {
/* Deliberately not adding a StridedArrayView include, it should
work without since this is a templated function */
return Containers::arrayCast<dimensions, const T>(pixels());
}
private: private:
PixelStorage _storage; PixelStorage _storage;
PixelFormat _format; PixelFormat _format;

12
src/Magnum/Test/ImageTest.cpp

@ -682,14 +682,14 @@ void ImageTest::pixels1D() {
const Image1D& cimage = image; const Image1D& cimage = image;
{ {
Containers::StridedArrayView1D<Color3ub> pixels = Containers::arrayCast<1, Color3ub>(image.pixels()); Containers::StridedArrayView1D<Color3ub> pixels = image.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(), image.data() + 3*3); CORRADE_COMPARE(pixels.data(), image.data() + 3*3);
CORRADE_COMPARE(pixels[0], (Color3ub{3, 4, 5})); CORRADE_COMPARE(pixels[0], (Color3ub{3, 4, 5}));
CORRADE_COMPARE(pixels[1], (Color3ub{6, 7, 8})); CORRADE_COMPARE(pixels[1], (Color3ub{6, 7, 8}));
} { } {
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);
@ -716,14 +716,14 @@ void ImageTest::pixels2D() {
const Image2D& cimage = image; const Image2D& cimage = image;
{ {
Containers::StridedArrayView2D<Color3ub> pixels = Containers::arrayCast<2, Color3ub>(image.pixels()); Containers::StridedArrayView2D<Color3ub> pixels = image.pixels<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);
CORRADE_COMPARE(pixels[3][0], (Color3ub{4, 5, 6})); CORRADE_COMPARE(pixels[3][0], (Color3ub{4, 5, 6}));
CORRADE_COMPARE(pixels[3][1], (Color3ub{7, 8, 9})); CORRADE_COMPARE(pixels[3][1], (Color3ub{7, 8, 9}));
} { } {
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);
@ -776,14 +776,14 @@ void ImageTest::pixels3D() {
const Image3D& cimage = image; const Image3D& cimage = image;
{ {
Containers::StridedArrayView3D<Color3ub> pixels = Containers::arrayCast<3, Color3ub>(image.pixels()); Containers::StridedArrayView3D<Color3ub> pixels = image.pixels<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);
CORRADE_COMPARE(pixels[1][3][0], (Color3ub{9, 8, 7})); CORRADE_COMPARE(pixels[1][3][0], (Color3ub{9, 8, 7}));
CORRADE_COMPARE(pixels[1][3][1], (Color3ub{6, 5, 4})); CORRADE_COMPARE(pixels[1][3][1], (Color3ub{6, 5, 4}));
} { } {
Containers::StridedArrayView3D<const Color3ub> pixels = Containers::arrayCast<3, const Color3ub>(cimage.pixels()); Containers::StridedArrayView3D<const Color3ub> pixels = cimage.pixels<Color3ub>();
CORRADE_COMPARE(pixels.size(), (Containers::StridedArrayView3D<const Color3ub>::Size{3, 4, 2})); CORRADE_COMPARE(pixels.size(), (Containers::StridedArrayView3D<const Color3ub>::Size{3, 4, 2}));
CORRADE_COMPARE(pixels.stride(), (Containers::StridedArrayView3D<const Color3ub>::Stride{140, 20, 3})); CORRADE_COMPARE(pixels.stride(), (Containers::StridedArrayView3D<const Color3ub>::Stride{140, 20, 3}));
CORRADE_COMPARE(pixels.data(), cimage.data() + 140 + 2*20 + 3*3); CORRADE_COMPARE(pixels.data(), cimage.data() + 140 + 2*20 + 3*3);

14
src/Magnum/Test/ImageViewTest.cpp

@ -601,7 +601,7 @@ void ImageViewTest::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<const Color3ub> pixels = Containers::arrayCast<1, const Color3ub>(image.pixels()); Containers::StridedArrayView1D<const Color3ub> pixels = image.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(), image.data() + 3*3); CORRADE_COMPARE(pixels.data(), image.data() + 3*3);
@ -618,9 +618,9 @@ void ImageViewTest::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<const Color3ub> pixels = Containers::arrayCast<2, const Color3ub>(image.pixels()); Containers::StridedArrayView2D<const Color3ub> pixels = image.pixels<Color3ub>();
CORRADE_COMPARE(pixels.size(), (Containers::StridedArrayView2D<const Color3ub>::Size{4, 2})); CORRADE_COMPARE(pixels.size(), (Containers::StridedArrayView2D<Color3ub>::Size{4, 2}));
CORRADE_COMPARE(pixels.stride(), (Containers::StridedArrayView2D<const 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);
} }
@ -636,9 +636,9 @@ void ImageViewTest::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<const Color3ub> pixels = Containers::arrayCast<3, const Color3ub>(image.pixels()); Containers::StridedArrayView3D<const Color3ub> pixels = image.pixels<Color3ub>();
CORRADE_COMPARE(pixels.size(), (Containers::StridedArrayView3D<const Color3ub>::Size{3, 4, 2})); CORRADE_COMPARE(pixels.size(), (Containers::StridedArrayView3D<Color3ub>::Size{3, 4, 2}));
CORRADE_COMPARE(pixels.stride(), (Containers::StridedArrayView3D<const 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);
} }

19
src/Magnum/Trade/ImageData.h

@ -361,6 +361,25 @@ template<UnsignedInt dimensions> class ImageData {
Containers::StridedArrayView<dimensions + 1, char> pixels(); Containers::StridedArrayView<dimensions + 1, char> pixels();
Containers::StridedArrayView<dimensions + 1, const char> pixels() const; /**< @overload */ Containers::StridedArrayView<dimensions + 1, const char> pixels() const; /**< @overload */
/**
* @brief View on pixel data with a concrete pixel type
*
* Compared to non-templated @ref pixels() in addition casts the pixel
* data to a specified type. The user is responsible for choosing
* correct type for given @ref format() --- checking it on the library
* side is not possible for the general case.
*/
template<class T> Containers::StridedArrayView<dimensions, T> pixels() {
/* Deliberately not adding a StridedArrayView include, it should
work without since this is a templated function */
return Containers::arrayCast<dimensions, T>(pixels());
}
/** @overload */
template<class T> Containers::StridedArrayView<dimensions, const T> pixels() const {
return Containers::arrayCast<dimensions, const T>(pixels());
}
/** /**
* @brief Release data storage * @brief Release data storage
* *

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

@ -603,7 +603,7 @@ 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 = Containers::arrayCast<1, Color3ub>(image.pixels()); Containers::StridedArrayView1D<Color3ub> pixels = image.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(), image.data() + 3*3); CORRADE_COMPARE(pixels.data(), image.data() + 3*3);
@ -628,7 +628,7 @@ 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 = Containers::arrayCast<2, Color3ub>(image.pixels()); Containers::StridedArrayView2D<Color3ub> pixels = image.pixels<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);
@ -654,12 +654,12 @@ 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 = Containers::arrayCast<3, Color3ub>(image.pixels()); Containers::StridedArrayView3D<Color3ub> pixels = image.pixels<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);
} { } {
Containers::StridedArrayView3D<const Color3ub> pixels = Containers::arrayCast<3, const Color3ub>(cimage.pixels()); Containers::StridedArrayView3D<const Color3ub> pixels = cimage.pixels<Color3ub>();
CORRADE_COMPARE(pixels.size(), (Containers::StridedArrayView3D<const Color3ub>::Size{3, 4, 2})); CORRADE_COMPARE(pixels.size(), (Containers::StridedArrayView3D<const Color3ub>::Size{3, 4, 2}));
CORRADE_COMPARE(pixels.stride(), (Containers::StridedArrayView3D<const Color3ub>::Stride{140, 20, 3})); CORRADE_COMPARE(pixels.stride(), (Containers::StridedArrayView3D<const Color3ub>::Stride{140, 20, 3}));
CORRADE_COMPARE(pixels.data(), cimage.data() + 140 + 2*20 + 3*3); CORRADE_COMPARE(pixels.data(), cimage.data() + 140 + 2*20 + 3*3);

Loading…
Cancel
Save