Частина 9 - SDL2_ttf: Візуалізація шрифтів та тексти

Коротко: Що таке шрифт?

Шрифт відображає стиль відображення букв слова чи речення (наприклад, Arial, New Times Roman тощо). Ці відомості про стиль зберігаються у так званих файлах шрифтів. Існують різні стандарти, як зберегти шрифт у файл, але найважливішим стандартом файлу шрифтів є стандарт TrueType Font, звідси й назва SDL2_ttf. SDL2_ttf здатний працювати зі шрифтами TrueType. Типовим розширенням файлу шрифтів TrueType є ".ttf". FreeType 2.0 - це не стандартний файл шрифтів, а бібліотека програмного забезпечення для відтворення файлів шрифтів TrueType.

Шрифти в програмах

В останніх главах Ви багато чули про створення текстур із файлів зображень та про те, як ними маніпулювати та малювати. Але майже немає жодної програми, де б ви обходились без текстових повідомлень. Звичайно, ви можете надрукувати трохи тексту на зображенні та завантажити це зображення у свою програму, щоб додати до нього текстове повідомлення. Але що, якщо Ви захочете вбудувати функцію чату для своєї програми, де текстові повідомлення створюються динамічно? Або Ви хочете, щоб користувач міг записати своє ім’я в таблицю рекордів? Існують тисячі інших обставин, коли Вам потрібні динамічно генеровані тексти.

Модуль SDL2_ttf

Тут в гру вступає модуль SDL2_ttf. У нативній бібліотеці SDL2.0 ці функціі не реалізовані, але проект SDL надає офіційне розширення, яке називається SDL2_ttf для візуалізації шрифтів TrueType на основі FreeType project та їх реалізації FreeType 2.0.

Встановлення SDL2_ttf

Що стосується роботи з SDL2_image, вам доведеться додати до вашої системи деякі бібліотечні файли, щоб використовувати шрифти, оскільки SDL2_ttf не є частиною нативної SDL 2.0.

  • завантажте найсвіжішу версію бінарних файлів періоду виконання бібліотеки SDL2_ttf для Вашої системи
  • встановіть бібліотеку відповідно до вашої системи (Win32/64, Linux, Mac OS X)

Інструкції по встановленню SDL2_ttf для ОС Windows

Завантажте пакунок SDL2_ttf в залежності від Вашої системи (32 бітної або 64 бітної) та розпакуйте файл формату zip. В кінцевому результаті Ви отримаєте SDL2_ttf.dll та ще дві додаткових dll (zlib1.dll, libfreetype-6.dll) які необхідні для підтримки стиснення та рутини FreeType. Скопіюйте всі ці файли до Вашої системної папки, наприклад для Windows XP чи Windows 8.1 32 розрядної C:\WINDOWS\system32\. Якщо Ви не впевнені щодо системної папки, Ви можете скопіювати всі ці файлив ту ж саму папку, де розміщені сирцеві файли (.pas чи .pp) Вашої програми SDL 2.0.

Якщо Ви працюєте на іншій платформі (наприклад Mac OS X, Linux, …), будь ласка, перевірте посилання вказане вище щодо встановлення SDL2_ttf.

Дорога до текстів в SDL 2.0

Перед тим, як зануритись в код, давайте знову обговоримо дві концепції SDL 2.0 щодо збереження зображень в пам'яті. Нам краще працювати з текстурами, тому що вони мають переваги які ми обговорювали в попередній частині, особливо в Частині 5 – поверхні та текстури. В будь-якому випадку, в SDL2_ttf є декілька функцій для завантаження тексту як поверхні (деталі пізніше). Після цього нам потрібно конвертувати поверхню в текстуру як вже відомо. Подивіться на наступну схему щоб зрозуміти яким шляхом нам потрібно рухатись:

SDL2 text creation diagram
Створення поверхні з заданим текстом і створення з неї текстури. Тоді це може бути візуалізовано як вже відомо на екран.

