Browse Source

TextureTools: support padding in the landfill atlas packer.

This achieves feature parity with the silly old packer so I can
deprecate it.
pull/168/head
Vladimír Vondruš 3 years ago
parent
commit
10bc66884a
  1. 65
      src/Magnum/TextureTools/Atlas.cpp
  2. 69
      src/Magnum/TextureTools/Atlas.h
  3. 143
      src/Magnum/TextureTools/Test/AtlasTest.cpp

65
src/Magnum/TextureTools/Atlas.cpp

@ -81,13 +81,14 @@ struct AtlasLandfillState {
/* X = MAX and z = 1 is for 2D unbounded, z = MAX is for 3D unbounded */ /* X = MAX and z = 1 is for 2D unbounded, z = MAX is for 3D unbounded */
Vector3i size; Vector3i size;
AtlasLandfillFlags flags = AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::WidestFirst; AtlasLandfillFlags flags = AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::WidestFirst;
Vector2i padding;
}; };
} }
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) { 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) {
/* 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());
@ -134,13 +135,25 @@ bool atlasLandfillAddSortedFlipped(Implementation::AtlasLandfillState& state, co
for(UnsignedShort& yOffset: placementYOffsets) for(UnsignedShort& yOffset: placementYOffsets)
yOffset = newYOffset; yOffset = newYOffset;
/* Index of this item in the original array */
const UnsignedInt index = sortedFlippedSizes[i].second();
/* Figure out padding of this item. If the size was rotated, rotate it
as well. If the rotations aren't even present, no rotations were
done. */
const Vector2i padding = !rotations.isEmpty() && rotations[index] ?
state.padding.flipped() : state.padding;
/* 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),
advance to the next X offset */ add the (appropriately rotated) padding to it so it points to the
offsets[sortedFlippedSizes[i].second()] = { original unpadded size */
offsets[index] = padding + Vector2i{
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
}; };
/* Advance to the next X offset */
sliceState.xOffset += size.x(); sliceState.xOffset += size.x();
} }
@ -154,7 +167,7 @@ bool atlasLandfillAddSortedFlipped(Implementation::AtlasLandfillState& state, co
if(i < sortedFlippedSizes.size()) { if(i < sortedFlippedSizes.size()) {
if(slice + 1 == state.size.z()) if(slice + 1 == state.size.z())
return false; return false;
return atlasLandfillAddSortedFlipped(state, slice + 1, sortedFlippedSizes.exceptPrefix(i), offsets, zOffsets); return atlasLandfillAddSortedFlipped(state, slice + 1, sortedFlippedSizes.exceptPrefix(i), offsets, zOffsets, rotations);
} }
/* Everything fit, success */ /* Everything fit, success */
@ -180,17 +193,31 @@ bool atlasLandfillAdd(const char* messagePrefix, Implementation::AtlasLandfillSt
Containers::Array<Containers::Pair<Vector2i, UnsignedInt>> sortedFlippedSizes{NoInit, sizes.size()}; Containers::Array<Containers::Pair<Vector2i, UnsignedInt>> sortedFlippedSizes{NoInit, sizes.size()};
for(std::size_t i = 0; i != sizes.size(); ++i) { for(std::size_t i = 0; i != sizes.size(); ++i) {
Vector2i size = sizes[i]; Vector2i size = sizes[i];
if((state.flags & AtlasLandfillFlag::RotateLandscape && size.x() < size.y()) || #ifndef CORRADE_NO_ASSERT
(state.flags & AtlasLandfillFlag::RotatePortrait && size.x() > size.y())) Vector2i padding = state.padding;
#endif
Vector2i sizePadded = size + 2*state.padding;
if((state.flags & AtlasLandfillFlag::RotateLandscape && sizePadded.x() < sizePadded.y()) ||
(state.flags & AtlasLandfillFlag::RotatePortrait && sizePadded.x() > sizePadded.y()))
{ {
#ifndef CORRADE_NO_ASSERT
size = size.flipped(); size = size.flipped();
padding = padding.flipped();
#endif
sizePadded = sizePadded.flipped();
rotations.set(i); rotations.set(i);
} }
CORRADE_ASSERT(size.product() && size <= state.size.xy(), #ifndef CORRADE_NO_ASSERT
messagePrefix << "expected size" << i << "to be non-zero and not larger than" << Debug::packed << state.size.xy() << "but got" << Debug::packed << size, {}); if(state.padding.isZero())
CORRADE_ASSERT(size.product() && sizePadded <= state.size.xy(),
messagePrefix << "expected size" << i << "to be non-zero and not larger than" << Debug::packed << state.size.xy() << "but got" << Debug::packed << size, {});
else
CORRADE_ASSERT(size.product() && sizePadded <= state.size.xy(),
messagePrefix << "expected size" << i << "to be non-zero and not larger than" << Debug::packed << state.size.xy() << "but got" << Debug::packed << size << "and padding" << Debug::packed << padding, {});
#endif
sortedFlippedSizes[i] = {size, UnsignedInt(i)}; sortedFlippedSizes[i] = {sizePadded, UnsignedInt(i)};
} }
/* Sort to have the highest first. Assuming the items are square, /* Sort to have the highest first. Assuming the items are square,
@ -216,7 +243,7 @@ bool atlasLandfillAdd(const char* messagePrefix, Implementation::AtlasLandfillSt
return a.first().y() > b.first().y(); return a.first().y() > b.first().y();
}); });
return atlasLandfillAddSortedFlipped(state, 0, sortedFlippedSizes, offsets, zOffsets); return atlasLandfillAddSortedFlipped(state, 0, sortedFlippedSizes, offsets, zOffsets, rotations);
} }
} }
@ -249,6 +276,15 @@ Vector2i AtlasLandfill::filledSize() const {
return {_state->size.x(), Math::max(_state->yOffsets)}; return {_state->size.x(), Math::max(_state->yOffsets)};
} }
Vector2i AtlasLandfill::padding() const {
return _state->padding;
}
AtlasLandfill& AtlasLandfill::setPadding(const Vector2i& padding) {
_state->padding = padding;
return *this;
}
AtlasLandfillFlags AtlasLandfill::flags() const { AtlasLandfillFlags AtlasLandfill::flags() const {
return _state->flags; return _state->flags;
} }
@ -309,6 +345,15 @@ Vector3i AtlasLandfillArray::filledSize() const {
return {_state->size.xy(), Int(_state->slices.size())}; return {_state->size.xy(), Int(_state->slices.size())};
} }
Vector2i AtlasLandfillArray::padding() const {
return _state->padding;
}
AtlasLandfillArray& AtlasLandfillArray::setPadding(const Vector2i& padding) {
_state->padding = padding;
return *this;
}
AtlasLandfillFlags AtlasLandfillArray::flags() const { AtlasLandfillFlags AtlasLandfillArray::flags() const {
return _state->flags; return _state->flags;
} }

69
src/Magnum/TextureTools/Atlas.h

@ -254,6 +254,32 @@ class MAGNUM_TEXTURETOOLS_EXPORT AtlasLandfill {
return setFlags(this->flags() & ~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 * @brief Add textures to the atlas
* @param[in] sizes Texture sizes * @param[in] sizes Texture sizes
@ -262,20 +288,22 @@ class MAGNUM_TEXTURETOOLS_EXPORT AtlasLandfill {
* *
* 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 non-zero and not * the same size. The @p sizes are all expected to be non-zero and not
* larger than @ref size() after a rotation based on * larger than @ref size() after appying padding and then a rotation
* @ref AtlasLandfillFlag::RotatePortrait or * based on @ref AtlasLandfillFlag::RotatePortrait or
* @relativeref{AtlasLandfillFlag,RotateLandscape} being set. If * @relativeref{AtlasLandfillFlag,RotateLandscape} being set. If
* neither @relativeref{AtlasLandfillFlag,RotatePortrait} nor * neither @relativeref{AtlasLandfillFlag,RotatePortrait} nor
* @relativeref{AtlasLandfillFlag,RotateLandscape} is set, the * @relativeref{AtlasLandfillFlag,RotateLandscape} is set, the
* @p rotations view can be also empty or you can use the * @p rotations view can be also empty or you can use the
* @ref add(const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector2i>&) * @ref add(const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector2i>&)
* overload. * 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 * On success returns @cpp true @ce and updates @ref filledSize(). If
* @ref size() is bounded, can return @cpp false @ce if the items * @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 * 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 * and @p rotations are left in an undefined state. For an unbounded
* @ref size() returns @cpp true @ce always. * @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); bool add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector2i>& offsets, Containers::MutableBitArrayView rotations);
@ -409,6 +437,33 @@ class MAGNUM_TEXTURETOOLS_EXPORT AtlasLandfillArray {
return setFlags(this->flags() & ~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 * @brief Add textures to the atlas
* @param[in] sizes Texture sizes * @param[in] sizes Texture sizes
@ -417,20 +472,22 @@ class MAGNUM_TEXTURETOOLS_EXPORT AtlasLandfillArray {
* *
* 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 non-zero and not * the same size. The @p sizes are all expected to be non-zero and not
* larger than @ref size() after a rotation based on * larger than @ref size() after applying padding and then a rotation
* @ref AtlasLandfillFlag::RotatePortrait or * based on @ref AtlasLandfillFlag::RotatePortrait or
* @relativeref{AtlasLandfillFlag,RotateLandscape} being set. If * @relativeref{AtlasLandfillFlag,RotateLandscape} being set. If
* neither @relativeref{AtlasLandfillFlag,RotatePortrait} nor * neither @relativeref{AtlasLandfillFlag,RotatePortrait} nor
* @relativeref{AtlasLandfillFlag,RotateLandscape} is set, the * @relativeref{AtlasLandfillFlag,RotateLandscape} is set, the
* @p rotations view can be also empty or you can use the * @p rotations view can be also empty or you can use the
* @ref add(const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector3i>&) * @ref add(const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector3i>&)
* overload. * 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 * On success returns @cpp true @ce and updates @ref filledSize(). If
* @ref size() is bounded, can return @cpp false @ce if the items * @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 * 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 * and @p rotations are left in an undefined state. For an unbounded
* @ref size() returns @cpp true @ce always. * @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); bool add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector3i>& offsets, Containers::MutableBitArrayView rotations);

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

@ -50,6 +50,7 @@ struct AtlasTest: TestSuite::Tester {
void landfillFullFit(); void landfillFullFit();
void landfill(); void landfill();
void landfillIncremental(); void landfillIncremental();
void landfillPadded();
void landfillNoFit(); void landfillNoFit();
void landfillCopy(); void landfillCopy();
void landfillMove(); void landfillMove();
@ -57,6 +58,7 @@ struct AtlasTest: TestSuite::Tester {
void landfillArrayFullFit(); void landfillArrayFullFit();
void landfillArray(); void landfillArray();
void landfillArrayIncremental(); void landfillArrayIncremental();
void landfillArrayPadded();
void landfillArrayNoFit(); void landfillArrayNoFit();
void landfillArrayCopy(); void landfillArrayCopy();
void landfillArrayMove(); void landfillArrayMove();
@ -66,6 +68,7 @@ struct AtlasTest: TestSuite::Tester {
void landfillAddMissingRotations(); void landfillAddMissingRotations();
void landfillAddInvalidViewSizes(); void landfillAddInvalidViewSizes();
void landfillAddTooLargeElement(); void landfillAddTooLargeElement();
void landfillAddTooLargeElementPadded();
void basic(); void basic();
void padding(); void padding();
@ -430,6 +433,7 @@ AtlasTest::AtlasTest() {
Containers::arraySize(LandfillData)); Containers::arraySize(LandfillData));
addTests({&AtlasTest::landfillIncremental, addTests({&AtlasTest::landfillIncremental,
&AtlasTest::landfillPadded,
&AtlasTest::landfillNoFit, &AtlasTest::landfillNoFit,
&AtlasTest::landfillCopy, &AtlasTest::landfillCopy,
&AtlasTest::landfillMove, &AtlasTest::landfillMove,
@ -440,6 +444,7 @@ AtlasTest::AtlasTest() {
Containers::arraySize(LandfillArrayData)); Containers::arraySize(LandfillArrayData));
addTests({&AtlasTest::landfillArrayIncremental, addTests({&AtlasTest::landfillArrayIncremental,
&AtlasTest::landfillArrayPadded,
&AtlasTest::landfillArrayNoFit, &AtlasTest::landfillArrayNoFit,
&AtlasTest::landfillArrayCopy, &AtlasTest::landfillArrayCopy,
&AtlasTest::landfillArrayMove, &AtlasTest::landfillArrayMove,
@ -449,6 +454,7 @@ AtlasTest::AtlasTest() {
&AtlasTest::landfillAddMissingRotations, &AtlasTest::landfillAddMissingRotations,
&AtlasTest::landfillAddInvalidViewSizes, &AtlasTest::landfillAddInvalidViewSizes,
&AtlasTest::landfillAddTooLargeElement, &AtlasTest::landfillAddTooLargeElement,
&AtlasTest::landfillAddTooLargeElementPadded,
&AtlasTest::basic, &AtlasTest::basic,
&AtlasTest::padding, &AtlasTest::padding,
@ -496,6 +502,7 @@ void AtlasTest::landfillFullFit() {
CORRADE_COMPARE(atlas.size(), (Vector2i{4, 6})); CORRADE_COMPARE(atlas.size(), (Vector2i{4, 6}));
CORRADE_COMPARE(atlas.filledSize(), (Vector2i{4, 0})); CORRADE_COMPARE(atlas.filledSize(), (Vector2i{4, 0}));
CORRADE_COMPARE(atlas.flags(), AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::WidestFirst); CORRADE_COMPARE(atlas.flags(), AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::WidestFirst);
CORRADE_COMPARE(atlas.padding(), Vector2i{});
Vector2i offsets[4]; Vector2i offsets[4];
UnsignedByte rotationData[1]; UnsignedByte rotationData[1];
@ -639,6 +646,53 @@ void AtlasTest::landfillIncremental() {
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
} }
void AtlasTest::landfillPadded() {
AtlasLandfill atlas{{15, 14}};
atlas.setPadding({1, 2});
CORRADE_COMPARE(atlas.padding(), (Vector2i{1, 2}));
Vector2i offsets[6];
UnsignedByte rotationData[1];
Containers::MutableBitArrayView rotations{rotationData, 0, 6};
CORRADE_VERIFY(atlas.add({
{6, 2}, /* 0, padded to {8, 6}, flipped */
{1, 3}, /* 1, padded to {3, 7} */
{4, 1}, /* 2, padded to {6, 5}, flipped */
{2, 2}, /* 3, padded to {4, 6} */
{2, 1}, /* 4, padded to {4, 5}, not flipped as padded it's portrait */
{1, 1}, /* 5, padded to {3, 5} */
}, offsets, rotations));
CORRADE_COMPARE(atlas.filledSize(), (Vector2i{15, 13}));
CORRADE_COMPARE_AS(rotations, Containers::stridedArrayView({
true, false, true, false, false, false
}).sliceBit(0), TestSuite::Compare::Container);
/* ...
...----....
10 .5.----....
9 ...-44-.33.
8 ...----.33.
______ ----....
__00__... ....
__00__..._____
__00__.1.__2__
__00__.1.__2__
2 __00__.1.__2__
1 __00__...__2__
______..._____
2 5 78 12 */
CORRADE_COMPARE_AS(Containers::arrayView(offsets), Containers::arrayView<Vector2i>({
{ 2, 1}, /* 0 */
{ 7, 2}, /* 1 */
{11, 1}, /* 2 */
{12, 8}, /* 3 */
{ 8, 9}, /* 4 */
{ 5, 10} /* 5 */
}), TestSuite::Compare::Container);
}
void AtlasTest::landfillNoFit() { void AtlasTest::landfillNoFit() {
/* Same as landfill(portrait, widest first) (which is the default flags) /* Same as landfill(portrait, widest first) (which is the default flags)
which fits into {11, 10} but limiting height to 9 */ which fits into {11, 10} but limiting height to 9 */
@ -684,6 +738,7 @@ void AtlasTest::landfillArrayFullFit() {
CORRADE_COMPARE(atlas.size(), (Vector3i{4, 5, 2})); CORRADE_COMPARE(atlas.size(), (Vector3i{4, 5, 2}));
CORRADE_COMPARE(atlas.filledSize(), (Vector3i{4, 5, 0})); CORRADE_COMPARE(atlas.filledSize(), (Vector3i{4, 5, 0}));
CORRADE_COMPARE(atlas.flags(), AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::WidestFirst); CORRADE_COMPARE(atlas.flags(), AtlasLandfillFlag::RotatePortrait|AtlasLandfillFlag::WidestFirst);
CORRADE_COMPARE(atlas.padding(), Vector2i{});
Vector3i offsets[6]; Vector3i offsets[6];
UnsignedByte rotationData[1]; UnsignedByte rotationData[1];
@ -818,6 +873,54 @@ void AtlasTest::landfillArrayIncremental() {
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
} }
void AtlasTest::landfillArrayPadded() {
/* Like landfillPadded(), but item 5 overlflowing to the next slice */
AtlasLandfillArray atlas{{15, 12, 3}};
atlas.setPadding({1, 2});
CORRADE_COMPARE(atlas.padding(), (Vector2i{1, 2}));
Vector3i offsets[6];
UnsignedByte rotationData[1];
Containers::MutableBitArrayView rotations{rotationData, 0, 6};
CORRADE_VERIFY(atlas.add({
{6, 2}, /* 0, padded to {8, 6}, flipped */
{1, 3}, /* 1, padded to {3, 7} */
{4, 1}, /* 2, padded to {6, 5}, flipped */
{2, 2}, /* 3, padded to {4, 6} */
{2, 1}, /* 4, padded to {4, 5}, not flipped as padded it's portrait */
{1, 1}, /* 5, padded to {3, 5} */
}, offsets, rotations));
CORRADE_COMPARE(atlas.filledSize(), (Vector3i{15, 12, 2}));
CORRADE_COMPARE_AS(rotations, Containers::stridedArrayView({
true, false, true, false, false, false
}).sliceBit(0), TestSuite::Compare::Container);
/* ----....
----....
9 -44-.33.
8 ----.33.
______ ----....
__00__... ....
__00__..._____
__00__.1.__2__ ...
__00__.1.__2__ ...
2 __00__.1.__2__ .5.
1 __00__...__2__ ...
______..._____ ...
2 5 78 12 1 */
CORRADE_COMPARE_AS(Containers::arrayView(offsets), Containers::arrayView<Vector3i>({
{ 2, 1, 0}, /* 0 */
{ 7, 2, 0}, /* 1 */
{11, 1, 0}, /* 2 */
{12, 8, 0}, /* 3 */
{ 8, 9, 0}, /* 4 */
{ 1, 2, 1} /* 5 */
}), TestSuite::Compare::Container);
}
void AtlasTest::landfillArrayNoFit() { void AtlasTest::landfillArrayNoFit() {
/* Same as landfillArray(portrait, widest first) (which is the default /* Same as landfillArray(portrait, widest first) (which is the default
flags) which fits into {11, 6, 2} but limiting depth to 1 */ flags) which fits into {11, 6, 2} but limiting depth to 1 */
@ -983,6 +1086,46 @@ void AtlasTest::landfillAddTooLargeElement() {
TestSuite::Compare::String); TestSuite::Compare::String);
} }
void AtlasTest::landfillAddTooLargeElementPadded() {
/* Sizes (except for zeros) are same as above minus padding */
CORRADE_SKIP_IF_NO_ASSERT();
/* The atlas makes the sizes portrait first, the array landscape instead */
AtlasLandfill atlas{{16, 23}};
AtlasLandfill atlas2{{16, 13}};
AtlasLandfillArray array{{23, 16, 3}};
AtlasLandfillArray array2{{13, 16, 3}};
atlas.setPadding({2, 1});
atlas2.setPadding({2, 1});
array.setPadding({1, 2})
.setFlags(AtlasLandfillFlag::RotateLandscape);
array2.setPadding({1, 2})
.setFlags(AtlasLandfillFlag::RotateLandscape);
Vector2i offsets[2];
Vector3i offsets3[2];
UnsignedByte rotationsData[1];
Containers::MutableBitArrayView rotations{rotationsData, 0, 2};
std::ostringstream out;
Error redirectError{&out};
atlas.add({{12, 21}, {0, 21}}, offsets, rotations);
array.add({{21, 12}, {21, 0}}, offsets3, rotations);
atlas.add({{12, 21}, {13, 21}}, offsets, rotations);
array.add({{21, 12}, {21, 13}}, offsets3, rotations);
/* Sizes that fit but don't after a flip */
atlas2.add({{9, 11}, {12, 11}}, offsets, rotations);
array2.add({{11, 9}, {11, 12}}, offsets3, rotations);
CORRADE_COMPARE_AS(out.str(),
"TextureTools::AtlasLandfill::add(): expected size 1 to be non-zero and not larger than {16, 23} but got {0, 21} and padding {2, 1}\n"
"TextureTools::AtlasLandfillArray::add(): expected size 1 to be non-zero and not larger than {23, 16} but got {21, 0} and padding {1, 2}\n"
"TextureTools::AtlasLandfill::add(): expected size 1 to be non-zero and not larger than {16, 23} but got {13, 21} and padding {2, 1}\n"
"TextureTools::AtlasLandfillArray::add(): expected size 1 to be non-zero and not larger than {23, 16} but got {21, 13} and padding {1, 2}\n"
"TextureTools::AtlasLandfill::add(): expected size 1 to be non-zero and not larger than {16, 13} but got {11, 12} and padding {1, 2}\n"
"TextureTools::AtlasLandfillArray::add(): expected size 1 to be non-zero and not larger than {13, 16} but got {12, 11} and padding {2, 1}\n",
TestSuite::Compare::String);
}
void AtlasTest::basic() { void AtlasTest::basic() {
std::vector<Range2Di> atlas = TextureTools::atlas({64, 64}, { std::vector<Range2Di> atlas = TextureTools::atlas({64, 64}, {
{12, 18}, {12, 18},

Loading…
Cancel
Save