Browse Source

TextureTools: return the filled range from AtlasLandfill::add().

Because otherwise the users likely have to do something similar on their
end to perform a texture upload etc., which means they'll either take
the easy path and upload everything including the unused area, or they
introduce various bugs in the process, leading to random artifacts,
especially when it comes to padding.

Which is exactly what I think is causing random test failures in the Ui
library text rendering, because the glyph cache filling process in
plugins is calculating the rectangle too tight, without considering
padding. Gonna fix that now.
pull/651/merge
Vladimír Vondruš 1 year ago
parent
commit
b5325ab0ac
  1. 1
      doc/snippets/TextureTools.cpp
  2. 2
      src/Magnum/Text/AbstractGlyphCache.cpp
  3. 49
      src/Magnum/TextureTools/Atlas.cpp
  4. 38
      src/Magnum/TextureTools/Atlas.h
  5. 72
      src/Magnum/TextureTools/Test/AtlasTest.cpp

1
doc/snippets/TextureTools.cpp

@ -36,6 +36,7 @@
#include "Magnum/Math/Color.h" #include "Magnum/Math/Color.h"
#include "Magnum/Math/FunctionsBatch.h" #include "Magnum/Math/FunctionsBatch.h"
#include "Magnum/Math/Matrix3.h" #include "Magnum/Math/Matrix3.h"
#include "Magnum/Math/Range.h"
#include "Magnum/MeshTools/Transform.h" #include "Magnum/MeshTools/Transform.h"
#include "Magnum/TextureTools/Atlas.h" #include "Magnum/TextureTools/Atlas.h"
#include "Magnum/Trade/MaterialData.h" #include "Magnum/Trade/MaterialData.h"

2
src/Magnum/Text/AbstractGlyphCache.cpp