Отже, згідно з діаграмою, шлях полягає в наступному. Якщо Ви придумали якийсь текст (лівий квадрат на схемі), спочатку створіть SDL_Surface, використовуючи одну з функцій, яку надає SDL2_ttf (нижній шлях до квадрата внизу на діаграмі). Далі Ви конвертуєте цю SDL_Surface в SDL_Texture функцією SDL_CreateTextureFromSurface (перейдіть від нижнього чотирикутника до верхнього квадрата на схемі). Нарешті візуалізуйте текст на екран як завгодно.

Примітка: “blitting” та SDL_Flip більше неможливі в SDL2 з поважних причин (продуктивності).

Давайте зараз перейдемо до коду.

program SDL_Fonts;

uses SDL2, SDL2_ttf;

var
  sdlSurface1 : PSDL_Surface;
  ttfFont : PTTF_Font;
  sdlColor1, sdlColor2 : TSDL_Color;
  sdlWindow1 : PSDL_Window;
  sdlRenderer : PSDL_Renderer;
  sdlTexture1 : PSDL_Texture;

begin

  //ініціалізація підсистеми відео
  if SDL_Init(SDL_INIT_VIDEO) < 0 then HALT;

  sdlWindow1 := SDL_CreateWindow('Window1', 50, 50, 500, 500, SDL_WINDOW_SHOWN);
  if sdlWindow1 = nil then HALT;

  sdlRenderer := SDL_CreateRenderer(sdlWindow1, -1, 0);
  if sdlRenderer = nil then HALT;


  //ініціалізація рушія шрифтів TrueType і завантаження шрифта
  if TTF_Init = -1 then HALT;
  ttfFont := TTF_OpenFont('C:\WINDOWS\fonts\Arial.ttf', 40);
  TTF_SetFontStyle(ttfFont, TTF_STYLE_UNDERLINE or TTF_STYLE_ITALIC);
  TTF_SetFontOutline(ttfFont, 1);
  TTF_SetFontHinting(ttfFont, TTF_HINTING_NORMAL);

  //визначення кольорів за RGB значенням
  sdlColor1.r := 255; sdlColor1.g := 0; sdlColor1.b := 0;
  sdlColor2.r := 0; sdlColor2.g := 255; sdlColor2.b := 255;

  //візуалізація тексту SDL_Surface
  sdlSurface1 := TTF_RenderText_Shaded(ttfFont, 'Hello World!', sdlColor1, sdlColor2);

  //конвертація SDL_Surface в SDL_Texture
  sdlTexture1 := SDL_CreateTextureFromSurface(sdlRenderer, sdlSurface1);

  //візуалізація текстури
  SDL_RenderCopy(sdlRenderer, sdlTexture1, nil, nil);
  SDL_RenderPresent(sdlRenderer);
  SDL_Delay(5000);

  //процедура очистки
  TTF_CloseFont(ttfFont);
  TTF_Quit;

  SDL_FreeSurface(sdlSurface1);
  SDL_DestroyTexture(sdlTexture1);
  SDL_DestroyRenderer(sdlRenderer);
  SDL_DestroyWindow(sdlWindow1);

  //вивантаження підсистеми відео
  SDL_Quit;
end.

Кінцевий результат повинен бути приблизно таким так: Текст "Hello world!" з'являється на п'ять секунд курсивом і підкресленим. Текст червоний, а фон блакитний. Наступний знімок екрану дасть уяву, чого слід очікувати.

Кінцевий скріншот

Почнемо з початкових рядків коду.

program SDL_Fonts;

uses SDL2, SDL2_ttf;

Програма називається “SDL_Fonts” і нам потрібний модуль SDL2_ttf додатково до SDL2 щоб отримати доступ до рушія TrueType font engine.

var
  sdlSurface1 : PSDL_Surface;
  ttfFont : PTTF_Font;
  sdlColor1, sdlColor2 : TSDL_Color;
  sdlWindow1 : PSDL_Window;
  sdlRenderer : PSDL_Renderer;
  sdlTexture1 : PSDL_Texture;

