Познакомьтесь, SDL.

Урок 4. "Вывод текста".

Дополнение к уроку 3 Содержание Урок 5

   

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

Осталось сделать механизм, который будет переводить строку char * в последовательность из спрайтов. Можно сделать последовательность символов в исходном рисунке в соответствии со стандартной кодовой таблицей (например ASCII или KOI8-R): номер кадра с символом равен соответствующему коду этого символа. Это самый простой пример создаиня своего механизма печати. Я пересмотрел несколько подобных механизмов, некоторые были гораздо изощреннее и богаче по возможностям. Возможно этот метод пригодится, когда нужно использовать уникальный шрифт в какой-нибудь игре. Теперь давайте попробуем сделать простую функцию для вывода текста с помощью растрового шрифта, т.е. шрифт, который находится в файле изображении.
Для начала познакомимся с библиотекой SDL_image. Достать ее можно все там же, а именно на сайте http://www.libsdl.org. Установка чрезвычайно проста. Для работы с этой библиотекой нам нужно подключить файл заголовков #include "SDL_image.h" и при линковке указать библиотеку: -lSDL_image. Эта библиотека очень простая в использовании. Справки с ней не прилагается, но в файле заголовков очень понятные комментарии. И так, что же она нам дает. Как я уже упоминал в предыдущих уроках, штатными средствами SDL мы можем загружать только файлы BMP. Но при помощи SDL_image мы можем работать с практически любым форматом изображения. Файл нашего растрового шрифта имеет формат PNG (Portable Network Graphics). Чтобы загрузить такой файл мы используем функцию IMG_Load из библиотеки SDL_image. Вот ее прототип: SDL_Surface* IMG_Load(const char* file);
И все! Таким нехитрым способом можно загрузить на поверхность SDL_Surface любую графику. И давайте посмотрим на функцию загрузки шрифта:
/* ------------------------------------------------------ */
SDL_Surface* LoadFont(char* BitmapFont){

  SDL_Surface* temp;
  
  temp = IMG_Load(BitmapFont);
  if(temp == NULL){
  	printf("Can't load image %s\n",BitmapFont);
	return NULL;
  }
  
  SDL_SetColorKey(temp,SDL_SRCCOLORKEY,SDL_MapRGB(temp->format,255,255,255));
  
  return temp;

}
Функция очень простая. Она просто загружает изображение на поверхность и устанавливает прозрачность. Устанавливать прозрачность (Color Keying) мы уже умеем. Следует только добавить, что здесь у нас используется белый цвет в качестве прозрачного (триплет 255,255,255). Теперь об ограничениях нашего шрифта. Буквы могут быть только английские и только в верхнем регистре. Собственно, диапазон шрифта от A до Z. Цифр и других символов нет. Теперь немного теории о кодировках.
Как вы уже заметили, буквы в нашем шрифте идут по алфавитному порядку. И вот почему. В большинстве кодовых таблиц первые 128 символов занимают латинские буквы и стандартные и уравляющие символы. Остальные 128 используются для локализации, то есть содержат символы других языков. Вот кусок из кодовой таблицы KOI8-R:
 
. . . . . . ! " # $ % & ' (
) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; <
= > ? @ A B C D E F G H I J K L M N O P
Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d
e f g h i j k l m n o p q r s t u v w x
y z { | } ~ . . . . .
. . . . . .© ю а б ц д е ф г х
и й к л м н о п я р с т у ж в ь ы з ш э
щ ч ъ Ю А Б Ц Д Е Ф Г Х И Й К Л М Н О П
Я Р С Т У Ж В Ь Ы З Ш Э Щ Ч Ъ
 
Нетрудно заметить, что латинские буквы построены по алфавиту, а вот русские нет. Это небольшая проблема для адаптирования нашего примера для русского шрифта, хотя создать версию KOI8-R шрифта несложно. Поэтому мы рассмотрим только латинские буквы в верхнем регистре.
Далее код функции для рисования текста. Посмотрите на него внимательно, попытайтесь понять что происходит, а ниже мы разберем ее подробно.
/* ------------------------------------------------------ */
void DrawText(SDL_Surface* sFont, int x, int y, char * str){

  int i;
  SDL_Rect src, dest;
  
  for (i = 0; i < strlen(str); i++)
    {
      if (str[i] >= 'A' && str[i] <= 'Z')
	{
	  src.x = (str[i] - 'A') * 32;
	  src.y = 0;
	  src.w = 32;
	  src.h = 32;
	  
	  dest.x = x + (i * 32);
	  dest.y = y;
	  dest.w = 32;
	  dest.h = 32;
	  
	  SDL_BlitSurface(sFont, &src,
			  screen, &dest);
	}
    }
  SDL_Flip(screen);
  
}
В качестве аргументов передаем поверхность с загруженным изображением, координаты куда выводить текст и сам текст. Далее в цикле выводим по порядку каждый символ строки:
for (i = 0; i < strlen(str); i++)
Обязательно проверяем чтобы символ был в диапазоне A-Z:
if (str[i] >= 'A' && str[i] <= 'Z')
Далее расчитываем координату х в исходном изображении (откуда вырезать). У нас размеры буквы заведомо 32х32.
Выражение (str[i] -'A') * 32; вычисляет эту координату. Допустим у нас символ B (код 66), тогда получаем: (66-65)*32 = 32. То есть начинаем вырезать букву B в координатах x=32, y=0. Для выходных координат ( в которые выводить текст) рассчитываем позицию каждого следующего символа:
x + (i * 32); и поскольку индекс i самого первого символа равен нулю, то первый символ будет выведен в тех координатах, что поступают как аргументы.
И наконец, как же это добро использовать. Вот кусок кода, использующий наши функции:
 SDL_Surface* sFont;
 sFont = LoadFont("font.png");
 DrawText(sFont, 20, 150, "BITMAP FONT EXAMPLE");
