From fe0a46abad3d44860dede50da27ae3d66a6cf412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 12 May 2023 16:53:34 +0200 Subject: [PATCH] Improve docs of image classes. It looked like it was last touched in 2012. Not great. Also, with this I can finally stop explaining the four-byte-aligned-row defaults to people and can just point them to docs. --- doc/snippets/Magnum.cpp | 75 ++++++++++++++++++++++++--------- doc/snippets/MagnumTrade.cpp | 65 +++++++++++++++++++++------- src/Magnum/Image.h | 60 ++++++++++++++------------ src/Magnum/ImageView.h | 57 +++++++++++++------------ src/Magnum/Trade/ImageData.h | 82 ++++++++++++++++++++++++++---------- 5 files changed, 228 insertions(+), 111 deletions(-) diff --git a/doc/snippets/Magnum.cpp b/doc/snippets/Magnum.cpp index cdf939a07..77441c472 100644 --- a/doc/snippets/Magnum.cpp +++ b/doc/snippets/Magnum.cpp @@ -113,7 +113,7 @@ std::nullptr_t data = nullptr; Image2D image{PixelFormat::RGB8Unorm, {128, 128}, data}; Containers::StridedArrayView2D pixels = image.pixels(); -for(auto row: pixels.slice({48, 48}, {80, 80})) { +for(auto row: pixels.sliceSize({48, 48}, {32, 32})) { for(Color3ub& pixel: row) pixel *= 1.1f; } /* [Image-pixels] */ @@ -122,10 +122,23 @@ for(auto row: pixels.slice({48, 48}, {80, 80})) { { char data[3]; /* [ImageView-usage] */ -ImageView2D image{PixelFormat::RGBA8Unorm, {512, 256}, data}; +ImageView2D view{PixelFormat::RGBA8Unorm, {512, 256}, data}; /* [ImageView-usage] */ } +{ +char tightlyPackedData[3]; +/* [ImageView-usage-alignment] */ +PixelFormat format = DOXYGEN_ELLIPSIS({}); +Vector2i size = DOXYGEN_ELLIPSIS({}); +std::size_t rowLength = size.x()*pixelFormatSize(format); + +ImageView2D view{ + PixelStorage{}.setAlignment(rowLength % 4 == 0 ? 4 : 1), + format, size, tightlyPackedData}; +/* [ImageView-usage-alignment] */ +} + { char evenFrameData[3], oddFrameData[3]; /* [ImageView-usage-streaming] */ @@ -142,10 +155,10 @@ frame.setData(oddFrameData); { char data[3]; /* [ImageView-usage-storage] */ -ImageView2D image{ +ImageView2D view{ PixelStorage{} + .setAlignment(1) .setRowLength(75) - .setAlignment(4) .setSkip({25, 25, 0}), PixelFormat::RGBA8Unorm, {25, 25}, data}; /* [ImageView-usage-storage] */ @@ -155,13 +168,13 @@ ImageView2D image{ { char data[3]; /* [ImageView-usage-gl] */ -ImageView2D image{GL::PixelFormat::DepthComponent, - GL::PixelType::UnsignedInt, {512, 256}, data}; +ImageView2D view{GL::PixelFormat::DepthComponent, + GL::PixelType::UnsignedInt, {512, 256}, data}; /* [ImageView-usage-gl] */ /* [ImageView-usage-gl-extract] */ -auto format = pixelFormatUnwrap(image.format()); -auto type = GLenum(image.formatExtra()); +auto format = pixelFormatUnwrap(view.format()); +auto type = GLenum(view.formatExtra()); /* [ImageView-usage-gl-extract] */ static_cast(format); static_cast(type); @@ -180,7 +193,7 @@ ImageView2D view{{}, MTLPixelFormatRGBA8Unorm_sRGB, {}, 4, {256, 256}, data}; { char data[3]; /* [CompressedImageView-usage] */ -CompressedImageView2D image{CompressedPixelFormat::Bc1RGBUnorm, +CompressedImageView2D view{CompressedPixelFormat::Bc1RGBUnorm, {512, 256}, data}; /* [CompressedImageView-usage] */ } @@ -201,7 +214,7 @@ frame.setData(oddFrameData); { char data[3]; /* [CompressedImageView-usage-storage] */ -CompressedImageView2D image{ +CompressedImageView2D view{ CompressedPixelStorage{} .setRowLength(64) .setCompressedBlockSize({4, 4, 1}) @@ -215,12 +228,12 @@ CompressedImageView2D image{ { char data[3]; /* [CompressedImageView-usage-gl] */ -CompressedImageView2D image{GL::CompressedPixelFormat::SignedRGRgtc2, +CompressedImageView2D view{GL::CompressedPixelFormat::SignedRGRgtc2, {512, 256}, data}; /* [CompressedImageView-usage-gl] */ /* [CompressedImageView-usage-gl-extract] */ -auto format = compressedPixelFormatUnwrap(image.format()); +auto format = compressedPixelFormatUnwrap(view.format()); /* [CompressedImageView-usage-gl-extract] */ static_cast(format); } @@ -228,26 +241,50 @@ static_cast(format); { /* [Image-usage] */ -Containers::Array data; -Image2D image{PixelFormat::RGBA8Unorm, {512, 256}, std::move(data)}; +Vector2i size{512, 256}; +PixelFormat format = PixelFormat::RGBA8Unorm; + +Image2D image{format, size, Containers::Array{ValueInit, + std::size_t(size.product()*pixelFormatSize(format))}}; /* [Image-usage] */ } +{ +/* [Image-usage-padding] */ +PixelFormat format = PixelFormat::RGB8Unorm; +Vector2i size{173, 232}; +std::size_t rowStride = 4*((size.x()*pixelFormatSize(format) + 3)/4); + +Image2D image{format, size, + Containers::Array{ValueInit, std::size_t(size.y()*rowStride)}}; +/* [Image-usage-padding] */ +} + +{ +/* [Image-usage-alignment] */ +Vector2i size{173, 232}; +PixelFormat format = PixelFormat::RGB8Unorm; +std::size_t rowLength = size.x()*pixelFormatSize(format); + +Image2D image{ + PixelStorage{}.setAlignment(rowLength % 4 == 0 ? 4 : 1), format, size, + Containers::Array{ValueInit, std::size_t(size.y()*rowLength)}}; +/* [Image-usage-alignment] */ +} + #if defined(MAGNUM_TARGET_GL) && !defined(MAGNUM_TARGET_GLES) { /* [Image-usage-query] */ GL::Texture2D texture; -Image2D image = texture.image(0, {GL::PixelFormat::DepthComponent, - GL::PixelType::UnsignedInt}); +Image2D image = texture.image(0, Image2D{PixelFormat::Depth32F}); /* [Image-usage-query] */ } #endif { /* [CompressedImage-usage] */ -Containers::Array data; CompressedImage2D image{CompressedPixelFormat::Bc1RGBUnorm, - {512, 256}, std::move(data)}; + {512, 256}, Containers::Array{ValueInit, DOXYGEN_ELLIPSIS(0)}}; /* [CompressedImage-usage] */ } @@ -255,7 +292,7 @@ CompressedImage2D image{CompressedPixelFormat::Bc1RGBUnorm, { /* [CompressedImage-usage-query] */ GL::Texture2D texture; -CompressedImage2D image = texture.compressedImage(0, {}); +CompressedImage2D image = texture.compressedImage(0, CompressedImage2D{}); /* [CompressedImage-usage-query] */ } #endif diff --git a/doc/snippets/MagnumTrade.cpp b/doc/snippets/MagnumTrade.cpp index 2c0ec70a7..78feb6856 100644 --- a/doc/snippets/MagnumTrade.cpp +++ b/doc/snippets/MagnumTrade.cpp @@ -415,34 +415,67 @@ for(UnsignedInt i = 0; i != data.trackCount(); ++i) { } { -/* [ImageData-construction] */ -Containers::Array data; -Trade::ImageData2D image{PixelFormat::RGB8Unorm, {32, 32}, std::move(data)}; -/* [ImageData-construction] */ +/* [ImageData-populating] */ +Containers::Array uncompressedData = DOXYGEN_ELLIPSIS({}); +Trade::ImageData2D uncompressed{PixelFormat::RGB8Unorm, + {32, 32}, std::move(uncompressedData)}; + +Containers::Array compressedData = DOXYGEN_ELLIPSIS({}); +Trade::ImageData2D compressed{CompressedPixelFormat::Bc1RGBUnorm, + {32, 32}, std::move(compressedData)}; +/* [ImageData-populating] */ +} + +{ +/* [ImageData-populating-non-owned] */ +Color3ub uncompressedData[]{DOXYGEN_ELLIPSIS({})}; +Trade::ImageData2D uncompressed{PixelFormat::RGB8Unorm, + {32, 32}, Trade::DataFlag::Mutable, uncompressedData}; + +Containers::ArrayView compressedData = DOXYGEN_ELLIPSIS({}); +Trade::ImageData2D compressed{CompressedPixelFormat::Bc1RGBUnorm, + {32, 32}, Trade::DataFlags{}, compressedData}; +/* [ImageData-populating-non-owned] */ +} + +{ +/* [ImageData-populating-padding] */ +PixelFormat format = DOXYGEN_ELLIPSIS({}); +Vector2i size = DOXYGEN_ELLIPSIS({}); +std::size_t rowStride = 4*((size.x()*pixelFormatSize(format) + 3)/4); +Containers::Array data{ValueInit, std::size_t(size.y()*rowStride)}; +DOXYGEN_ELLIPSIS() + +Trade::ImageData2D image{format, size, std::move(data)}; +/* [ImageData-populating-padding] */ } { -/* [ImageData-construction-compressed] */ -Containers::Array data; -Trade::ImageData2D image{CompressedPixelFormat::Bc1RGBUnorm, - {32, 32}, std::move(data)}; -/* [ImageData-construction-compressed] */ +/* [ImageData-populating-alignment] */ +PixelFormat format = DOXYGEN_ELLIPSIS({}); +Vector2i size = DOXYGEN_ELLIPSIS({}); +std::size_t rowLength = size.x()*pixelFormatSize(format); +Containers::Array data{ValueInit, std::size_t(size.y()*rowLength)}; +DOXYGEN_ELLIPSIS() + +Trade::ImageData2D image{ + PixelStorage{}.setAlignment(rowLength % 4 == 0 ? 4 : 1), + format, size, std::move(data)}; +/* [ImageData-populating-alignment] */ } #ifdef MAGNUM_TARGET_GL { /* [ImageData-usage] */ -Containers::Pointer importer; -Containers::Optional image = importer->image2D(0); -if(!image) Fatal{} << "Oopsie!"; +Trade::ImageData2D image = DOXYGEN_ELLIPSIS(Trade::ImageData2D{PixelFormat{}, {}, nullptr}); GL::Texture2D texture; DOXYGEN_ELLIPSIS() -texture.setStorage(1, GL::textureFormat(image->format()), image->size()); -if(!image->isCompressed()) - texture.setSubImage(0, {}, *image); +texture.setStorage(1, GL::textureFormat(image.format()), image.size()); +if(!image.isCompressed()) + texture.setSubImage(0, {}, image); else - texture.setCompressedSubImage(0, {}, *image); + texture.setCompressedSubImage(0, {}, image); /* [ImageData-usage] */ } #endif diff --git a/src/Magnum/Image.h b/src/Magnum/Image.h index 8fbed0cce..c606df4cc 100644 --- a/src/Magnum/Image.h +++ b/src/Magnum/Image.h @@ -62,19 +62,27 @@ functionality targeted on compressed image formats. @section Image-usage Basic usage -The image takes ownership of a passed @ref Corrade::Containers::Array, together -with storing image size and one of the generic @ref PixelFormat values: +The image takes ownership of a passed @relativeref{Corrade,Containers::Array}, +together with a @ref PixelFormat and size in pixels: @snippet Magnum.cpp Image-usage -On construction, the image internally calculates pixel size corresponding to -given pixel format using @ref pixelFormatSize(). This value is needed to check -that the passed data array is large enough and is also required by most image -manipulation operations. +The constructor internally checks that the passed array is large enough. For +performance reasons it by default expects rows aligned to four bytes, which you +need to account for if using odd image sizes in combination with one-, two- or +three-component formats. The recommended way is to pad the row data to satisfy +the alignment: + +@snippet Magnum.cpp Image-usage-padding + +Alternatively, if padding is not possible or desirable, you can pass a +@ref PixelStorage instance with the alignment overriden to @cpp 1 @ce: + +@snippet Magnum.cpp Image-usage-alignment It's also possible to create just an image placeholder, storing only the image properties without data or size. That is useful for example to specify desired -format of image queries in graphics APIs: +format of image queries in graphics APIs such as @ref GL::Texture::image(): @snippet Magnum.cpp Image-usage-query @@ -82,7 +90,7 @@ As with @ref ImageView, this class supports extra storage parameters and implementation-specific pixel format specification. See the @ref ImageView documentation for more information. -@section Image-pixel-views Obtaining a view on pixel data +@section Image-pixel-access Pixel data access While the raw image data are available through @ref data(), for correct pixel addressing it's required to incorporate all @ref storage() parameters such as @@ -90,30 +98,29 @@ row alignment, row length, skip offset and such. This is very error-prone to do by hand even with the help of @ref dataProperties(). The @ref pixels() accessor returns a multi-dimensional -@ref Corrade::Containers::StridedArrayView describing layout of the data and -providing easy access to particular rows, pixels and pixel channels. The -non-templated version returns a view that has one dimension more than the +@relativeref{Corrade,Containers::StridedArrayView} describing layout of the +data and providing easy access to particular rows, pixels and pixel contents. +The non-templated version returns a view that has one dimension more than the actual image, with the last dimension being bytes in a particular pixels. The second-to-last dimension is always pixels in a row, the one before (if the image is at least 2D) is rows in an image, and for 3D images the very first dimension describes image slices. Desired usage is casting to a concrete type based on @ref format() first, either using the templated @ref pixels() or -using @ref Corrade::Containers::arrayCast() and then operating on the +using @relativeref{Corrade,Containers::arrayCast()} and then operating on the concretely typed array. The following example brightens the center 32x32 area of an image: @snippet Magnum.cpp Image-pixels -@attention Note that the correctness of the cast is can't be generally checked - apart from expecting that the last dimension size is equal to the new type - size. It's the user responsibility to ensure the type matches given - @ref format(). - -This operation is available also on @ref ImageView and non-compressed -@ref Trade::ImageData. See @ref Corrade::Containers::StridedArrayView docs for -more information about transforming, slicing and converting the view further. +@attention Note that the correctness of the cast can't be generally checked + apart from comparing that the last dimension size to the type size. It's + the user responsibility to ensure the type matches given @ref format(). -@see @ref Image1D, @ref Image2D, @ref Image3D, @ref CompressedImage +This operation is available also on a @ref ImageView, and non-compressed +@ref Trade::ImageData. See @relativeref{Corrade,Containers::StridedArrayView} +docs for more information about transforming, slicing and casting the view +further. +@see @ref Image1D, @ref Image2D, @ref Image3D */ template class Image { public: @@ -460,7 +467,7 @@ template class Image { * @m_since{2019,10} * * Provides direct and easy-to-use access to image pixels. See - * @ref Image-pixel-views for more information. + * @ref Image-pixel-access for more information. */ Containers::StridedArrayView pixels(); Containers::StridedArrayView pixels() const; /**< @overload */ @@ -472,7 +479,8 @@ template class Image { * 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. + * side is not possible for the general case. See also + * @ref Image-pixel-access for more information. */ template Containers::StridedArrayView pixels() { /* Deliberately not adding a StridedArrayView include, it should @@ -535,9 +543,8 @@ for equivalent functionality targeted on non-compressed image formats. @section CompressedImage-usage Basic usage -The image takes ownership of a passed @ref Corrade::Containers::Array, together -with storing image size and one of the generic @ref CompressedPixelFormat -values: +The image takes ownership of a passed @relativeref{Corrade,Containers::Array}, +together with a @ref CompressedPixelFormat and size in pixels: @snippet Magnum.cpp CompressedImage-usage @@ -550,7 +557,6 @@ format of image queries in graphics APIs: As with @ref CompressedImageView, this class supports extra storage parameters and implementation-specific compressed pixel format specification. See its documentation for more information. - @see @ref CompressedImage1D, @ref CompressedImage2D, @ref CompressedImage3D */ template class CompressedImage { diff --git a/src/Magnum/ImageView.h b/src/Magnum/ImageView.h index ef0f63a70..010e4591c 100644 --- a/src/Magnum/ImageView.h +++ b/src/Magnum/ImageView.h @@ -66,16 +66,20 @@ functionality targeted on compressed image formats. @section ImageView-usage Basic usage -Usually, the view is created on some pre-existing data array in order to -describe its layout, with pixel format being one of the values from the generic -@link PixelFormat @endlink: +The view is created from a @ref PixelFormat, size in pixels and a data view: @snippet Magnum.cpp ImageView-usage -On construction, the image view internally calculates pixel size corresponding -to given pixel format using @ref pixelFormatSize(). This value is needed to -check that the passed data array is large enough and is also required by most -image manipulation operations. +The constructor internally checks that the passed array is large enough. For +performance reasons it by default expects rows aligned to four bytes, which you +need to account for if using odd image sizes in combination with one-, two- or +three-component formats. While the recommended way is to pad the row data to +satisfy the alignment similarly as shown in @ref Image-usage "Image usage docs", +but with views it's more likely that you have to adapt to a layout of an +existing data array by passing a @ref PixelStorage instance with the alignment +value overriden if needed: + +@snippet Magnum.cpp ImageView-usage-alignment It's also possible to create an empty view and assign the memory later. That is useful for example in case of multi-buffered video streaming, where each frame @@ -83,25 +87,27 @@ has the same properties but a different memory location: @snippet Magnum.cpp ImageView-usage-streaming -It's possible to have views on image sub-rectangles, 3D texture slices or -images with over-aligned rows by passing a particular @ref PixelStorage as -first parameter. In the following snippet, the view is the center 25x25 -sub-rectangle of a 75x75 8-bit RGB image , with rows aligned to four bytes: +The @ref PixelStorage also allows for specifying arbitrary image slices by +passing appropriate row length, image height and skip parameters. In the +following snippet, the view is the center 25x25 sub-rectangle of a 75x75 8-bit +RGB image (with tightly packed rows again): @snippet Magnum.cpp ImageView-usage-storage +Image views provides pixel data access via @ref pixels() in the same way as the +@ref Image class. See @ref Image-pixel-access "its documentation for more information". + @section ImageView-mutable Data mutability -When using types derived from @ref BasicImageView (e.g. @ref ImageView2D), the -viewed data are immutable. This is the most common use case. In order to be -able to mutate the underlying data (for example in order to read into a -pre-allocated memory), use @ref BasicMutableImageView -(e.g. @ref MutableImageView2D) instead. @ref Image and @ref Trade::ImageData -are convertible to either of these. Similarly to -@ref Corrade::Containers::ArrayView etc., a mutable view is also implicitly -convertible to a const one. +The @ref ImageView2D type and related one- and three-dimensional variants only +provide immutable access to the referenced data, as that's the most common use +case. In order to be able to modify the data (for example in order to read into +a pre-allocated memory), use @ref MutableImageView2D and friends instead. +@ref Image and @ref Trade::ImageData are convertible to either of these. +Similarly to @ref Corrade::Containers::ArrayView etc., a mutable view is also +implicitly convertible to a const one. -@subsection ImageView-usage-implementation-specific Implementation-specific formats +@section ImageView-usage-implementation-specific Implementation-specific formats For known graphics APIs, there's a set of utility functions converting from @ref PixelFormat to implementation-specific format identifiers and such @@ -138,7 +144,7 @@ image view using Metal-specific format identifier: @see @ref BasicImageView, @ref ImageView1D, @ref ImageView2D, @ref ImageView3D, @ref BasicMutableImageView, @ref MutableImageView1D, - @ref MutableImageView2D, @ref MutableImageView3D, @ref Image-pixel-views + @ref MutableImageView2D, @ref MutableImageView3D */ template class ImageView { public: @@ -486,7 +492,7 @@ template class ImageView { * @m_since{2019,10} * * Provides direct and easy-to-use access to image pixels. See - * @ref Image-pixel-views for more information. If the view is empty + * @ref Image-pixel-access for more information. If the view is empty * (with @ref data() being @cpp nullptr @ce), returns @cpp nullptr @ce * as well. */ @@ -501,7 +507,7 @@ template class ImageView { * correct type for given @ref format() --- checking it on the library * side is not possible for the general case. If the view is empty * (with @ref data() being @cpp nullptr @ce), returns @cpp nullptr @ce - * as well. + * as well. See also @ref Image-pixel-access for more information. */ template Containers::StridedArrayView::value, typename std::add_const::type, U>::type> pixels() const { if(!_data && !_data.size()) return {}; @@ -608,9 +614,8 @@ functionality targeted on non-compressed image formats. @section CompressedImageView-usage Basic usage -Usually, the view is created on some pre-existing data array in order to -describe its layout, with pixel format being one of the values from the generic -@link CompressedPixelFormat @endlink: +The view is created from a @ref CompressedPixelFormat, size in pixels and a +data view: @snippet Magnum.cpp CompressedImageView-usage diff --git a/src/Magnum/Trade/ImageData.h b/src/Magnum/Trade/ImageData.h index 63256d033..8883f5275 100644 --- a/src/Magnum/Trade/ImageData.h +++ b/src/Magnum/Trade/ImageData.h @@ -54,6 +54,13 @@ namespace Magnum { namespace Trade { /** @brief Image data +Provides access to both uncompressed and compressed image data together with +information about data layout, image size and pixel format. Populated instances +of this class are returned from @ref AbstractImporter::image1D(), +@relativeref{AbstractImporter,image2D()}, +@relativeref{AbstractImporter,image3D()}, +@ref AbstractImageConverter::convert() and other APIs. + Used mainly by @ref AbstractImporter classes to store either compressed or non-compressed multi-dimensional image data together with layout and pixel format description. @@ -66,29 +73,20 @@ Particular graphics API wrappers provide additional image classes, for example @section Trade-ImageData-usage Basic usage -The image usually comes out of @ref AbstractImporter::image1D(), -@ref AbstractImporter::image2D() "image2D()" or -@ref AbstractImporter::image3D() "image3D()" and, based on what format the -particular imported data is in, it stores either compressed or uncompressed -data. - -@snippet MagnumTrade.cpp ImageData-construction - -@snippet MagnumTrade.cpp ImageData-construction-compressed - -As with @ref Image / @ref ImageView, this class supports extra storage -parameters and implementation-specific format specification, if the importer -has a need for that. See the @ref ImageView documentation for more information. - -When using the image, its compression status can be distinguished using -@ref isCompressed(). Uncompressed image properties are available through -@ref storage(), @ref format(), @ref formatExtra() and @ref pixelSize(); +Based on whether the @ref ImageData has an uncompressed or compressed pixel +format, it behaves either like an @ref Image / @ref ImageView or like a +@ref CompressedImage / @ref CompressedImageView. It can be distinguished using +@ref isCompressed(); uncompressed image properties are then available through +@ref storage(), @ref format(), @ref formatExtra() and @ref pixelSize(), compressed properties through @ref compressedStorage() and -@ref compressedFormat(). Example of uploading the image to +@ref compressedFormat(). Example of uploading the image to a @link GL::Texture @endlink: @snippet MagnumTrade.cpp ImageData-usage +Uncompressed image data instances provide pixel data access via @ref pixels() +in the same way as the @ref Image class. See @ref Image-pixel-access "its documentation for more information". + @section Trade-ImageData-usage-mutable Mutable data access The interfaces implicitly provide @cpp const @ce views on the contained @@ -101,8 +99,44 @@ 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, - @ref Image-pixel-views +@section Trade-ImageData-populating Populating an instance + +An @ref ImageData instance by default takes over the ownership of an +@relativeref{Corrade,Containers::Array} containing the pixel data together +with size and either @ref PixelFormat or @ref CompressedPixelFormat, similarly +to the @ref Image and @ref CompressedImage classes: + +@snippet MagnumTrade.cpp ImageData-populating + +The constructor internally checks that the passed array is large enough and as +with other image classes, care must be taken in presence of +non-four-byte-aligned rows. This often closely depends on behavior of the code +or library that operates with the image data and the recommended way is to pad +the row data to satisfy the alignment: + +@snippet MagnumTrade.cpp ImageData-populating-padding + +Alternatively, if padding is not possible or desirable, you can pass a +@ref PixelStorage instance with the alignment overriden to @cpp 1 @ce: + +@snippet MagnumTrade.cpp ImageData-populating-alignment + +As with @ref Image / @ref ImageView, this class supports extra storage +parameters and implementation-specific format specification, if the importer +has a need for that. See the @ref ImageView documentation for more information. + +@subsection Trade-ImageData-populating-non-owned Non-owned instances + +In some cases you may want the @ref ImageData instance to only refer to +external data without taking ownership, for example with a memory-mapped file, +global data etc. For that, instead of moving in an +@relativeref{Corrade,Containers::Array}, pass @ref DataFlags describing data +mutability and ownership together with an +@relativeref{Corrade,Containers::ArrayView}: + +@snippet MagnumTrade.cpp ImageData-populating-non-owned + +@see @ref ImageData1D, @ref ImageData2D, @ref ImageData3D */ template class ImageData { public: @@ -802,7 +836,7 @@ template class ImageData { * the image is not compressed. The last dimension represents the * actual data type (its size is equal to type size) and is guaranteed * to be contiguous. Use the templated overload below to get pixels in - * a concrete type. See @ref Image-pixel-views for more information. + * a concrete type. See @ref Image-pixel-access for more information. * @see @ref isCompressed(), * @ref Corrade::Containers::StridedArrayView::isContiguous() */ @@ -813,7 +847,8 @@ template class ImageData { * @m_since{2020,06} * * Like @ref pixels() const, but returns a non-const view. Expects that - * the image is mutable. + * the image is mutable. See also @ref Image-pixel-access for more + * information. * @see @ref dataFlags() */ Containers::StridedArrayView mutablePixels(); @@ -825,7 +860,8 @@ template class ImageData { * 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. + * side is not possible for the general case. See also + * @ref Image-pixel-access for more information. */ template Containers::StridedArrayView pixels() const { /* Deliberately not adding a StridedArrayView include, it should