begin

  //ініціалізація підсистеми відео
  if SDL_Init(SDL_INIT_VIDEO) < 0 then HALT;

  sdlWindow1 := SDL_CreateWindow('Window1', 50, 50, 500, 500, SDL_WINDOW_SHOWN);
  if sdlWindow1 = nil then HALT;

  sdlRenderer := SDL_CreateRenderer(sdlWindow1, -1, 0);
  if sdlRenderer = nil then HALT;

Тоді ми підготуємо деякі нові типи змінних. Перш за все ми маємо вказівник, який називається “sdlSurface1” типу PSDL_Surface і вказує на SDL_Surface. Текстове повідомлення яке ми надаємо, спочатку буде візуалізоване в цю SDL_Surface.

Далі ми знаходимо змінну типу PTTF_Font variable яка називається “ttfFont”, яка вказує на TTF_Font що містить дані шрифту. Зазвичай ці дані шрифта містяться в шрифтовому файлі.

Нарешті, є дві змінні для кольору, що називаються “sdlColor1” та “sdlColor” вказівного типу PSDL_Color. Ці вказівники вказують на записи які містять дані про колір як складові значення r, b і g. Точне визначення буде показано, коли вони будуть використані в коді.

Що ж, наступні змінні відомі з повереднійх частин. Також вже відомі ініціалізація SDL 2.0 та створення вікна і візуалізатора. Тож давайте перейдемо до наступного фрагменту коду.


  //ініціалізація рушія шрифтів TrueType і завантаження шрифта
  if TTF_Init = -1 then HALT;
  ttfFont := TTF_OpenFont('C:\WINDOWS\fonts\Arial.ttf', 40);
  TTF_SetFontStyle(ttfFont, TTF_STYLE_UNDERLINE or TTF_STYLE_ITALIC);
  TTF_SetFontOutline(ttfFont, 1);
  TTF_SetFontHinting(ttfFont, TTF_HINTING_NORMAL);

Ініалізувати механізм шрифтів TrueType викликається функція

TTF_Init(): Integer

яка не містить жодних аргументів і повертає 0 при успіху та -1 при помилці. Немає специфічних кодів помилок, які б могли повернутись.

TTF_OpenFont(_file: PAnsiChar; ptsize: Integer): PTTF_Font

підготовлює шрифт. Перший параметр “_file” запитує абсолютний fшлях до файлу шрифта. Параметр “ptsize” запитує цілочисельне значення, яке визначає розмір шрифту. Чим більше значення, тим більші літери з'являться в результаті (так само, як відомо з текстових редакторів). В будь-якому випадку, якщо Ви оберете надто великий розмір шрифту, буде використано найбільший розмір.

Стилізація тексту в SDL 2.0

Процедурами

TTF_SetFontStyle(font: PTTF_Font; style: Integer),

TTF_SetFontOutline(font: PTTF_Font; outline: Integer)

та

TTF_SetFontHinting(font: PTTF_Font; hinting: Integer)

може бути виконано подальше формування зовнішнього вигляду тексту. Усі процедури повинні знати як перший аргумент, до якого шрифту їх слід застосовувати, тож у нашому випадку це “ttfFont”. Тоді стиль можна встановити константами, наведеними в наступній таблиці, які є зрозумілими для пояснення. За допомогою OR Ви можете навіть комбінувати їх, як показано в прикладі коду. Текст має бути виділено курсивом та підкреслено.

  1. TTF_STYLE_NORMAL
    • Не застосовується жодного стилю
  2. TTF_STYLE_BOLD
    • Встановлює жирний стиль
  3. TTF_STYLE_ITALIC
    • Встановлює нахилені літери
  4. TTF_STYLE_UNDERLINE
    • Підкреслити текст
  5. TTF_STYLE_STRIKETHROUGH
    • Перекреслити текст

Контур просто задається цифрою. Чим більша цифра, тим більшим буде контур букв. Якщо вам не потрібен контур, аргумент повинен бути 0.

