Упаковщик текстур

В OpenGL привязывается текстура и делается визуализация, затем привязывается другая текстура и делается еще дополнительная визуализация и так далее. Привязывание текстуры является относительно дорогим, поэтому идеальным считается хранение большого числа маленьких изображений на одном большом изображении, привязывание один раз большой текстуры, а затем многократная визуализация отдельных частей. libGDX имеет класс TexturePacker, который представляет собой приложение командной строки для упаковки множества маленьких изображений в одно большое. Он хранит местоположение маленьких изображений, так что в приложении на них можно легко сослаться по имени, используя класс TextureAtlas.

TexturePacker использует несколько алгоритмов запаковки, но самый важный основан на алгоритме максимальных прямоугольников. TexturePacker так же использует подбор грубой силой, запаковку с многочисленными эвристиками различных размеров и затем выбирает наиболее эффективный результат.

Запуск TexturePacker

Класс TexturePacker находится в проекте gdx-tools. Он может быть запущен из исходного кода с помощью Eclipse:

import com.badlogic.gdx.tools.texturepacker.TexturePacker;
public class MyPacker {
    public static void main (String[] args) throws Exception {
        TexturePacker.process(inputDir, outputDir, packFileName);
    }
}

Если вы используете gradle и класс TexturePacker не найден, добавьте gdx-tools в ваш build.gradle файл.

Его также можно запустить из nightly сборки:

// OS X / Linux
java -cp gdx.jar:extensions/gdx-tools/gdx-tools.jar com.badlogic.gdx.tools.texturepacker.TexturePacker inputDir [outputDir] [packFileName]

// WINDOWS
java -cp gdx.jar;extensions/gdx-tools/gdx-tools.jar com.badlogic.gdx.tools.texturepacker.TexturePacker inputDir [outputDir] [packFileName]
Производительность TexturePacker

TexturePacker выполняется значительно быстрее используя Java 1.7+, особенно при упаковке сотни входных изображений.

Структура директорий

TexturePacker может запаковать все изображения для приложения за один раз. Указывая директорию, он будет рекурсивно сканировать файлы изображений. Для каждой директории с изображениями, он запаковывает изображения в одну большую текстуру, называемою страницей. Если изображения в директории не помещаются в максимальных размер одной страницы, то тогда используется несколько страниц.

Изображения в той же директории будут в том же наборе страниц. Если все изображения помещаются на одну страницу, тогда не будут использоваться поддиректории, потому что с одной страницей приложение выполнит только одну привязку текстуры. В противном случае, поддиректории могут быть использованы для разделения взаимосвязанных изображений и уменьшения привязок текстур. Например, приложение может захотеть поместить все игровые изображения в отдельную директорию, от изображений для меню паузы, так как существует два набора изображений, то визуализируются они последовательно: все игровые изображения визуализируются (одна привязка), затем меню паузы визуализируется сверху (другая привязка). Если изображения были бы в одной директории, что привело к более чем одной странице, тогда каждая страница могла содержать смесь игровых изображений и меню паузы. Это может привести к нескольким привязкам текстур для визуализации игры и меню паузы, вместо одной привязки для соответствующих изображений.

Поддиректории также полезны для группировки изображений с соответствующими настройками текстуры. Настройки вроде формата памяти (RGBA, RGB и так далее) и фильтрации (nearest, linear и так далее) для текстуры. Изображения, нуждающиеся в различных текстурных настройках должны быть в отдельных страницах, следовательно, должны быть в отдельных директориях.

Чтобы использовать поддиректории для организации вывода TexturePacker без набора страниц для каждой директории, смотрите настройку combineSubdirectories.

Чтобы избежать использования путей поддиректорий в именах изображений в файле атласа, смотрите настройку flattenPaths.

Конфигурация

Каждая директория может содержать "pack.json" файл, который является JSON представлением TexturePacker.Settings класса. Каждая поддиректория наследует все параметры настроек родительской директории. Любые настройки параметров в поддиректории переопределяют параметры, наследованные от родительской директории.

Ниже пример JSON со всеми доступными параметрами настройки и значениями по умолчанию для каждого из них. Не нужно указывать все настройки, некоторые или даже все настройки могут быть опущены. Если для директории или любой родительской директории настройки не указаны, то используется значения по умолчанию.