Все чрезвычайно просто. Вы можете доработать эту функцию, расширив диапазон символов в шрифте. Например, можно создать шрифт на 256 символов, а вместо служебных и непечатных символов оставить пустое место. Код получается абсолютно несложным. Вы можете создавать шрифты любых форм, гарнитур и размеров. Посмотрите исходный код программы bmf.cxx (Bitmap Font) и попробуйте разобраться в нем. А теперь рассмотрим следующий способ.
 
Нет ничего лучше как использовать разнообразные и красивые TrueType шрифты, коих очень много можно найти в интернете и причем совершенно бесплатно. В системах Unix для этого существует библиотека FreeType, которая является бесплатной реализацией библиотеки для работы со шрифтами TrueType. В Win32 этот формат вообще является родным для системы. Но работа с библиотекой FreeType очень сложна. Разработчик библиотеки SDL Sam Lantinga был очень любезен и создал надстройку (wrapper) для библиотеки FreeType чтобы мы могли использовать ее в своих программах. Новая библиотека называется SDL_ttf. Найти ее можно все на том же узле http://www.libsdl.org, который уже наверно стал местом вашего паломничества. Устанавливается эта библиотека чрезвычайно просто для любой платформы, под которую есть реализация SDL. Единственный заголовочный файл SDL_ttf.h помещается вместе с другими заголовочными файлами SDL. То же самое и для файла библиотеки (libSDL_ttf.a и libSDL_ttf.so или SDL_ttf.lib и SDL_ttf.dll в Win32). Для работы с этой библиотекой вам нужно просто добавить заголовок: #include "SDL_ttf.h" и указать библиотеку для линковки: -lSDL_ttf. Вот и все подготовительные процедуры. Ну что ж, давайте попробуем наваять небольшой пример, чтобы убедиться, какая же простая эта библиотека.
Для начала необходимо инициализировать библиотеку SDL_ttf, наподобии того, как мы инициализируем SDL:

 TTF_Init();
 atexit(TTF_Quit);

Также указываем вызывать функцию TTF_Quit() перед выходом из программы. Теперь посмотрите на код функции для вывода текста. Это очень просто.
/* ------------------------------------------------------ */
void print_ttf(SDL_Surface *sDest, char* message, char* font, int size, SDL_Color color, SDL_Rect dest){

 TTF_Font *fnt = TTF_OpenFont(font, size);
 SDL_Surface *sText = TTF_RenderText_Blended( fnt, message, color);
 SDL_BlitSurface( sText,NULL, sDest,&dest );
 SDL_FreeSurface( sText );
 TTF_CloseFont( fnt );

}
Аргументы: SDL_Surface *sDest - поверхность, на которую будет скопирован текст; char* message - текст сообщения; char* font - имя файла TrueType шрифта (например arial.ttf); int size - размер шрифта; SDL_Color color - структура, содержащяя цвет шрифта, вот ее прототип:
typedef struct{
  Uint8 r;
  Uint8 g;
  Uint8 b;
  Uint8 unused;
} SDL_Color;
Параметр Uint8 unused не используется. И, наконец, SDL_Rect dest - содержит координаты, куда следует вывести текст (в этой структуре не используются элементы w и h). Чтобы вывести текст, мы загружаем шрифт:
TTF_Font *fnt = TTF_OpenFont(font, size);
Далее создаем поверхность, на которую отображаем текст при помощи загруженного шрифта:
SDL_Surface *sText = TTF_RenderText_Blended( fnt, message, color);
Функция TTF_RenderText_Blended() отображает сглаженный шрифт. Если вам нужно использовать шрифт без сглаживания, то используйте TTF_RenderText_Solid() с теми же самыми параметрами. Наконец, копируем надпись на поверхность, которую задали в аргументах (можно задать к примеру screen), удаляем временную поверхность sText, чтобы очистить память и закрываем шрифт.
Из main() вызываем функцию для отображения текста:
 SDL_Color clr = {255,50,40,0};
 SDL_Rect dest = {80, 120,0,0};
 print_ttf(screen, "SDL_ttf example", "courier.ttf", 46, clr, dest);

 clr.r = 255;
 clr.g = 255;
 clr.b = 0;
 dest.x = 80;
 dest.y = 250;
 print_ttf(screen, "Пример SDL_ttf", "courier.ttf", 46, clr, dest);
 
 SDL_Flip(screen);
Сначала создаем структуры цвета и координат и заполняем их значениями, а затем выводим надпись на английском и на русском. Заметьте, что на русском можно писать, если только у вас шрифт в соответствующей кодировке. Прилагаемый с исходниками шрифт Courier в кодировке KOI8-R.
Рекомендую загрузить исходники для этого урока и внимательно их изучить.
Дополнение к уроку 3 Содержание Урок 5

©opyleft PLG, 2003.