Підказка встановлюється аналогічно стилю заздалегідь визначеними константами, показаними в наступній таблиці. Параметр підказки впливає на зовнішній вигляд букв та тексту. Як правило, це повинно призводити до більш чітких літер. В будь-якому випадку, налаштування яке найкраще підходить для певної ситуації і пристрою відображення може відрізнятися. Тож якщо Ви не впевнені, який параметр використовувати, Вам слід вибрати TTF_HINTING_NORMAL. Якщо Ви не викликаєте цю процедуру, це налаштування буде типовим.

  1. TTF_HINTING_NORMAL
    • Застосовується звичайна підказка
  2. TTF_HINTING_LIGHT
    • Застосовується легка підказка
  3. TTF_HINTING_MONO
    • Я гадаю, моноширинні символи, тож усі символи мають однакові проміжкиміж собою
  4. TTF_HINTING_NONE
    • Підказка не застосовується

Всі ці три процедури мають функцію-лічильник, яка повертає встановлений стиль, контур та підказку як цілі числа, визначені як:

TTF_GetFontStyle(font: PTTF_Font): Integer,

TTF_GetFontOutline(font: PTTF_Font): Integer,

TTF_GetFontHinting(font: PTTF_Font): Integer.

Очевидно, єдиним параметром, який слід встановити, є шрифт, стиль, контур чи підказки якого ви хочете знати. Однак у прикладі ці функції не продемонстровано.

Розфарбовування тексту за допомогою TSDL_Color / PSDL_Color

На нас чекають наступні рядки коду.


  //визначення кольорів за RGB значенням
  sdlColor1.r := 255; sdlColor1.g := 0; sdlColor1.b := 0;
  sdlColor2.r := 0; sdlColor2.g := 255; sdlColor2.b := 255;

Тут ми починаємо розглядати розфарбовування тексту. Першим кольором, встановленим у “sdlColor1” типу TSDL_ColorTSDL_Color:

TSDL_Color і RGB триплети

  TSDL_Color = record
    r: UInt8;
    g: UInt8;
    b: UInt8;
    a: UInt8;
  end;

Запис TSDL_Color має чотири поля. Для адитивного змішування кольорів у Вас часто є червоний, зелений і синій (RGB триплет). Ці три основні кольори здатні генерувати будь-який інший колір шляхом змішування у відповідних пропорціях. Наприклад 100% червоний та 0% зелений та 0% синій призведуть до червоного. Але 100% червоний, 100% зелений і 0% синій призведуть до жовтого. Якщо всі три кольори на 100%, Ви отримаєте білий. Якщо всі вони становлять 0%, ви отримаєте чорний колір. Ви можете помітити змінну типу UInt8, що означає 8-бітове ціле число без знака. З цього випливає, що значення можуть коливатися в межах від 0 до 255, де 0 дорівнює 0% і 255 дорівнює 100% відповідного кольору. Отже, можна створити 2563 = 16,777,216 окремих кольорів.

Поле a призначене для альфа-значення, яке визначає частку прозорості. За замовчуванням встановлено непрозорість (значення 255).

Створення тексту

//візуалізація тексту в SDL_Surface
  sdlSurface1 := TTF_RenderText_Shaded(ttfFont, 'Hello World!', sdlColor1, sdlColor2);

Нарешті текст, напр. знаменитий "Hello World!" з обраними кольорами. Для цього існує кілька функцій, і лише одна з них обрана для демонстрації того, як взагалі працює створення тексту. Вибраною функцією є

TTF_RenderText_Shaded(font: PTTF_Font; text: PAnsiChar; fg, bg: TSDL_Color): PSDL_Surface

Вона вимагає чотирьох аргументів і повертає PSDL_Surface (не PSDL_Texture!), вказівник на SDL_Surface. Перший аргумент - це шрифт, який Ви хотіли б використовувати. Далі - власне текст. Текст може бути записаний безпосередньо, як показано в прикладі, але також може зберігатися у змінній типу PAnsiChar.

PAnsiChar і тип String Pascal

