четверг, 3 мая 2012 г.

Компонент для работы с растровыми шрифтами

Продолжая работу над портом движка Flixel на Haxe, столкнулся с необходимостью оптимизации работы с текстом. Проблема заключается в следующем: стандартный класс движка FlxText сильно завязан на использование метода draw(), и при обновлении текста создается новый экземпляр класса BitmapData, еще одним недостатком является то, что для каждого такого текстового объекта создается свой экземпляр класса TileSheet (если в качестве целевой платформы установлен c++, neko или android). В общем видно, что такая реализация класса для отображения текста довольно неудачна и нужно искать другие решения. И тут на сцену выходят растровые шрифты -- каждый символ такого шрифта представляет собой по-сути простое изображение и работать с ними можно точно также, как и с обыкновенными изображениями. В наборе классов Flixel Power Tools как раз есть такой класс FlxBitmapFont и я его уже давно портировал, но в нем также присутствует довольно раздражающее меня ограничение -- он поддерживает только моноширинные шрифты (все символы таких шрифтов должны иметь одинаковую ширину), а мне же хотелось чего-нибудь более универсального. И недавно мне на глаза попался движок Pixelizer (упоминал его в одном из предыдущих сообщений), в котором очень понравилась реализация такого компонента: в нем уже был такой функционал, как изменение цвета шрифта, обводка и тени, поддержка многострочного текста и др. Встала задача по переводу этого добра на другие рельсы:
  1. Реализация в виде отдельного компонента, не завязанного ни на какой из движков -- такой класс нужен был для того, чтобы наконец-то закончить портирование прелоадера Flixel.
  2. Реализация в виде плагина для Flixel, которую можно использовать непосредственно в играх.
Самая первая версия порта была готова через 1-2 дня, но сразу же вылезли проблемы с производительностью: для отрисовки текста использовался метод draw(), позволяющий задавать тексту цветовую трансформацию и прочие параметры. Нужно было искать другой способ отрисовки. Подумав немного и посмотрев на реализацию спрайтов в Flixel, пришел к следующему решению: на входе имеем набор растровых изображений, представляющих собой исходные изображения символов растрового шрифта, а для того, чтобы отображать масштабированный и "окрашенный" текст создается дополнительный массив трансформированных изображений символов, таким образом, мы можем использовать уже гораздо быстрый метод copyPixels() (после такого преобразования нагрузка на процессор при отрисовке текста снизилась в 2-4 раза).
Завершив с оптимизацией, я приступил к реализации дополнительных "фишек":
  • настройка размера шрифта
  • настройка расстояний как между символами, так и между строками текста
  • настройка переноса слов
На это ушло еще несколько дней, скачать и посмотреть что получилось можно со странички репозитория - https://github.com/Beeblerox/BitmapFont
На данный момент там присутствуют три различные версии описываемого компонента:
  • кросс-платформенная версия под Haxe (данная версия используется в порте класса FlxPreloader). Работает на flash, c++ и neko; должно также работать  на iOS и Android, но не проверялось.
  • версия на AS3
  • версия для Flixel, также на AS3. В ней отсутствует возможность задания фона у текста, от этого функционала пришлось отказаться из-за проблем, которые возникнут при портировании на Haxe. Может быть, когда у метода drawTiles() класса TileSheet появиться возможность задавать масштаб тайлов по обеим осям (данный реквест можете помочь продавить в данной теме на форуме haxenme.org), я введу такой функционал, но пока оставлю так.
В каждой из папок есть примеры использования, так что проблем не должно возникнуть.
Версия для Flixel на Haxe находится в работе (на данный момент работает только под flash) и должна быть готова в течение недели (надеюсь, что так и будет :) ).

Напоследок расскажу как создать свой растровый шрифт, для этого вам понадобятся две вещи:
Пример изображения для растрового шрифта
  1.  Графический файл (обычно в формате png), содержащий изображения символов шрифта. Для того, чтобы программа смогла распознать границы изображений символов шрифта, изображение должно иметь фоновую заливку, а изображения символов не должны содержать данного цвета, плюс между символами дожен быть минимум 1 пиксел и каждый ряд изображений символов должен начинаться с одного уровня (см. рисунок выше) 
  2. Строку, содержащую символы, соответствующие изображению. Например, для приведенного выше изображения данная строка будет иметь вид: " !\"#$%&'()*+,-./" + "0123456789:;<=>?" + "@ABCDEFGHIJKLMNO" + "PQRSTUVWXYZ[]^_" + "abcdefghijklmno" + "pqrstuvwxyz{|}~\\"
Чтобы создать сам шрифт нужна всего одна строчка:
_font = new PxBitmapFont(fontBitmapData, fontString);
где fontBitmapData - изображение символов шрифта, а fontString - строка с символами шрифта.
Для создания текстового поля обязательными являются две строки:
_text = new PxTextField(_font); // непосредственно создание текстового поля
_text.setWidth(100); // задание ширины поля, по-умолчанию равна единице


Думаю, что разобраться со свойствами создаваемого таким образом текстового поля не составит особых затруднений (их названия говорят сами за себя и должны быть знакомы, если вы уже работали со стандартными текстовыми полями), поэтому на этом и заканчиваю. А если возникнут какие вопросы - то добро пожаловать в комменты :)

UPD: Стоило мне вчера написать, что не хватает в методе drawTiles() аргументов, задающих масштабные коэффициенты по обеим осям, как уже сегодня в svn-репозитории NME была добавлена эта возможность -- теперь каждому тайлу можно задавать матрицу 2d-преобразований, т.е. не только масштабы и поворот, но и отражение по оси, наклоны и т.д. Вот такие дела. 

Комментариев нет:

Отправить комментарий