mirror of https://github.com/mosra/magnum.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
595 lines
26 KiB
595 lines
26 KiB
#ifndef Magnum_TextureTools_Atlas_h |
|
#define Magnum_TextureTools_Atlas_h |
|
/* |
|
This file is part of Magnum. |
|
|
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
|
2020, 2021, 2022, 2023 Vladimír Vondruš <mosra@centrum.cz> |
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a |
|
copy of this software and associated documentation files (the "Software"), |
|
to deal in the Software without restriction, including without limitation |
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
|
and/or sell copies of the Software, and to permit persons to whom the |
|
Software is furnished to do so, subject to the following conditions: |
|
|
|
The above copyright notice and this permission notice shall be included |
|
in all copies or substantial portions of the Software. |
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
DEALINGS IN THE SOFTWARE. |
|
*/ |
|
|
|
/** @file |
|
* @brief Class @ref Magnum::TextureTools::AtlasLandfill, @ref Magnum::TextureTools::AtlasLandfillArray, enum @ref Magnum::TextureTools::AtlasLandfillFlag, enum set @ref Magnum::TextureTools::AtlasLandfillFlags, function @ref Magnum::TextureTools::atlas(), @ref Magnum::TextureTools::atlasArrayPowerOfTwo() |
|
*/ |
|
|
|
#include <Corrade/Containers/Pointer.h> |
|
|
|
#include "Magnum/Magnum.h" |
|
#include "Magnum/Math/Vector2.h" |
|
#include "Magnum/TextureTools/visibility.h" |
|
|
|
#ifdef MAGNUM_BUILD_DEPRECATED |
|
#include <Corrade/Utility/Macros.h> |
|
#include <Corrade/Utility/StlForwardVector.h> |
|
#endif |
|
|
|
namespace Magnum { namespace TextureTools { |
|
|
|
namespace Implementation { |
|
struct AtlasLandfillState; |
|
} |
|
|
|
/** |
|
@brief Landfill texture atlas packer behavior flag |
|
@m_since_latest |
|
|
|
@see @ref AtlasLandfillFlags, @ref AtlasLandfill::setFlags(), |
|
@ref AtlasLandfill::addFlags(), @ref AtlasLandfill::clearFlags(), |
|
@ref AtlasLandfillArray::setFlags(), @ref AtlasLandfillArray::addFlags(), |
|
@ref AtlasLandfillArray::clearFlags() |
|
*/ |
|
enum class AtlasLandfillFlag { |
|
/** |
|
* Rotate all textures to a portrait orientation. Only one of |
|
* @ref AtlasLandfillFlag::RotatePortrait and |
|
* @relativeref{AtlasLandfillFlag,RotateLandscape} can be set. If neither |
|
* is set, keeps the original orientation. |
|
*/ |
|
RotatePortrait = 1 << 0, |
|
|
|
/** |
|
* Rotate all textures to a landscape orientation. Only one of |
|
* @ref AtlasLandfillFlag::RotatePortrait and |
|
* @relativeref{AtlasLandfillFlag,RotateLandscape} can be set. If neither |
|
* is set, keeps the original orientation. |
|
*/ |
|
RotateLandscape = 1 << 1, |
|
|
|
/** |
|
* Sort same-height textures widest first. Only one of |
|
* @ref AtlasLandfillFlag::WidestFirst and |
|
* @relativeref{AtlasLandfillFlag,NarrowestFirst} can be set. If neither is |
|
* set, textures of the same height keep their original order. |
|
*/ |
|
WidestFirst = 1 << 2, |
|
|
|
/** |
|
* Sort same-height textures narrowest first. Only one of |
|
* @ref AtlasLandfillFlag::WidestFirst and |
|
* @relativeref{AtlasLandfillFlag,NarrowestFirst} can be set. If neither is |
|
* set, textures of the same height keep their original order. |
|
*/ |
|
NarrowestFirst = 1 << 3 |
|
}; |
|
|
|
/** @debugoperatorenum{AtlasLandfillFlag} */ |
|
MAGNUM_TEXTURETOOLS_EXPORT Debug& operator<<(Debug& output, AtlasLandfillFlag value); |
|
|
|
/** |
|
@brief Landfill texture atlas packer behavior flags |
|
@m_since_latest |
|
|
|
@see @ref Flags, @ref AtlasLandfill::setFlags(), @ref AtlasLandfill::addFlags(), |
|
@ref AtlasLandfill::clearFlags(), @ref AtlasLandfillArray::setFlags(), |
|
@ref AtlasLandfillArray::addFlags(), @ref AtlasLandfillArray::clearFlags() |
|
*/ |
|
typedef Containers::EnumSet<AtlasLandfillFlag> AtlasLandfillFlags; |
|
|
|
CORRADE_ENUMSET_OPERATORS(AtlasLandfillFlags) |
|
|
|
/** @debugoperatorenum{AtlasLandfillFlags} */ |
|
MAGNUM_TEXTURETOOLS_EXPORT Debug& operator<<(Debug& output, AtlasLandfillFlags value); |
|
|
|
/** |
|
@brief Landfill texture atlas packer |
|
@m_since_latest |
|
|
|
Keeps track of currently filled height at every pixel with the aim to fill the |
|
available space bottom-up as evenly as possible. Packs to a 2D texture with the |
|
height optionally unbounded. See @ref AtlasLandfillArray for a variant that |
|
works with 2D texture arrays, and @ref atlasArrayPowerOfTwo() for a variant |
|
that always provides optimal packing for power-of-two sizes. |
|
|
|
@htmlinclude atlas-landfill.svg |
|
|
|
* *The Trash Algorithm.* Naming credit goes to [\@lacyyy](https://github.com/lacyyy). |
|
|
|
@section TextureTools-AtlasLandfill-usage Example usage |
|
|
|
The following snippets shows packing a list of images into an atlas with the |
|
width set to 1024 and height unbounded. The algorithm by default makes all |
|
images the same orientation as that significantly improves the layout |
|
efficiency while not making any difference for texture mapping. |
|
|
|
@snippet MagnumTextureTools.cpp AtlasLandfill-usage |
|
|
|
If rotations are undesirable, for example if the resulting atlas is used by a |
|
linear rasterizer later, they can be disabled by clearing appropriate |
|
@ref AtlasLandfillFlags. The process can then also use the |
|
@ref add(const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector2i>&) |
|
overload without the rotations argument. |
|
|
|
@snippet MagnumTextureTools.cpp AtlasLandfill-usage-no-rotation |
|
|
|
@section TextureTools-AtlasLandfill-process Packing process |
|
|
|
On every @ref add(), the algorithm first makes all sizes the same orientation |
|
depending on @ref AtlasLandfillFlag::RotatePortrait or |
|
@relativeref{AtlasLandfillFlag,RotateLandscape} being set and sorts the sizes |
|
highest first and then depending on @ref AtlasLandfillFlag::WidestFirst or |
|
@relativeref{AtlasLandfillFlag,NarrowestFirst} being set. |
|
|
|
A per-pixel array of currently filled `heights`, initially all @cpp 0 @ce, and |
|
a horizontal insertion `cursor`, initially @cpp 0 @ce, is maintained. An item |
|
of given `size` gets placed at a `height` that's |
|
@cpp max(heights[cursor], heights[cursor + size.x]) @ce, this range gets then |
|
set to `height + size.y` and the cursor is updated to `cursor + size.x`. If |
|
cursor reaches the edge that an item cannot fit there anymore, it's reset to |
|
@cpp 0 @ce and the process continues again in the opposite direction. With the |
|
assumption that the texture sizes are uniformly distributed, this results in a |
|
fairly leveled out height. The process is aborted if the atlas height is |
|
bounded and the next item cannot fit there anymore. |
|
|
|
The sort is performed using @ref std::stable_sort(), which is usually |
|
@f$ \mathcal{O}(n \log{} n) @f$, the actual atlasing is a single |
|
@f$ \mathcal{O}(n) @f$ operation. Memory complexity is |
|
@f$ \mathcal{O}(n + w) @f$ with @f$ n @f$ being a sorted copy of the input size |
|
array and @f$ w @f$ being a 16-bit integer for every pixel of atlas width, |
|
additionally @ref std::stable_sort() performs its own allocation. |
|
|
|
@section TextureTools-AtlasLandfill-incremental Incremental population |
|
|
|
It's possible to call @ref add() multiple times in order to incrementally fill |
|
the atlas with new data as much as the atlas height (if bounded) allows. In an |
|
ideal scenario, if the previous fill resulted in an uniform height the newly |
|
added data will be added in an optimal way as well, but in practice calling |
|
@ref add() with all data just once will always result in a more optimal |
|
packing than an incremental one. |
|
*/ |
|
class MAGNUM_TEXTURETOOLS_EXPORT AtlasLandfill { |
|
public: |
|
/** |
|
* @brief Constructor |
|
* |
|
* The @p size is expected to have non-zero width, and height not |
|
* larger than 65536. If height is zero, the dimension is treated as |
|
* unbounded, i.e. @ref add() never fails. |
|
*/ |
|
explicit AtlasLandfill(const Vector2i& size); |
|
|
|
/** @brief Copying is not allowed */ |
|
AtlasLandfill(const AtlasLandfill&) = delete; |
|
|
|
/** @brief Move constructor */ |
|
AtlasLandfill(AtlasLandfill&&) noexcept; |
|
|
|
~AtlasLandfill(); |
|
|
|
/** @brief Copying is not allowed */ |
|
AtlasLandfill& operator=(const AtlasLandfill&) = delete; |
|
|
|
/** @brief Move assignment */ |
|
AtlasLandfill& operator=(AtlasLandfill&&) noexcept; |
|
|
|
/** |
|
* @brief Atlas size specified in constructor |
|
* |
|
* @see @ref filledSize() |
|
*/ |
|
Vector2i size() const; |
|
|
|
/** |
|
* @brief Currently filled size |
|
* |
|
* Width is always taken from @ref size(). The height is @cpp 0 @ce |
|
* initially, and at most the height of @ref size() if it's bounded. |
|
* The size is calculated with a @f$ \mathcal{O}(w) @f$ complexity, |
|
* with @f$ w @f$ being the atlas width. |
|
*/ |
|
Vector2i filledSize() const; |
|
|
|
/** |
|
* @brief Behavior flags |
|
* |
|
* Default is @ref AtlasLandfillFlag::RotatePortrait and |
|
* @relativeref{AtlasLandfillFlag,WidestFirst}. |
|
*/ |
|
AtlasLandfillFlags flags() const; |
|
|
|
/** |
|
* @brief Set behavior flags |
|
* |
|
* Can be called with different values before each particular |
|
* @ref add(). |
|
* @see @ref addFlags(), @ref clearFlags() |
|
*/ |
|
AtlasLandfill& setFlags(AtlasLandfillFlags flags); |
|
|
|
/** |
|
* @brief Add behavior flags |
|
* |
|
* Calls @ref setFlags() with the existing flags ORed with @p flags. |
|
* Useful for preserving the defaults. |
|
* @see @ref clearFlags() |
|
*/ |
|
AtlasLandfill& addFlags(AtlasLandfillFlags flags) { |
|
return setFlags(this->flags()|flags); |
|
} |
|
|
|
/** |
|
* @brief Clear behavior flags |
|
* |
|
* Calls @ref setFlags() with the existing flags ANDed with the inverse |
|
* of @p flags. Useful for preserving the defaults. |
|
* @see @ref addFlags() |
|
*/ |
|
AtlasLandfill& clearFlags(AtlasLandfillFlags flags) { |
|
return setFlags(this->flags() & ~flags); |
|
} |
|
|
|
/** |
|
* @brief Padding around each texture |
|
* |
|
* Default is a zero vector. |
|
*/ |
|
Vector2i padding() const; |
|
|
|
/** |
|
* @brief Set padding around each texture |
|
* |
|
* Sizes are extended with twice the padding value before placement but |
|
* the returned offsets are without padding again. In order to have |
|
* @ref AtlasLandfillFlag::RotatePortrait and |
|
* @relativeref{AtlasLandfillFlag,RotateLandscape} work well also |
|
* with non-uniform padding, the padding is applied *before* a |
|
* potential rotation. I.e., the horizontal padding value is always |
|
* applied on input image width independently on how it's rotated |
|
* after. If you need different behavior, disable rotations with |
|
* @ref clearFlags() or pre-pad the input sizes directly instead of |
|
* using this function. |
|
* |
|
* Can be called with different values before each particular |
|
* @ref add(). |
|
*/ |
|
AtlasLandfill& setPadding(const Vector2i& padding); |
|
|
|
/** |
|
* @brief Add textures to the atlas |
|
* @param[in] sizes Texture sizes |
|
* @param[out] offsets Resulting offsets in the atlas |
|
* @param[out] rotations Which textures got rotated |
|
* |
|
* The @p sizes, @p offsets and @p rotations views are expected to have |
|
* the same size. The @p sizes are all expected to be non-zero and not |
|
* larger than @ref size() after appying padding and then a rotation |
|
* based on @ref AtlasLandfillFlag::RotatePortrait or |
|
* @relativeref{AtlasLandfillFlag,RotateLandscape} being set. If |
|
* neither @relativeref{AtlasLandfillFlag,RotatePortrait} nor |
|
* @relativeref{AtlasLandfillFlag,RotateLandscape} is set, the |
|
* @p rotations view can be also empty or you can use the |
|
* @ref add(const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector2i>&) |
|
* overload. The @p offsets always point to the original potentially |
|
* rotated sizes without padding applied. |
|
* |
|
* On success returns @cpp true @ce and updates @ref filledSize(). If |
|
* @ref size() is bounded, can return @cpp false @ce if the items |
|
* didn't fit, in which case the internals and contents of @p offsets |
|
* and @p rotations are left in an undefined state. For an unbounded |
|
* @ref size() returns @cpp true @ce always. |
|
* @see @ref setFlags(), @ref setPadding() |
|
*/ |
|
bool add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector2i>& offsets, Containers::MutableBitArrayView rotations); |
|
|
|
/** @overload */ |
|
bool add(std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector2i>& offsets, Containers::MutableBitArrayView rotations); |
|
|
|
/** |
|
* @brief Add textures to the atlas with rotations disabled |
|
* |
|
* Equivalent to calling @ref add(const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector2i>&, Containers::MutableBitArrayView) |
|
* with the @p rotations view being empty. Can be called only if |
|
* neither @ref AtlasLandfillFlag::RotatePortrait nor |
|
* @relativeref{AtlasLandfillFlag,RotateLandscape} is set. |
|
* @see @ref clearFlags() |
|
*/ |
|
bool add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector2i>& offsets); |
|
|
|
/** @overload */ |
|
bool add(std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector2i>& offsets); |
|
|
|
private: |
|
Containers::Pointer<Implementation::AtlasLandfillState> _state; |
|
}; |
|
|
|
/** |
|
@brief Landfill texture atlas packer |
|
@m_since_latest |
|
|
|
Extends @ref AtlasLandfill to a third dimension. Instead of expanding to an |
|
unbounded height, on overflow a new texture slice is made. See also |
|
@ref atlasArrayPowerOfTwo() for a variant that always provides optimal packing |
|
for power-of-two sizes. |
|
|
|
@section TextureTools-AtlasLandfillArray-usage Example usage |
|
|
|
Compared to the @ref TextureTools-AtlasLandfill-usage "2D usage" it's extended |
|
to three dimensions: |
|
|
|
@snippet MagnumTextureTools.cpp AtlasLandfillArray-usage |
|
|
|
@section TextureTools-AtlasLandfillArray-process Packing process |
|
|
|
Apart from expanding to new slices on height overflow, the underlying process |
|
is @ref TextureTools-AtlasLandfill-process "the same as in AtlasLandfill". |
|
|
|
In this case, memory complexity is @f$ \mathcal{O}(n + wd) @f$ with @f$ n @f$ |
|
being a sorted copy of the input size array and @f$ wd @f$ being a 16-bit |
|
integer for every pixel of atlas width times atlas depth. |
|
|
|
@section TextureTools-AtlasLandfillArray-incremental Incremental population |
|
|
|
Compared to the @ref TextureTools-AtlasLandfill-incremental "2D incremental population", |
|
the incremental process always starts from the first slice, finding the first |
|
that can fit the first (sorted) item. Then it attempts to place as many items |
|
as possible and on overflow continues searching for the next slice that can fit |
|
the first remaining item. If all slices are exhausted, adds a new one. |
|
*/ |
|
class MAGNUM_TEXTURETOOLS_EXPORT AtlasLandfillArray { |
|
public: |
|
/** |
|
* @brief Constructor |
|
* |
|
* The @p size has to have non-zero width and height. If depth is |
|
* @cpp 0 @ce, the dimension is treated as unbounded, i.e. @ref add() |
|
* never fails. If depth is @cpp 1 @ce, behaves the same as |
|
* @ref AtlasLandfillArray with a bounded height. |
|
*/ |
|
explicit AtlasLandfillArray(const Vector3i& size); |
|
|
|
/** @brief Copying is not allowed */ |
|
AtlasLandfillArray(const AtlasLandfillArray&) = delete; |
|
|
|
/** @brief Move constructor */ |
|
AtlasLandfillArray(AtlasLandfillArray&&) noexcept; |
|
|
|
~AtlasLandfillArray(); |
|
|
|
/** @brief Copying is not allowed */ |
|
AtlasLandfillArray& operator=(const AtlasLandfillArray&) = delete; |
|
|
|
/** @brief Move assignment */ |
|
AtlasLandfillArray& operator=(AtlasLandfillArray&&) noexcept; |
|
|
|
/** |
|
* @brief Atlas size specified in constructor |
|
* |
|
* @see @ref filledSize() |
|
*/ |
|
Vector3i size() const; |
|
|
|
/** |
|
* @brief Currently filled size |
|
* |
|
* Width and height is always taken from @ref size(). The depth is |
|
* @cpp 0 @ce initially, and at most @ref size() depth if the size is |
|
* bounded. |
|
*/ |
|
Vector3i filledSize() const; |
|
|
|
/** @brief Behavior flags */ |
|
AtlasLandfillFlags flags() const; |
|
|
|
/** |
|
* @brief Set behavior flags |
|
* |
|
* Can be called with different values before each particular |
|
* @ref add(). Default is @ref AtlasLandfillFlag::RotatePortrait. |
|
* @see @ref addFlags(), @ref clearFlags() |
|
*/ |
|
AtlasLandfillArray& setFlags(AtlasLandfillFlags flags); |
|
|
|
/** |
|
* @brief Add behavior flags |
|
* |
|
* Calls @ref setFlags() with the existing flags ORed with @p flags. |
|
* Useful for preserving the defaults. |
|
* @see @ref clearFlags() |
|
*/ |
|
AtlasLandfillArray& addFlags(AtlasLandfillFlags flags) { |
|
return setFlags(this->flags()|flags); |
|
} |
|
|
|
/** |
|
* @brief Clear behavior flags |
|
* |
|
* Calls @ref setFlags() with the existing flags ANDed with the inverse |
|
* of @p flags. Useful for preserving the defaults. |
|
* @see @ref addFlags() |
|
*/ |
|
AtlasLandfillArray& clearFlags(AtlasLandfillFlags flags) { |
|
return setFlags(this->flags() & ~flags); |
|
} |
|
|
|
/** |
|
* @brief Padding around each texture |
|
* |
|
* Default is a zero vector. |
|
*/ |
|
Vector2i padding() const; |
|
|
|
/** |
|
* @brief Set padding around each texture |
|
* |
|
* Sizes are extended with twice the padding value before placement but |
|
* the returned offsets are without padding again. The third dimension |
|
* isn't treated in any special way. In order to have |
|
* @ref AtlasLandfillFlag::RotatePortrait and |
|
* @relativeref{AtlasLandfillFlag,RotateLandscape} work well also |
|
* with non-uniform padding, the padding is applied *before* a |
|
* potential rotation. I.e., the horizontal padding value is always |
|
* applied on input image width independently on how it's rotated |
|
* after. If you need different behavior, disable rotations with |
|
* @ref clearFlags() or pre-pad the input sizes directly instead of |
|
* using this function. |
|
* |
|
* Can be called with different values before each particular |
|
* @ref add(). |
|
*/ |
|
AtlasLandfillArray& setPadding(const Vector2i& padding); |
|
|
|
/** |
|
* @brief Add textures to the atlas |
|
* @param[in] sizes Texture sizes |
|
* @param[out] offsets Resulting offsets in the atlas |
|
* @param[out] rotations Which textures got rotated |
|
* |
|
* The @p sizes, @p offsets and @p rotations views are expected to have |
|
* the same size. The @p sizes are all expected to be non-zero and not |
|
* larger than @ref size() after applying padding and then a rotation |
|
* based on @ref AtlasLandfillFlag::RotatePortrait or |
|
* @relativeref{AtlasLandfillFlag,RotateLandscape} being set. If |
|
* neither @relativeref{AtlasLandfillFlag,RotatePortrait} nor |
|
* @relativeref{AtlasLandfillFlag,RotateLandscape} is set, the |
|
* @p rotations view can be also empty or you can use the |
|
* @ref add(const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector3i>&) |
|
* overload. The @p offsets always point to the original potentially |
|
* rotated sizes without padding applied. |
|
* |
|
* On success returns @cpp true @ce and updates @ref filledSize(). If |
|
* @ref size() is bounded, can return @cpp false @ce if the items |
|
* didn't fit, in which case the internals and contents of @p offsets |
|
* and @p rotations are left in an undefined state. For an unbounded |
|
* @ref size() returns @cpp true @ce always. |
|
* @see @ref setFlags(), @ref setPadding() |
|
*/ |
|
bool add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector3i>& offsets, Containers::MutableBitArrayView rotations); |
|
|
|
/** @overload */ |
|
bool add(std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector3i>& offsets, Containers::MutableBitArrayView rotations); |
|
|
|
/** |
|
* @brief Add textures to the atlas with rotations disabled |
|
* |
|
* Equivalent to calling @ref add(const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector3i>&, Containers::MutableBitArrayView) |
|
* with the @p rotations view being empty. Can be called only if |
|
* neither @ref AtlasLandfillFlag::RotatePortrait nor |
|
* @relativeref{AtlasLandfillFlag,RotateLandscape} is set. |
|
* @see @ref clearFlags() |
|
*/ |
|
bool add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector3i>& offsets); |
|
|
|
/** @overload */ |
|
bool add(std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector3i>& offsets); |
|
|
|
private: |
|
Containers::Pointer<Implementation::AtlasLandfillState> _state; |
|
}; |
|
|
|
#ifdef MAGNUM_BUILD_DEPRECATED |
|
/** |
|
@brief Pack textures into a texture atlas |
|
@param atlasSize Size of the resulting atlas |
|
@param sizes Sizes of all textures in the atlas |
|
@param padding Padding around each texture |
|
|
|
Packs many small textures into one larger. If the textures cannot be packed |
|
into required size, an empty vector is returned. |
|
|
|
Padding is added twice to each size and the atlas is laid out so the padding |
|
don't overlap. Returned sizes are the same as original sizes, i.e. without the |
|
padding. |
|
|
|
@m_deprecated_since_latest Use the @ref AtlasLandfill class, which has a vastly |
|
better packing efficiency, supports incremental packing and doesn't force |
|
the caller to use a @ref std::vector. |
|
*/ |
|
MAGNUM_TEXTURETOOLS_EXPORT CORRADE_DEPRECATED("use the AtlasLandfill class instead") std::vector<Range2Di> atlas(const Vector2i& atlasSize, const std::vector<Vector2i>& sizes, const Vector2i& padding = {}); |
|
#endif |
|
|
|
/** |
|
@brief Pack square power-of-two textures into a texture atlas array |
|
@param[in] layerSize Size of a single layer in the texture atlas |
|
@param[in] sizes Sizes of all textures in the atlas |
|
@param[out] offsets Resulting offsets in the atlas |
|
@return Total layer count |
|
@m_since_latest |
|
|
|
The @p sizes and @p offsets views are expected to have the same size. The |
|
@p layerSize is expected to be non-zero, square and power-of-two. All items in |
|
@p sizes are expected to be non-zero, square, power-of-two and not larger than |
|
@p layerSize. With such constraints the packing is optimal with no wasted space |
|
in all but the last layer. Setting @p layerSize to the size of the largest |
|
texture in the set will lead to the least wasted space in the last layer. |
|
|
|
@htmlinclude atlas-array-power-of-two.svg |
|
|
|
Example usage is shown below. |
|
|
|
@snippet MagnumTextureTools.cpp atlasArrayPowerOfTwo |
|
|
|
The algorithm first sorts the textures by size using @ref std::stable_sort(), |
|
which is usually @f$ \mathcal{O}(n \log{} n) @f$, and then performs the actual |
|
atlasing in a single @f$ \mathcal{O}(n) @f$ operation. Memory complexity is |
|
@f$ \mathcal{O}(n) @f$ with @f$ n @f$ being a sorted copy of the input size |
|
array, additionally @ref std::stable_sort() performs its own allocation. See |
|
the [Zero-waste single-pass packing of power-of-two textures](https://blog.magnum.graphics/backstage/pot-array-packing/) |
|
article for a detailed description of the algorithm. |
|
|
|
See the @ref AtlasLandfill and @ref AtlasLandfillArray classes for an |
|
alternative that isn't restricted to power-of-two sizes and can be used in an |
|
incremental way but doesn't always produce optimal packing. |
|
*/ |
|
MAGNUM_TEXTURETOOLS_EXPORT Int atlasArrayPowerOfTwo(const Vector2i& layerSize, const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector3i>& offsets); |
|
|
|
/** |
|
* @overload |
|
* @m_since_latest |
|
*/ |
|
MAGNUM_TEXTURETOOLS_EXPORT Int atlasArrayPowerOfTwo(const Vector2i& layerSize, std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector3i>& offsets); |
|
|
|
#ifdef MAGNUM_BUILD_DEPRECATED |
|
/** |
|
@brief @copybrief atlasArrayPowerOfTwo(const Vector2i&, const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector3i>&) |
|
@m_deprecated_since_latest Use @ref atlasArrayPowerOfTwo(const Vector2i&, const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector3i>&) |
|
instead. |
|
*/ |
|
MAGNUM_TEXTURETOOLS_EXPORT CORRADE_DEPRECATED("use the overload taking offsets as an output view instead") Containers::Pair<Int, Containers::Array<Vector3i>> atlasArrayPowerOfTwo(const Vector2i& layerSize, const Containers::StridedArrayView1D<const Vector2i>& sizes); |
|
|
|
/** |
|
@overload |
|
@m_deprecated_since_latest Use @ref atlasArrayPowerOfTwo(const Vector2i&, std::initializer_list<Vector2i>, const Containers::StridedArrayView1D<Vector3i>&) |
|
instead. |
|
*/ |
|
MAGNUM_TEXTURETOOLS_EXPORT CORRADE_DEPRECATED("use the overload taking offsets as an output view instead") Containers::Pair<Int, Containers::Array<Vector3i>> atlasArrayPowerOfTwo(const Vector2i& layerSize, std::initializer_list<Vector2i> sizes); |
|
#endif |
|
|
|
}} |
|
|
|
#endif
|
|
|