Зазвичай в мові програмування Pascal використовуються змінні типу String, тож чому тут ми маємо PAnsiChar? Що ж, оскільки SDL написано на C/C++, і там для зберігання і роботи з текстом типово використовуються рядки з нульовим закінченням, це збережено для кращої сумісності і полегшення трансляції SDL 2.0 для компіляторів Pascal. В принципі це невелика справа використовувати PAnsiChar замість String, хоча програмісти на Pascal надають перевагу використання змінних типу String.

Останні, але не менш важливі є два аргументи «fg» та «bg», що означає передній та задній план. Тут ми вставляємо кольори, які ми визначили раніше. Ось і все.

Якість тексту і швидкість візуалізації

Гаразд, як вже згадувалося раніше, є більше функцій для створення SDL_Surface з текстом. Загалом існує три групи функцій за їх властивостями. Загальна схема імен така:

TTF_Render[кодування]_[суфікс]

Ті, що мають суфікс “Solid”, створюються дуже швидко, але їх якість низька. Використовуйте їх при візуалізації тексту, що швидко змінюється (наприклад, рахунок в симуляції пінболу). Друга група має суфікс suffix “Shaded”. Вони візуалізують повільніше і мають колір фону, але мають набагато кращу якість. Остання група функцій має суфікс “Blended”. Вони якісні, але повільно візуалізуються. Використовуйте їх для більш статичного тексту, який не так часто змінюється.

Для кожної групи якості та функцій ви знайдете “Text”, “UTF8”, “UNICODE” та “Glyph” в частині кодування імені функцій відразу після “TTF_Render”. «Текст», «UTF8» та «UNICODE» - це три різні типи кодування тексту. “Текст” відповідає кодуванню Latin1, “UTF8” - кодуванню UTF-8, а “UNICODE” - кодуванню Unicode. Який тип вибрати, залежить від того, який тип символів (наприклад, кириличні літери, латинські символи, китайські символи) ви збираєтесь використовувати. Якщо ви не впевнені, яку з цих функцій використовувати, скористайтеся версією “UNICODE”.

Для візуалізації одного символу за кодом Unicode використовуйте функцію, яка містить “Glyph” як суфікс до “TTF_Render”.

Наостанок я хотів би згадати спеціальну функцію, яка якраз доступна у високоякісному режимі “Blended”. Вона має суфікс “_Blended_Wrapped”. Додатково до звичайних параметрів є параметр wrapLength типу UInt32 (32-бітове ціле число без знака). Тут Ви можете задати цілочисельне значення, яке визначає кількість пікселів, поки текст не буде розбито на слова для переносу. Отже, у нашому випадку при ширині вікна 500 пікселів установка wrapLength до 250, наприклад, призведе до перенесення слів, коли слово перевищить 250 пікселів.І

Огляд: режими візуалізації

