Magnum::TextureTools namespace

Texture tools.

Tools for generating, compressing and optimizing textures.

This library is built if MAGNUM_WITH_TEXTURETOOLS is enabled when building Magnum. To use this library with CMake, request the TextureTools component of the Magnum package in CMake and link to the Magnum::TextureTools target:

find_package(Magnum REQUIRED TextureTools)

# ...
target_link_libraries(your-app PRIVATE Magnum::TextureTools)

Note that functionality depending on GL APIs is available only if Magnum is built with both MAGNUM_WITH_GL and MAGNUM_TARGET_GL enabled (which is done by default).

Additional utilities are built separately. See the magnum-distancefieldconverter utility documentation, Downloading and building and Usage with CMake for more information.

Classes

class AtlasLandfill new in Git master
Landfill texture atlas packer.
class DistanceField
Create a signed distance field.

Enums

enum class AtlasLandfillFlag { RotatePortrait = 1 << 0, RotateLandscape = 1 << 1, WidestFirst = 1 << 2, NarrowestFirst = 1 << 3, ReverseDirectionAlways = 1 << 4 } new in Git master
Landfill texture atlas packer behavior flag.

Typedefs

using AtlasLandfillFlags = Containers::EnumSet<AtlasLandfillFlag> new in Git master
Landfill texture atlas packer behavior flags.

Functions

auto operator<<(Debug& output, AtlasLandfillFlag value) -> Debug&
Debug output operator.
auto operator<<(Debug& output, AtlasLandfillFlags value) -> Debug&
Debug output operator.
auto atlas(const Vector2i& atlasSize, const std::vector<Vector2i>& sizes, const Vector2i& padding = {}) -> std::vector<Range2Di> deprecated in Git master
Pack textures into a texture atlas.
auto atlasArrayPowerOfTwo(const Vector2i& layerSize, const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector3i>& offsets) -> Int new in Git master
Pack square power-of-two textures into a texture atlas array.
auto atlasArrayPowerOfTwo(const Vector2i& layerSize, std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector3i>& offsets) -> Int new in Git master
auto atlasArrayPowerOfTwo(const Vector2i& layerSize, const Containers::StridedArrayView1D<const Vector2i>& sizes) -> Containers::Pair<Int, Containers::Array<Vector3i>> deprecated in Git master
Pack square power-of-two textures into a texture atlas array.
auto atlasArrayPowerOfTwo(const Vector2i& layerSize, std::initializer_list<Vector2i> sizes) -> Containers::Pair<Int, Containers::Array<Vector3i>> deprecated in Git master
auto atlasTextureCoordinateTransformation(const Vector2i& atlasSize, const Vector2i& size, const Vector2i& offset) -> Matrix3 new in Git master
Calculate a texture coordinate transformation matrix for an atlas-packed item.
auto atlasTextureCoordinateTransformationRotatedCounterClockwise(const Vector2i& atlasSize, const Vector2i& size, const Vector2i& offset) -> Matrix3 new in Git master
Calculate a texture coordinate transformation matrix for an atlas-packed item rotated counterclockwise.
auto atlasTextureCoordinateTransformationRotatedClockwise(const Vector2i& atlasSize, const Vector2i& size, const Vector2i& offset) -> Matrix3 new in Git master
Calculate a texture coordinate transformation matrix for an atlas-packed item rotated clockwise.
void distanceField(GL::Texture2D& input, GL::Texture2D& output, const Range2Di& rectangle, Int radius, const Vector2i& imageSize = Vector2i{}) deprecated in 2019.01
Create a signed distance field.

Enum documentation

enum class Magnum::TextureTools::AtlasLandfillFlag new in Git master

Landfill texture atlas packer behavior flag.

Enumerators
RotatePortrait

Rotate all textures to a portrait orientation. Only one of AtlasLandfillFlag::RotatePortrait and RotateLandscape can be set. If neither is set, keeps the original orientation.