{
    pot: true,
    paddingX: 2,
    paddingY: 2,
    bleed: true,
    edgePadding: true,
    duplicatePadding: false,
    rotation: false,
    minWidth: 16,
    minHeight: 16,
    maxWidth: 1024,
    maxHeight: 1024,
    square: false,
    stripWhitespaceX: false,
    stripWhitespaceY: false,
    alphaThreshold: 0,
    filterMin: Nearest,
    filterMag: Nearest,
    wrapX: ClampToEdge,
    wrapY: ClampToEdge,
    format: RGBA8888,
    alias: true,
    outputFormat: png,
    jpegQuality: 0.9,
    ignoreBlankImages: true,
    fast: false,
    debug: false,
    combineSubdirectories: false,
    flattenPaths: false,
    premultiplyAlpha: false,
    useIndexes: true,
    limitMemory: true,
    grid: false,
    scale: [ 1 ],
    scaleSuffix: [ "" ]
}
Формат JSON в libGDX

JSON формат в libGDX минимальный, так что двойные кавычки в большинстве случаев являются необязательными.

Настройки

Поле Описание Значение по умолчанию
pot Если true, то страницы будут иметь размер степени двойки. true
paddingX Число пикселей между упакованными изображениями на оси X. 2
paddingY Число пикселей между упакованными изображениями на оси Y. 2
bleed Если true, RGB значения для прозрачных пикселей устанавливаются на основе RGB значений ближайших непрозрачных пикселей. Это предотвращает артефакты фильтрации, когда RGB значения подобранны для прозрачных пикселей. true
edgePadding Если true, то половина paddingX и paddingY будет использована вокруг краев упакованной текстуры. true
duplicatePadding Если true, то пиксели краев копируются в отступы. Отношение paddingX/Y должно быть больше или равно 2. true
rotation Если true, TexturePacker будет пытаться упаковать более эффективно, вращая изображения на 90 градусов. Приложение должно особым образом позаботиться о правильной визуализации таких регионов. false
minWidth Минимальная ширина выходных страниц. 16
minHeight Минимальная высота выходных страниц. 16
maxWidth Максимальная ширина выходных страниц. Значение 1024 является безопасным для всех устройств. Очень старые устройства могут иметь пониженную производительность при ширине более 512. 1024
maxHeight Максимальная высота выходных страниц. Значение 1024 является безопасным для всех устройств. Очень старые устройства могут иметь пониженную производительность при высоте более 512. 1024
square Если true, выходных страницы будут иметь одинаковую ширину и высоту. false
stripWhitespaceX Если true, пустые пиксели на левой и правой границе входного изображения будут удалены. Приложение должно особым образом позаботиться о правильной визуализации таких регионов. false
stripWhitespaceY Если true, пустые пиксели на верхней и нижней границе входного изображения будут удалены. Приложение должно особым образом позаботиться о правильной визуализации таких регионов. false
alphaThreshold От 0 до 255. Альфа ниже данных значений будет рассматриваться как ноль. 0
filterMin Фильтр минимизации для текстуры. Nearest
filterMag Фильтр увеличения для текстуры. Nearest
wrapX Настройки обертывания в X направлении для текстуры. ClampToEdge
wrapY Настройки обертывания в Y направлении для текстуры. ClampToEdge
format Формат текстуры, который будет использован в памяти. RGBA8888
alias Если true, тогда два изображения, совпадающие попиксельно будут упакованы только один раз. true
outputFormat Тип изображения для выходных страниц, "png" or "jpg". png
jpegQuality От 0 до 1. Настройка качества, если выходной формат "jpg. 0.9
ignoreBlankImages Если true, TexturePacker не будет добавлять регионы для совершенно пустых изображений. true
fast Если true, TexturePacker будет выполняться значительно быстрее, но без эффективной упаковки. false
debug Если true, тогда на выходных страницах рисуются линии, показывающие границы упакованных изображений. false
combineSubdirectories Если true, тогда директория содержащая файл настроек и все поддиректории будут упакованы, как если бы они были в одной директории. Любые файлы настроек в поддиректориях игнорируются. false
flattenPaths Если true, префиксы поддиректорий удаляются из имен изображений. Имена изображений должны быть уникальны. false
premultiplyAlpha Если true, RGB будет умножено на альфу. Смотрите об умножении на альфу. false
useIndexes Если false, имена изображений будут использованы без удаления суффикса индекса изображения. true
limitMemory Если true, в любой момент времени только одно изображения будет в памяти, но каждое изображение будет читаться дважды. Если false, все изображения будут храниться в памяти во время упаковки, но читаться только один раз. true
grid Если true, тогда изображения в порядке упаковываются в равномерную сетку. false
scale Изображения масштабируются с заданным значением, и на выходе получается весь атлас. [1]
scaleSuffix Для каждого масштаба и выходных файлов используется суффикс. Если значение опущено, файлы с несколькими масштабами будут выводиться с тем же именем в директории для каждого масштаба. [""]

9-Patch