@ -302,7 +302,7 @@ std::vector<Range2Di> AbstractGlyphCache::reserve(const std::vector<Vector2i>& s
for(std::size_t i = 0; i != sizes.size(); ++i) for(std::size_t i = 0; i != sizes.size(); ++i)
out[i].max() = sizes[i]; out[i].max() = sizes[i];
const bool succeeded = state.atlas.add( const bool succeeded = !!state.atlas.add(
Containers::stridedArrayView(out).slice(&Range2Di::max), Containers::stridedArrayView(out).slice(&Range2Di::max),
Containers::stridedArrayView(out).slice(&Range2Di::min)); Containers::stridedArrayView(out).slice(&Range2Di::min));

49
src/Magnum/TextureTools/Atlas.cpp

@ -31,17 +31,17 @@
#include <Corrade/Containers/BitArrayView.h> #include <Corrade/Containers/BitArrayView.h>
#include <Corrade/Containers/EnumSet.hpp> #include <Corrade/Containers/EnumSet.hpp>
#include <Corrade/Containers/GrowableArray.h> #include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Pair.h> #include <Corrade/Containers/Pair.h>
#include <Corrade/Containers/StridedArrayView.h> #include <Corrade/Containers/StridedArrayView.h>
#include "Magnum/Math/Matrix3.h" #include "Magnum/Math/Matrix3.h"
#include "Magnum/Math/Functions.h" #include "Magnum/Math/Functions.h"
#include "Magnum/Math/FunctionsBatch.h" #include "Magnum/Math/FunctionsBatch.h"
#include "Magnum/Math/Range.h"
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
#include <vector> #include <vector>
#include "Magnum/Math/Range.h"
#endif #endif
namespace Magnum { namespace TextureTools { namespace Magnum { namespace TextureTools {
@ -96,7 +96,7 @@ struct AtlasLandfillState {
namespace { namespace {
bool atlasLandfillAddSortedFlipped(Implementation::AtlasLandfillState& state, const Int slice, const Containers::StridedArrayView1D<const Containers::Pair<Vector2i, UnsignedInt>> sortedFlippedSizes, const Containers::StridedArrayView1D<Vector2i> offsets, const Containers::StridedArrayView1D<Int> zOffsets, const Containers::BitArrayView rotations) { Containers::Optional<Range3Di> atlasLandfillAddSortedFlipped(Implementation::AtlasLandfillState& state, const Int slice, const Containers::StridedArrayView1D<const Containers::Pair<Vector2i, UnsignedInt>> sortedFlippedSizes, const Containers::StridedArrayView1D<Vector2i> offsets, const Containers::StridedArrayView1D<Int> zOffsets, const Containers::BitArrayView rotations) {
/* Add a new slice if not there yet, extend the yOffsets array */ /* Add a new slice if not there yet, extend the yOffsets array */
if(UnsignedInt(slice) >= state.slices.size()) { if(UnsignedInt(slice) >= state.slices.size()) {
CORRADE_INTERNAL_ASSERT(UnsignedInt(slice) == state.slices.size()); CORRADE_INTERNAL_ASSERT(UnsignedInt(slice) == state.slices.size());
@ -117,6 +117,7 @@ bool atlasLandfillAddSortedFlipped(Implementation::AtlasLandfillState& state, co
if(sliceState.direction == -1) if(sliceState.direction == -1)
sliceYOffsets = sliceYOffsets.flipped<0>(); sliceYOffsets = sliceYOffsets.flipped<0>();
Range3Di range;
std::size_t i; std::size_t i;
for(i = 0; i != sortedFlippedSizes.size(); ++i) { for(i = 0; i != sortedFlippedSizes.size(); ++i) {
const Vector2i size = sortedFlippedSizes[i].first(); const Vector2i size = sortedFlippedSizes[i].first();
@ -161,11 +162,15 @@ bool atlasLandfillAddSortedFlipped(Implementation::AtlasLandfillState& state, co
/* Save the position (X-flip it in case we're in reverse direction), /* Save the position (X-flip it in case we're in reverse direction),
add the (appropriately rotated) padding to it so it points to the add the (appropriately rotated) padding to it so it points to the
original unpadded size */ original unpadded size */
offsets[index] = padding + Vector2i{ const Vector2i offset{
sliceState.direction > 0 ? sliceState.xOffset : sliceState.direction > 0 ? sliceState.xOffset :
state.size.x() - sliceState.xOffset - size.x(), state.size.x() - sliceState.xOffset - size.x(),
placementYOffset placementYOffset};
}; offsets[index] = padding + offset;
/* Add this item to the range spanning all added items, including the
(potentially rotated) padding */
range = join(range, Range3Di::fromSize({offset, slice}, {size, 1}));
/* Advance to the next X offset */ /* Advance to the next X offset */
sliceState.xOffset += size.x(); sliceState.xOffset += size.x();
@ -179,13 +184,18 @@ bool atlasLandfillAddSortedFlipped(Implementation::AtlasLandfillState& state, co
/* If there are items that didn't fit, recurse to the next slice. This /* If there are items that didn't fit, recurse to the next slice. This
should only happen if the Y size is bounded. */ should only happen if the Y size is bounded. */
if(i < sortedFlippedSizes.size()) { if(i < sortedFlippedSizes.size()) {
/* If there are no more slices, fail */
if(slice + 1 == state.size.z()) if(slice + 1 == state.size.z())
return false; return {};
return atlasLandfillAddSortedFlipped(state, slice + 1, sortedFlippedSizes.exceptPrefix(i), offsets, zOffsets, rotations); /* If the recursion succeeded, return the two ranges joined */
if(const Containers::Optional<Range3Di> out = atlasLandfillAddSortedFlipped(state, slice + 1, sortedFlippedSizes.exceptPrefix(i), offsets, zOffsets, rotations))
return Range3Di{join(range, *out)};
/* If it didn't, fail */
return {};
} }
/* Everything fit, success */ /* Everything fit, success */
return true; return range;
} }
} }
@ -252,7 +262,7 @@ AtlasLandfill& AtlasLandfill::setFlags(AtlasLandfillFlags flags) {
namespace { namespace {
bool atlasLandfillAdd(Implementation::AtlasLandfillState& state, const Containers::StridedArrayView1D<const Vector2i> sizes, const Containers::StridedArrayView1D<Vector2i> offsets, const Containers::StridedArrayView1D<Int> zOffsets, const Containers::MutableBitArrayView rotations) { Containers::Optional<Range3Di> atlasLandfillAdd(Implementation::AtlasLandfillState& state, const Containers::StridedArrayView1D<const Vector2i> sizes, const Containers::StridedArrayView1D<Vector2i> offsets, const Containers::StridedArrayView1D<Int> zOffsets, const Containers::MutableBitArrayView rotations) {
CORRADE_ASSERT(offsets.size() == sizes.size(), CORRADE_ASSERT(offsets.size() == sizes.size(),
"TextureTools::AtlasLandfill::add(): expected sizes and offsets views to have the same size, got" << sizes.size() << "and" << offsets.size(), {}); "TextureTools::AtlasLandfill::add(): expected sizes and offsets views to have the same size, got" << sizes.size() << "and" << offsets.size(), {});
CORRADE_ASSERT((!(state.flags & (AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::RotateLandscape)) && rotations.isEmpty()) || rotations.size() == sizes.size(), CORRADE_ASSERT((!(state.flags & (AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::RotateLandscape)) && rotations.isEmpty()) || rotations.size() == sizes.size(),
@ -342,41 +352,42 @@ bool atlasLandfillAdd(Implementation::AtlasLandfillState& state, const Container
} }
bool AtlasLandfill::add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector3i>& offsets, Containers::MutableBitArrayView flips) { Containers::Optional<Range3Di> AtlasLandfill::add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector3i>& offsets, Containers::MutableBitArrayView flips) {
return atlasLandfillAdd(*_state, sizes, offsets.slice(&Vector3i::xy), offsets.slice(&Vector3i::z), flips); return atlasLandfillAdd(*_state, sizes, offsets.slice(&Vector3i::xy), offsets.slice(&Vector3i::z), flips);
} }
bool AtlasLandfill::add(const std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector3i>& offsets, Containers::MutableBitArrayView flips) { Containers::Optional<Range3Di> AtlasLandfill::add(const std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector3i>& offsets, Containers::MutableBitArrayView flips) {
return add(Containers::stridedArrayView(sizes), offsets, flips); return add(Containers::stridedArrayView(sizes), offsets, flips);
} }
bool AtlasLandfill::add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector3i>& offsets) { Containers::Optional<Range3Di> AtlasLandfill::add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector3i>& offsets) {
CORRADE_ASSERT(!(_state->flags & (AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::RotateLandscape)), CORRADE_ASSERT(!(_state->flags & (AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::RotateLandscape)),
"TextureTools::AtlasLandfill::add():" << (_state->flags & (AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::RotateLandscape)) << "set, expected a rotations view", {}); "TextureTools::AtlasLandfill::add():" << (_state->flags & (AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::RotateLandscape)) << "set, expected a rotations view", {});
return add(sizes, offsets, nullptr); return add(sizes, offsets, nullptr);
} }
bool AtlasLandfill::add(const std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector3i>& offsets) { Containers::Optional<Range3Di> AtlasLandfill::add(const std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector3i>& offsets) {
return add(Containers::stridedArrayView(sizes), offsets); return add(Containers::stridedArrayView(sizes), offsets);
} }
bool AtlasLandfill::add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector2i>& offsets, Containers::MutableBitArrayView flips) { Containers::Optional<Range2Di> AtlasLandfill::add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector2i>& offsets, Containers::MutableBitArrayView flips) {
CORRADE_ASSERT(_state->size.z() == 1, CORRADE_ASSERT(_state->size.z() == 1,
"TextureTools::AtlasLandfill::add(): use the three-component overload for an array atlas", {}); "TextureTools::AtlasLandfill::add(): use the three-component overload for an array atlas", {});
return atlasLandfillAdd(*_state, sizes, offsets, nullptr, flips); const Containers::Optional<Range3Di> out = atlasLandfillAdd(*_state, sizes, offsets, nullptr, flips);
return out ? Containers::optional(out->xy()) : Containers::NullOpt;
} }
bool AtlasLandfill::add(const std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector2i>& offsets, Containers::MutableBitArrayView flips) { Containers::Optional<Range2Di> AtlasLandfill::add(const std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector2i>& offsets, Containers::MutableBitArrayView flips) {
return add(Containers::stridedArrayView(sizes), offsets, flips); return add(Containers::stridedArrayView(sizes), offsets, flips);
} }
bool AtlasLandfill::add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector2i>& offsets) { Containers::Optional<Range2Di> AtlasLandfill::add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector2i>& offsets) {
CORRADE_ASSERT(!(_state->flags & (AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::RotateLandscape)), CORRADE_ASSERT(!(_state->flags & (AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::RotateLandscape)),
"TextureTools::AtlasLandfill::add():" << (_state->flags & (AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::RotateLandscape)) << "set, expected a rotations view", {}); "TextureTools::AtlasLandfill::add():" << (_state->flags & (AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::RotateLandscape)) << "set, expected a rotations view", {});
return add(sizes, offsets, nullptr); return add(sizes, offsets, nullptr);
} }
bool AtlasLandfill::add(const std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector2i>& offsets) { Containers::Optional<Range2Di> AtlasLandfill::add(const std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector2i>& offsets) {
return add(Containers::stridedArrayView(sizes), offsets); return add(Containers::stridedArrayView(sizes), offsets);
} }

38
src/Magnum/TextureTools/Atlas.h

@ -345,6 +345,8 @@ class MAGNUM_TEXTURETOOLS_EXPORT AtlasLandfill {
* @param[in] sizes Texture sizes * @param[in] sizes Texture sizes
* @param[out] offsets Resulting offsets in the atlas * @param[out] offsets Resulting offsets in the atlas
* @param[out] rotations Which textures got rotated * @param[out] rotations Which textures got rotated
* @return Range spanning all added items including padding or
* @relativeref{Corrade,Containers::NullOpt} if they didn't fit
* *
* The @p sizes, @p offsets and @p rotations views are expected to have * The @p sizes, @p offsets and @p rotations views are expected to have
* the same size. The @p sizes are all expected to be not larger than * the same size. The @p sizes are all expected to be not larger than
@ -364,17 +366,18 @@ class MAGNUM_TEXTURETOOLS_EXPORT AtlasLandfill {
* height are treated as any others to make sure they don't overlap * height are treated as any others to make sure they don't overlap
* other items. * other items.
* *
* On success returns @cpp true @ce and updates @ref filledSize(). If * On success updates @ref filledSize() and returns a range spanning
* @ref size() is bounded, can return @cpp false @ce if the items * all added items including padding, which can be used for example to
* didn't fit, in which case the internals and contents of @p offsets * perform a partial GPU texture upload. If @ref size() is bounded, can
* and @p rotations are left in an undefined state. For an unbounded * return @relativeref{Corrade,Containers::NullOpt} if the items didn't
* @ref size() returns @cpp true @ce always. * 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() the function never fails.
* @see @ref setFlags(), @ref setPadding() * @see @ref setFlags(), @ref setPadding()
*/ */
bool add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector3i>& offsets, Containers::MutableBitArrayView rotations); Containers::Optional<Range3Di> add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector3i>& offsets, Containers::MutableBitArrayView rotations);
/** @overload */ /** @overload */
bool add(std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector3i>& offsets, Containers::MutableBitArrayView rotations); Containers::Optional<Range3Di> add(std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector3i>& offsets, Containers::MutableBitArrayView rotations);
/** /**
* @brief Add textures to the atlas with rotations disabled * @brief Add textures to the atlas with rotations disabled
@ -385,20 +388,20 @@ class MAGNUM_TEXTURETOOLS_EXPORT AtlasLandfill {
* @relativeref{AtlasLandfillFlag,RotateLandscape} is set. * @relativeref{AtlasLandfillFlag,RotateLandscape} is set.
* @see @ref clearFlags() * @see @ref clearFlags()
*/ */
bool add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector3i>& offsets); Containers::Optional<Range3Di> add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector3i>& offsets);
/** @overload */ /** @overload */
bool add(std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector3i>& offsets); Containers::Optional<Range3Di> add(std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector3i>& offsets);
/** /**
* @brief Add textures to a non-array atlas * @brief Add textures to a non-array atlas
* *
* Can be called only if @ref size() depth is @cpp 1 @ce. * Like @ref add(const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector3i>&, Containers::MutableBitArrayView),
* but omitting the third dimension. Can be called only if @ref size()
* depth is @cpp 1 @ce.
*/ */
bool add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector2i>& offsets, Containers::MutableBitArrayView rotations); Containers::Optional<Range2Di> add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector2i>& offsets, Containers::MutableBitArrayView rotations);
/** @overload */ /** @overload */
bool add(std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector2i>& offsets, Containers::MutableBitArrayView rotations); Containers::Optional<Range2Di> add(std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector2i>& offsets, Containers::MutableBitArrayView rotations);
/** /**
* @brief Add textures to a non-array atlas with rotations disabled * @brief Add textures to a non-array atlas with rotations disabled
@ -410,10 +413,9 @@ class MAGNUM_TEXTURETOOLS_EXPORT AtlasLandfill {
* @relativeref{AtlasLandfillFlag,RotateLandscape} is set. * @relativeref{AtlasLandfillFlag,RotateLandscape} is set.
* @see @ref clearFlags() * @see @ref clearFlags()
*/ */
bool add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector2i>& offsets); Containers::Optional<Range2Di> add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector2i>& offsets);
/** @overload */ /** @overload */
bool add(std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector2i>& offsets); Containers::Optional<Range2Di> add(std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector2i>& offsets);
private: private:
Containers::Pointer<Implementation::AtlasLandfillState> _state; Containers::Pointer<Implementation::AtlasLandfillState> _state;

72
src/Magnum/TextureTools/Test/AtlasTest.cpp

@ -25,9 +25,10 @@
*/ */
#include <Corrade/Containers/Array.h> #include <Corrade/Containers/Array.h>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Pair.h>
#include <Corrade/Containers/StridedArrayView.h> #include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Containers/StridedBitArrayView.h> #include <Corrade/Containers/StridedBitArrayView.h>
#include <Corrade/Containers/Pair.h>
#include <Corrade/Containers/String.h> #include <Corrade/Containers/String.h>
#include <Corrade/TestSuite/Tester.h> #include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h> #include <Corrade/TestSuite/Compare/Container.h>
@ -35,12 +36,11 @@
#include <Corrade/Utility/Format.h> #include <Corrade/Utility/Format.h>
#include "Magnum/Math/Matrix3.h" #include "Magnum/Math/Matrix3.h"
#include "Magnum/Math/Range.h"
#include "Magnum/TextureTools/Atlas.h" #include "Magnum/TextureTools/Atlas.h"
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
#include <vector> #include <vector>
#include "Magnum/Math/Range.h"
#endif #endif
namespace Magnum { namespace TextureTools { namespace Test { namespace { namespace Magnum { namespace TextureTools { namespace Test { namespace {
@ -584,12 +584,12 @@ void AtlasTest::landfillFullFit() {
UnsignedByte rotationData[1]; UnsignedByte rotationData[1];
Containers::MutableBitArrayView rotations{rotationData, 0, 4}; Containers::MutableBitArrayView rotations{rotationData, 0, 4};
/* Testing the init list overload here as all others test the view */ /* Testing the init list overload here as all others test the view */
CORRADE_VERIFY(atlas.add({ CORRADE_COMPARE(atlas.add({
{2, 4}, /* 0 */ {2, 4}, /* 0 */
{2, 3}, /* 1 */ {2, 3}, /* 1 */
{2, 3}, /* 2 */ {2, 3}, /* 2 */
{2, 2}, /* 3 */ {2, 2}, /* 3 */
}, offsets, rotations)); }, offsets, rotations), (Range2Di{{}, {4, 6}}));
CORRADE_COMPARE(atlas.filledSize(), (Vector3i{4, 6, 1})); CORRADE_COMPARE(atlas.filledSize(), (Vector3i{4, 6, 1}));
CORRADE_COMPARE_AS(rotations, Containers::stridedArrayView({ CORRADE_COMPARE_AS(rotations, Containers::stridedArrayView({
false, false, false, false false, false, false, false
@ -626,9 +626,9 @@ void AtlasTest::landfill() {
/* Test the rotations-less overload if no rotations are enabled */ /* Test the rotations-less overload if no rotations are enabled */
if(!(data.flags & (AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::RotateLandscape))) if(!(data.flags & (AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::RotateLandscape)))
CORRADE_VERIFY(atlas.add(LandfillSizes, offsets)); CORRADE_COMPARE(atlas.add(LandfillSizes, offsets), (Range2Di{{}, data.filledSize}));
else else
CORRADE_VERIFY(atlas.add(LandfillSizes, offsets, rotations)); CORRADE_COMPARE(atlas.add(LandfillSizes, offsets, rotations), (Range2Di{{}, data.filledSize}));
CORRADE_COMPARE(atlas.filledSize(), (Vector3i{data.filledSize, 1})); CORRADE_COMPARE(atlas.filledSize(), (Vector3i{data.filledSize, 1}));
CORRADE_COMPARE_AS(rotations, CORRADE_COMPARE_AS(rotations,
@ -672,22 +672,25 @@ void AtlasTest::landfillIncremental() {
AtlasLandfill atlas{{11, 8}}; AtlasLandfill atlas{{11, 8}};
CORRADE_COMPARE(atlas.filledSize(), (Vector3i{11, 0, 1})); CORRADE_COMPARE(atlas.filledSize(), (Vector3i{11, 0, 1}));
CORRADE_VERIFY(atlas.add( /* The first addition spans a range that begins at the origin and ends at
filledSize() */
CORRADE_COMPARE(atlas.add(
sizes.prefix(5), sizes.prefix(5),
offsets.prefix(5), offsets.prefix(5),
rotations.prefix(5))); rotations.prefix(5)), (Range2Di{{}, {11, 6}}));
CORRADE_COMPARE(atlas.filledSize(), (Vector3i{11, 6, 1})); CORRADE_COMPARE(atlas.filledSize(), (Vector3i{11, 6, 1}));
CORRADE_VERIFY(atlas.add( /* Following additions are just incremental */
CORRADE_COMPARE(atlas.add(
sizes.slice(5, 9), sizes.slice(5, 9),
offsets.slice(5, 9), offsets.slice(5, 9),
rotations.slice(5, 9))); rotations.slice(5, 9)), (Range2Di{{0, 4}, {8, 8}}));
CORRADE_COMPARE(atlas.filledSize(), (Vector3i{11, 8, 1})); CORRADE_COMPARE(atlas.filledSize(), (Vector3i{11, 8, 1}));
CORRADE_VERIFY(atlas.add( CORRADE_COMPARE(atlas.add(
sizes.exceptPrefix(9), sizes.exceptPrefix(9),
offsets.exceptPrefix(9), offsets.exceptPrefix(9),
rotations.exceptPrefix(9))); rotations.exceptPrefix(9)), (Range2Di{{7, 6}, {11, 8}}));
CORRADE_COMPARE(atlas.filledSize(), (Vector3i{11, 8, 1})); CORRADE_COMPARE(atlas.filledSize(), (Vector3i{11, 8, 1}));
CORRADE_COMPARE_AS(rotations, Containers::stridedArrayView({ CORRADE_COMPARE_AS(rotations, Containers::stridedArrayView({
@ -728,7 +731,10 @@ void AtlasTest::landfillPadded() {
Vector2i offsets[8]; Vector2i offsets[8];
UnsignedByte rotationData[1]; UnsignedByte rotationData[1];
Containers::MutableBitArrayView rotations{rotationData, 0, 8}; Containers::MutableBitArrayView rotations{rotationData, 0, 8};
CORRADE_VERIFY(atlas.add({
/* The filled size includes the padding as well, since that's what is
likely desirable to get copied as well */
CORRADE_COMPARE(atlas.add({
{6, 2}, /* 0, padded to {8, 6}, flipped */ {6, 2}, /* 0, padded to {8, 6}, flipped */
{1, 3}, /* 1, padded to {3, 7} */ {1, 3}, /* 1, padded to {3, 7} */
{4, 1}, /* 2, padded to {6, 5}, flipped */ {4, 1}, /* 2, padded to {6, 5}, flipped */
@ -737,7 +743,7 @@ void AtlasTest::landfillPadded() {
{1, 1}, /* 5, padded to {3, 5} */ {1, 1}, /* 5, padded to {3, 5} */
{3, 0}, /* 6 (zero height), padded to {5, 4}, flipped */ {3, 0}, /* 6 (zero height), padded to {5, 4}, flipped */
{0, 2}, /* 7 (zero width), padded to {2, 6} */ {0, 2}, /* 7 (zero width), padded to {2, 6} */
}, offsets, rotations)); }, offsets, rotations), (Range2Di{{}, {17, 13}}));
CORRADE_COMPARE(atlas.filledSize(), (Vector3i{17, 13, 1})); CORRADE_COMPARE(atlas.filledSize(), (Vector3i{17, 13, 1}));
CORRADE_COMPARE_AS(rotations, Containers::stridedArrayView({ CORRADE_COMPARE_AS(rotations, Containers::stridedArrayView({
@ -780,7 +786,7 @@ void AtlasTest::landfillNoFit() {
Vector2i offsets[Containers::arraySize(LandfillSizes)]; Vector2i offsets[Containers::arraySize(LandfillSizes)];
UnsignedByte rotationData[2]; UnsignedByte rotationData[2];
Containers::MutableBitArrayView rotations{rotationData, 0, Containers::arraySize(LandfillSizes)}; Containers::MutableBitArrayView rotations{rotationData, 0, Containers::arraySize(LandfillSizes)};
CORRADE_VERIFY(!atlas.add(LandfillSizes, offsets, rotations)); CORRADE_COMPARE(atlas.add(LandfillSizes, offsets, rotations), Containers::NullOpt);
} }
void AtlasTest::landfillCopy() { void AtlasTest::landfillCopy() {
@ -822,14 +828,14 @@ void AtlasTest::landfillArrayFullFit() {
UnsignedByte rotationData[1]; UnsignedByte rotationData[1];
Containers::MutableBitArrayView rotations{rotationData, 0, 6}; Containers::MutableBitArrayView rotations{rotationData, 0, 6};
/* Testing the init list overload as all others test the view */ /* Testing the init list overload as all others test the view */
CORRADE_VERIFY(atlas.add({ CORRADE_COMPARE(atlas.add({
{3, 5}, /* 0 */ {3, 5}, /* 0 */
{1, 5}, /* 1 */ {1, 5}, /* 1 */
{3, 3}, /* 2 */ {3, 3}, /* 2 */
{1, 3}, /* 3 */ {1, 3}, /* 3 */
{2, 2}, /* 4 */ {2, 2}, /* 4 */
{2, 2}, /* 5 */ {2, 2}, /* 5 */
}, offsets, rotations)); }, offsets, rotations), (Range3Di{{}, {4, 5, 2}}));
CORRADE_COMPARE(atlas.filledSize(), (Vector3i{4, 5, 2})); CORRADE_COMPARE(atlas.filledSize(), (Vector3i{4, 5, 2}));
CORRADE_COMPARE_AS(rotations, Containers::stridedArrayView({ CORRADE_COMPARE_AS(rotations, Containers::stridedArrayView({
false, false, false, false, false, false false, false, false, false, false, false
@ -867,9 +873,9 @@ void AtlasTest::landfillArray() {
/* Test the rotations-less overload if no rotations are enabled */ /* Test the rotations-less overload if no rotations are enabled */
if(!(data.flags & (AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::RotateLandscape))) if(!(data.flags & (AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::RotateLandscape)))
CORRADE_VERIFY(atlas.add(LandfillArraySizes, offsets)); CORRADE_COMPARE(atlas.add(LandfillArraySizes, offsets), (Range3Di{{}, data.filledSize}));
else else
CORRADE_VERIFY(atlas.add(LandfillArraySizes, offsets, rotations)); CORRADE_COMPARE(atlas.add(LandfillArraySizes, offsets, rotations), (Range3Di{{}, data.filledSize}));
CORRADE_COMPARE(atlas.filledSize(), data.filledSize); CORRADE_COMPARE(atlas.filledSize(), data.filledSize);
CORRADE_COMPARE_AS(rotations, CORRADE_COMPARE_AS(rotations,
@ -909,22 +915,28 @@ void AtlasTest::landfillArrayIncremental() {
AtlasLandfill atlas{{11, 6, 2}}; AtlasLandfill atlas{{11, 6, 2}};
CORRADE_COMPARE(atlas.filledSize(), (Vector3i{11, 6, 0})); CORRADE_COMPARE(atlas.filledSize(), (Vector3i{11, 6, 0}));
CORRADE_VERIFY(atlas.add( /* The first addition spans a range that begins at the origin and ends at
filledSize(). Well, almost, because the first four items don't make use
of the rightmost column. */
CORRADE_COMPARE(atlas.add(
sizes.prefix(4), sizes.prefix(4),
offsets.prefix(4), offsets.prefix(4),
rotations.prefix(4))); rotations.prefix(4)), (Range3Di{{}, {10, 6, 1}}));
CORRADE_COMPARE(atlas.filledSize(), (Vector3i{11, 6, 1})); CORRADE_COMPARE(atlas.filledSize(), (Vector3i{11, 6, 1}));
CORRADE_VERIFY(atlas.add( /* Following additions are incremental ... well, in this case it overflows
to the next slice, which means it covers basically the whole area */
CORRADE_COMPARE(atlas.add(
sizes.slice(4, 7), sizes.slice(4, 7),
offsets.slice(4, 7), offsets.slice(4, 7),
rotations.slice(4, 7))); rotations.slice(4, 7)), (Range3Di{{}, {11, 6, 2}}));
CORRADE_COMPARE(atlas.filledSize(), (Vector3i{11, 6, 2})); CORRADE_COMPARE(atlas.filledSize(), (Vector3i{11, 6, 2}));
CORRADE_VERIFY(atlas.add( /* The last addition is then just a tiny bit of the second slice */
CORRADE_COMPARE(atlas.add(
sizes.exceptPrefix(7), sizes.exceptPrefix(7),
offsets.exceptPrefix(7), offsets.exceptPrefix(7),
rotations.exceptPrefix(7))); rotations.exceptPrefix(7)), (Range3Di{{2, 0, 1}, {7, 2, 2}}));
CORRADE_COMPARE(atlas.filledSize(), (Vector3i{11, 6, 2})); CORRADE_COMPARE(atlas.filledSize(), (Vector3i{11, 6, 2}));
CORRADE_COMPARE_AS(rotations, Containers::stridedArrayView({ CORRADE_COMPARE_AS(rotations, Containers::stridedArrayView({
@ -961,7 +973,7 @@ void AtlasTest::landfillArrayPadded() {
Vector3i offsets[8]; Vector3i offsets[8];
UnsignedByte rotationData[1]; UnsignedByte rotationData[1];
Containers::MutableBitArrayView rotations{rotationData, 0, 8}; Containers::MutableBitArrayView rotations{rotationData, 0, 8};
CORRADE_VERIFY(atlas.add({ CORRADE_COMPARE(atlas.add({
{6, 2}, /* 0, padded to {8, 6}, flipped */ {6, 2}, /* 0, padded to {8, 6}, flipped */
{1, 3}, /* 1, padded to {3, 7} */ {1, 3}, /* 1, padded to {3, 7} */
{4, 1}, /* 2, padded to {6, 5}, flipped */ {4, 1}, /* 2, padded to {6, 5}, flipped */
@ -970,7 +982,7 @@ void AtlasTest::landfillArrayPadded() {
{1, 1}, /* 5, padded to {3, 5} */ {1, 1}, /* 5, padded to {3, 5} */
{3, 0}, /* 6 (zero height), padded to {5, 4}, flipped */ {3, 0}, /* 6 (zero height), padded to {5, 4}, flipped */
{0, 2}, /* 7 (zero width), padded to {2, 6} */ {0, 2}, /* 7 (zero width), padded to {2, 6} */
}, offsets, rotations)); }, offsets, rotations), (Range3Di{{}, {16, 12, 2}}));
CORRADE_COMPARE(atlas.filledSize(), (Vector3i{16, 12, 2})); CORRADE_COMPARE(atlas.filledSize(), (Vector3i{16, 12, 2}));
CORRADE_COMPARE_AS(rotations, Containers::stridedArrayView({ CORRADE_COMPARE_AS(rotations, Containers::stridedArrayView({
@ -1011,7 +1023,7 @@ void AtlasTest::landfillArrayNoFit() {
Vector3i offsets[Containers::arraySize(LandfillArraySizes)]; Vector3i offsets[Containers::arraySize(LandfillArraySizes)];
UnsignedByte rotationData[2]; UnsignedByte rotationData[2];
Containers::MutableBitArrayView rotations{rotationData, 0, Containers::arraySize(LandfillArraySizes)}; Containers::MutableBitArrayView rotations{rotationData, 0, Containers::arraySize(LandfillArraySizes)};
CORRADE_VERIFY(!atlas.add(LandfillArraySizes, offsets, rotations)); CORRADE_COMPARE(atlas.add(LandfillArraySizes, offsets, rotations), Containers::NullOpt);
/* Sanity check that with one more slice it works */ /* Sanity check that with one more slice it works */
} { } {
@ -1019,7 +1031,7 @@ void AtlasTest::landfillArrayNoFit() {
Vector3i offsets[Containers::arraySize(LandfillArraySizes)]; Vector3i offsets[Containers::arraySize(LandfillArraySizes)];
UnsignedByte rotationData[2]; UnsignedByte rotationData[2];
Containers::MutableBitArrayView rotations{rotationData, 0, Containers::arraySize(LandfillArraySizes)}; Containers::MutableBitArrayView rotations{rotationData, 0, Containers::arraySize(LandfillArraySizes)};
CORRADE_VERIFY(atlas.add(LandfillArraySizes, offsets, rotations)); CORRADE_COMPARE(atlas.add(LandfillArraySizes, offsets, rotations), (Range3Di{{}, {6, 6, 3}}));
} }
} }

Loading…
Cancel
Save