RotateLandscape

Rotate all textures to a landscape orientation. Only one of AtlasLandfillFlag::RotatePortrait and RotateLandscape can be set. If neither is set, keeps the original orientation.

WidestFirst

Sort same-height textures widest first. Only one of AtlasLandfillFlag::WidestFirst and NarrowestFirst can be set. If neither is set, textures of the same height keep their original order.

NarrowestFirst

Sort same-height textures narrowest first. Only one of AtlasLandfillFlag::WidestFirst and NarrowestFirst can be set. If neither is set, textures of the same height keep their original order.

ReverseDirectionAlways

By default, when reaching an edge, the next row is filled in reverse direction only if the previous row ended lower than it started. If it ended at the same height or higher, the next row is filled in the same direction again in an attempt to level it out with decreasing heights. Enabling this flag reverses the fill direction always.

Typedef documentation

Function documentation

Debug& Magnum::TextureTools::operator<<(Debug& output, AtlasLandfillFlag value)

Debug output operator.

Debug& Magnum::TextureTools::operator<<(Debug& output, AtlasLandfillFlags value)

Debug output operator.

std::vector<Range2Di> Magnum::TextureTools::atlas(const Vector2i& atlasSize, const std::vector<Vector2i>& sizes, const Vector2i& padding = {})

Pack textures into a texture atlas.

Parameters
atlasSize Size of the resulting atlas
sizes Sizes of all textures in the atlas
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.

Int Magnum::TextureTools::atlasArrayPowerOfTwo(const Vector2i& layerSize, const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector3i>& offsets) new in Git master

Pack square power-of-two textures into a texture atlas array.

Parameters
layerSize in Size of a single layer in the texture atlas
sizes in Sizes of all textures in the atlas
offsets out Resulting offsets in the atlas
Returns Total layer count

The sizes and offsets views are expected to have the same size. The layerSize is expected to be non-zero, square and power-of-two. All items in sizes are expected to be non-zero, square, power-of-two and not larger than layerSize. With such constraints the packing is optimal with no wasted space in all but the last layer. Setting layerSize to the size of the largest texture in the set will lead to the least wasted space in the last layer.

Example usage is shown below. Calculating a texture coordinate transformation matrix for a particular image can then be done with atlasTextureCoordinateTransformation(), see its documentation for how to calculate and apply the matrix to either the mesh directly or to a material / shader.

Containers::ArrayView<const ImageView2D> input;
Containers::StridedArrayView1D<const Vector2i> sizes =
    stridedArrayView(input).slice(&ImageView2D::size);
Containers::Array<Vector3i> offsets{NoInit, input.size()};

/* Size the atlas based on the largest image and fill it */
Vector2i layerSize = Math::max(sizes);
Int layerCount = TextureTools::atlasArrayPowerOfTwo(layerSize, sizes, offsets);

/* Copy the image data to the atlas, assuming all are RGBA8Unorm as well */
Vector3i outputSize{layerSize, layerCount};
Image3D output{PixelFormat::RGBA8Unorm, outputSize,
    Containers::Array<char>{ValueInit, std::size_t(outputSize.product()*4)}};
Containers::StridedArrayView3D<Color4ub> dst = output.pixels<Color4ub>();
for(std::size_t i = 0; i != input.size(); ++i) {
    Containers::StridedArrayView3D<const Color4ub> src = input[i].pixels<Color4ub>();
    Utility::copy(src, dst.sliceSize(
        {std::size_t(offsets[i].z()),
         std::size_t(offsets[i].y()),
         std::size_t(offsets[i].x())}, src.size()));
}

The algorithm first sorts the textures by size using std::stable_sort(), which is usually $ \mathcal{O}(n \log{} n) $ , and then performs the actual atlasing in a single $ \mathcal{O}(n) $ operation. Memory complexity is $ \mathcal{O}(n) $ with $ n $ being a sorted copy of the input size array, additionally std::stable_sort() performs its own allocation. See the Zero-waste single-pass packing of power-of-two textures article for a detailed description of the algorithm.