Если имя файла изображения оканчивается на ".9" перед расширением, то изображение рассматривается как 9-patch. 9-patch изображения можно создать с помощью данного инструмента. Изображение должно иметь границу в одни прозрачный пиксель. Верхняя и левая граница могут иметь одну линию черных пикселей, определяющею информацию для разделения, то есть какие части будут растягиваться. Нижняя и правая граница тоже могут иметь линию черных пикселей, определяющею информацию об отступах, то есть, как будет размещен контент. Когда такое изображение запаковано, граница в 1 пиксель удаляется, а информация о разделении и отступах сохраняется в запакованный файл. TextureAtlas позволяет создать NinePatch экземпляр для региона, используя информацию о разделении.

Индексы изображений

Если имя файла изображения оканчивается подчеркиванием и затем числом (например, animation_23.png), то число считается "индексом" и хранится отдельно. Имя изображения храниться без подчеркивания и индекса. TextureAtlas допускает извлечение списка всех изображений с одинаковым именем, упорядоченных по индексу. Это позволяет легко упаковать анимацию без потери порядка кадров.

Упаковка

Класс TexturePacker находиться в gdx-tools.jar, который расположен в директории расширений в nightlies/releases zip файлах. Вам понадобиться TexturePacker в лишь в качестве инструмента для обработки файлов изображений для вашего приложения, вам не нужно иметь его в качестве зависимости для запуска приложения. Для запуска запаковщика вам необходимо оба gdx.jar и gdx-tools.jar файла.

// OS X / Linux
java -cp gdx.jar:gdx-tools.jar com.badlogic.gdx.tools.texturepacker.TexturePacker inputDir outputDir packFileName

// WINDOWS
java -cp gdx.jar;gdx-tools.jar com.badlogic.gdx.tools.texturepacker.TexturePacker inputDir outputDir packFileName

inputDir является корневой директорией, содержащей изображения. outputDir является директорией, где будет размещены упакованные изображения. packFileName это имя файл упаковки, используется префикс для выходных упакованных файлов изображений.

Если опустить outputDir, файлы будет размещены в новой директории, рядом с inputDir и суффиксом "-packed". Если опустить packFileName, то используется имя "pack".

В то время как упаковка текстур предназначена быть полностью автоматической, существует пользовательский интерфейс, предоставленный Obli (хотя немного устарел): TexturePacker GUI. Также существует коммерческий продукт на сайте texturepacker.com, который совершенно не связан с упаковщиком текстур из libGDX, имеющий пользовательский интерфейс, множество функций и хорошую документацию.

Автоматическая упаковка

Во время разработки может быть удобным иметь настольное приложение для запуска TexturePacker до начала игры:

public class DesktopGame {
    public static void main (String[] args) throws IOException {
        Settings settings = new Settings();
        settings.maxWidth = 512;
        settings.maxHeight = 512;
        TexturePacker.process(settings, "../images", "../game-android/assets", "game");

        new LwjglApplication(new Game(), "Game", 320, 480, false);
    }
}

Каждый раз, когда игра запускается, все изображения будут упакованы. Это может быть особенно удобно, когда художникам предоставляется сборка приложения, и они могут попробовать использовать новые изображения, даже не зная об упаковки. Если запаковывается много изображений, настройки быстрой работы могут быть полезны для избегания долгого ожидания.

При загрузки файлов из classpath, Eclipse как правило не отражает изменения файлов, которые обновляются внешне. Проект с измененными файлами в Eclipse должен быть обновлен вручную. Напротив, во время разработки файлы могут быть загружены через файловую систему, где нет такой проблемы.

TextureAtlas

TexturePacker в результате своей работы выдает директорию страниц изображений и текстовый файл, описывающий все изображения запакованные в страницы. Ниже показано, как использовать изображения в приложении:

TextureAtlas atlas;
atlas = new TextureAtlas(Gdx.files.internal("packedimages/pack.atlas"));
AtlasRegion region = atlas.findRegion("imagename");
Sprite sprite = atlas.createSprite("otherimagename");
NinePatch patch = atlas.createPatch("patchimagename");

TextureAtlas читает файл упаковки и загружает все изображения страницы. Можно получить TextureAtlas.AtlasRegions, являющиеся TextureRegions, которые предоставляют дополнительную информацию о запакованном изображении, такую как индекс кадра или пустую область, которая была удалена. Также могут быть созданы спрайты и 9-patch. Если пустая область была удалена, созданный спрайт будет являться TextureAtlas.AtlasSprite, который позволяет спрайту быть использованным как если бы была пустая область.

Метод findRegion() не очень быстрый, так что возвращаемое значение следует хранить, вместо вызова данного метода при каждом кадре. Также обратите внимание, что методы createSprite() и createNinePatch() создают новый экземпляр.

TextureAtlas содержит все текстуры страниц. Высвобождение TextureAtlas высвободит все текстуры страниц.