Наступний список узагальнює всі функції та найважливіші властивості для трьох різних режимів візуалізації.

  1. Суцільний
    • прозорість за ключовим кольором (0 піксель)
    • дуже швидко, але низької якості
    • 8-бітна палітризована RGB поверхня
    • Функції
      • TTF_RenderText_Solid(font: PTTF_Font; text: PAnsiChar; fg: TSDL_Color): PSDL_Surface
      • TTF_RenderUTF8_Solid(font: PTTF_Font; text: PAnsiChar; fg: TSDL_Color): PSDL_Surface
      • TTF_RenderUNICODE_Solid(font: PTTF_Font; text: PUInt16; fg: TSDL_Color): PSDL_Surface
      • TTF_RenderGlyph_Solid(font: PTTF_Font; ch: UInt16; fg: TSDL_Color): PSDL_Surface
  2. Затінений
    • згладжування
    • повільніше, ніж суцільний рендеринг, але якісніший
    • 8-бітна палітризована RGB поверхня
    • Функції
      • TTF_RenderText_Shaded(font: PTTF_Font; text: PAnsiChar; fg, bg: TSDL_Color): PSDL_Surface
      • TTF_RenderUTF8_Shaded(font: PTTF_Font; text: PAnsiChar; fg, bg: TSDL_Color): PSDL_Surface
      • TTF_RenderUNICODE_Shaded(font: PTTF_Font; text: PUInt16; fg, bg: TSDL_Color): PSDL_Surface
      • TTF_RenderGlyph_Shaded(font: PTTF_Font; ch: UInt16; fg, bg: TSDL_Color): PSDL_Surface
  3. Змішаний
    • прозорість (альфа канал)
    • згладжування
    • повільний, але дуже якісний
    • 32-бітна непалітризована (RGBA) поверхня
    • Функції
      • TTF_RenderText_Blended(font: PTTF_Font; text: PAnsiChar; fg: TSDL_Color): PSDL_Surface
      • TTF_RenderUTF8_Blended(font: PTTF_Font; text: PAnsiChar; fg: TSDL_Color): PSDL_Surface
      • TTF_RenderUNICODE_Blended(font: PTTF_Font; text: UInt16; fg: TSDL_Color): PSDL_Surface
      • TTF_RenderText_Blended_Wrapped(font: PTTF_Font; text: PAnsiChar; fg: TSDL_Color; wrapLength: UInt32): PSDL_Surface
      • TTF_RenderGlyph_Blended(font: PTTF_Font; ch: UInt16; fg: TSDL_Color): PSDL_Surface

Фсі ці функції повернуть nil при помилці створення SDL_Surface.

Конвертація SDL_Surface в SDL_Texture

Добре, тепер перейдемо до наступних рядків коду.

//конвертація SDL_Surface в SDL_Texture
  sdlTexture1 := SDL_CreateTextureFromSurface(sdlRenderer, sdlSurface1);

//візуалізація текстури
  SDL_RenderCopy(sdlRenderer, sdlTexture1, nil, nil);
  SDL_RenderPresent(sdlRenderer);
  SDL_Delay(5000);

У попередній таблиці Ви побачили всі функції для створення SDL_Surface з певним текстом. Тепер нам потрібно перетворити його на SDL_Texture. Як відомо, це виконує функція SDL_CreateTextureFromSurface.

Тоді результат буде візуалізовано у вікно. Для простоти третій та четвертий аргументи для SDL_RenderCopy() встановлено в nil, що означає, що SDL_Texture з текстом буде розтягнуто до розмірів вікна. Візуалізований результат відображається протягом 5000 мс (5 секунд).

Ми можемо перейти до процесу очищення.

//процедура очистки
  TTF_CloseFont(ttfFont);
  TTF_Quit;

  SDL_FreeSurface(sdlSurface1);
  SDL_DestroyTexture(sdlTexture1);
  SDL_DestroyRenderer(sdlRenderer);
  SDL_DestroyWindow(sdlWindow1);

//вивантаження підсистеми відео
  SDL_Quit;

Вся виділена пам’ять має бути звільнена. Шрифт звільняється процедурою

TTF_CloseFont(font: PTTF_Font)

і рушій TrueType engine зупиняється процедурою

TTF_Quit.

“sdlSurface1” звільняється процедурою

SDL_FreeSurface(surface: PSDL_Surface)

і текстура, візуалізатор та вікно звільняються як нам вже відомо. Після цього SDL 2.0 завершує роботу.

Примітка: НЕ створюйте текстові поверхні на льоту!

Що стосується завантаження файлів растрових зображень, тоді як можна безпосередньо завантажити поверхню до текстури без оголошення поверхні, поєднуючи створення поверхні із створенням текстури наступним чином:

sdlTexture1 := SDL_CreateTextureFromSurface(sdlRenderer, TTF_RenderText_Shaded(ttfFont, 'Hello World!', sdlColor1, sdlColor2));

Не робіль цього, тому що у Вас не буде обробника (вказівника на поверхню) щоб опісля звільнити пам'ять, зайняту поверхнею.

Приємної роботи з текстами :-).

← Попередня частина | Наступна частина →

Немає коментарів:

Дописати коментар