See the AtlasLandfill class 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.

Int Magnum::TextureTools::atlasArrayPowerOfTwo(const Vector2i& layerSize, std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector3i>& offsets) new in Git master

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

Containers::Pair<Int, Containers::Array<Vector3i>> Magnum::TextureTools::atlasArrayPowerOfTwo(const Vector2i& layerSize, std::initializer_list<Vector2i> sizes)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

Matrix3 Magnum::TextureTools::atlasTextureCoordinateTransformation(const Vector2i& atlasSize, const Vector2i& size, const Vector2i& offset) new in Git master

Calculate a texture coordinate transformation matrix for an atlas-packed item.

Together with atlasTextureCoordinateTransformationRotatedCounterClockwise() or atlasTextureCoordinateTransformationRotatedClockwise() meant be used to adjust mesh texture coordinate attributes after packing textures with AtlasLandfill or atlasArrayPowerOfTwo(). Expects that size and offset fit into the atlasSize, the rotated variants expect that size with coordinates flipped and offset fit into the atlasSize.

With a concrete atlasSize, sizes being the input sizes passed to AtlasLandfill::add() (i.e., without any potential rotations applied yet), and offsets and rotations being the output, the usage is as follows:

Matrix3 matrix = (rotations[i] ?
    TextureTools::atlasTextureCoordinateTransformationRotatedCounterClockwise :
    TextureTools::atlasTextureCoordinateTransformation
)(atlasSize, sizes[i], offsets[i]);

The resulting matrix can be then directly used to adjust texture coordinates, like below with MeshTools::transformTextureCoordinates2DInPlace() on a Trade::MeshData:

Trade::MeshData mesh = ;

MeshTools::transformTextureCoordinates2DInPlace(mesh, matrix);

Alternatively, for example in cases where a single mesh is used with several different textures, the transformation can be applied at draw time, such as with Shaders::FlatGL::setTextureMatrix(). In case there's already a texture transformation matrix being applied when drawing, the new transformation has to happen after, so multiplied from the left side. For example with a Trade::MaterialData that contains a Trade::MaterialAttribute::TextureMatrix:

Trade::MaterialData material = ;

Matrix3& materialMatrix =
    material.mutableAttribute<Matrix3>(Trade::MaterialAttribute::TextureMatrix);
materialMatrix = matrix*materialMatrix;

Matrix3 Magnum::TextureTools::atlasTextureCoordinateTransformationRotatedCounterClockwise(const Vector2i& atlasSize, const Vector2i& size, const Vector2i& offset) new in Git master

Calculate a texture coordinate transformation matrix for an atlas-packed item rotated counterclockwise.

Like atlasTextureCoordinateTransformation(), but swaps X and Y of size and produces a matrix that rotates the texture coordinates 90° counterclockwise. The lower left corner of the input becomes a lower right corner. See atlasTextureCoordinateTransformationRotatedClockwise() for a clockwise variant.

Matrix3 Magnum::TextureTools::atlasTextureCoordinateTransformationRotatedClockwise(const Vector2i& atlasSize, const Vector2i& size, const Vector2i& offset) new in Git master

Calculate a texture coordinate transformation matrix for an atlas-packed item rotated clockwise.

Like atlasTextureCoordinateTransformation(), but swaps X and Y of size and produces a matrix that rotates the texture coordinates 90° clockwise. The lower left corner of the input becomes an upper left corner. See atlasTextureCoordinateTransformationRotatedClockwise() for a counterclockwise variant.

void Magnum::TextureTools::distanceField(GL::Texture2D& input, GL::Texture2D& output, const Range2Di& rectangle, Int radius, const Vector2i& imageSize = Vector2i{})

Create a signed distance field.