Руководство программиста в Photon (953920), страница 52
Текст из файла (страница 52)
PhAttach(NULL, NULL);
overlay_example();
fprintf(stderr, "Нормальное завершение\n");
}
Слои
Некоторые графические адаптеры позволяют вам накладывать множество «экранов» на один. Каждый такой накладываемый экран называется «слой».
Слои могут использоваться для совмещения независимых отображаемых элементов. Поскольку такое наложение осуществляется аппаратурой графического адаптера, оно может оказаться более эффективным, чем рисование всех необходимых элементов на одном слое. Так например, быстрый навигационный интерфейс может быть реализован как «прокручивающаяся» карта, отображаемая на одном слое – в «подложке», и всплывающие элементы графического интерфейса пользователя, такие как меню или Web-браузер – на другом слое.
Возможности отображения слоев отличаются в зависимости от графического контроллера и драйвера. Некоторые графические контроллеры не поддерживают слои. Различные слои на одном и том же дисплее могут иметь разные возможности. Следует использовать функцию PgGetLayerCaps() для того, чтобы определить, существует ли слой и каковы его возможности.
Слои нумеруются для каждого адаптера в отдельности начиная с нуля по возрастающей. Нумерация начинается со слоя, находящегося ниже всех остальных на данном адаптере.
Слой может быть активным (отображается) или неактивным (не отображается). Может оказаться невозможно активировать слой, если его конфигурирование не выполнено полностью (если, например, не указан формат слоя, или для него выделено недостаточно поверхностей). Сконфигурировать слой можно только тогда, когда он является неактивным. После смены видеорежима конфигурация всех слоев на данном адаптере сбрасывается в установки по умолчанию.
Изображения на всех активных слоях адаптера объединяются используя механизмы альфа-сопряжения, хроматического ключа, или оба механизма одновременно – таким образом формируется окончательное изображение на экране.
Поверхности
Изображение, отображаемое на слое, берется с одного или нескольких внеэкранных контекстов, также называемых поверхностями. Количество поверхностей, необходимых для слоя, определяется его форматом. Так например, для слоя с форматом Pg_LAYER_FORMAT_ARGB888 требуется только одна поверхность, тогда как для слоя с форматом Pg_LAYER_FORMAT_YUV420 требуется три поверхности для получения полного изображения. Формат слоя устанавливается при помощи PgSetLayerArg().
Окна просмотра
окно-источник и отображаемое окно
«Окно-источник» определяет прямоугольную область в данных поверхности. Окно используется для извлечения из данных поверхности того набора, который необходимо отобразить на слое.
«Отображаемое окно» определяет прямоугольную область на экране, где будет отображаться содержимое слоя.
Прокрутка и масштабирование, если поддерживаются слоем, могут осуществляться изменением этих типов «окон». Для перемещения по изображению на поверхности, связанной со слоем необходимо менять координаты «окна-источника». Для масштабирования – менять размеры «окон».
Вам необходимо установить цель действия этих преобразований используя PdSetTargetDevice().
API слоев
Программный интерфейс слоев состоит из:
PtGetLayerCaps() получить информацию о возможностях слоя
PgCreateLayerSurface() создать внеэкранную поверхность, отображаемую слоем
PgSetLayerSurface() отобразить внеэкранную поверхность на слое
PgSetLayerArg() настроить параметры слоя
PgLockLayer() захватить слой для эксклюзивного использования приложением
PgUnlockLayer() снять захват со слоя
PgLayerCaps_t структура данных, описывающая возможности слоя
API слоев в настоящий момент несовместимо с API оверлеев (PgCreateVideoChannel(), PgConfigScalerChannel(), PgNextVideoFrame(), и так далее). Не запускайте одновременно два приложения использующие эти два программные интерфейса.
Учтите:
-
Photon не может рисовать во внеэкранных поверхностях, формат которых отличен от текущего видеорежима. Таким образом может оказаться невозможно использовать функции рисования Photon на внеэкранном онтексте, полученном при помощи PgCreateLayerSurface(). Вместо этого приложение должно использовать PdGetOffscreenContextPtr(), для получения указателя на видеопамять и записывать данные напрямую.
-
Если приложение изменит внеэкранную поверхность основного экрана при помощи PgSetLayerSurface(), Photon всеравно продолжит рисовать в старую внеэкранную поверхность. Приложению необходимо сохранить указатель на старую поверхность и восстановить её как только оно перестанет использовать слой. Как это сделать показано ниже в примере.
Использование слоев
Чтобы использовать возможности слоев вы обычно должны сделать следующее:
-
Используйте PgGetLayerCaps() с постоянно увеличивающимся индексом слоя для определения возможностей оборудования (если вы еще их не знаете). Если PgGetLayerCaps() дает ошибку для любых значений – драйвер не поддерживает слои.
-
Если вы хотите предотвратить использование слоя другими приложениями – используйте PgLockLayer().
-
Создайте поверхности для отображения на слое и внеэкранные контексты – для отображения данных на поверхность при помощи PgCreateLayerSurface().
-
Вызовите PgSetLayerArg() с аргументом Pg_LAYER_ARG_LIST_BEGIN.
-
Вызовите PgSetLayerArg() для установки параметров слоя.
-
Вызовите PgSetLayerSurface() для отображения внеэкранного контекста поверхности на слое.
-
Вызовите PgSetLayerArg() для установки параметров слоя. Вы можете использовать Pg_LAYER_ARG_ACTIVE в качестве аргумента для отображения содержимого слоя на экране.
-
Вызовите PgSetLayerArg() с аргументом Pg_LAYER_ARG_LIST_END.
-
Если формат слоя RGB или PAL8 установите один из контекстов поверхности слоя текущим а затем используйте Pg* - функции для рисования во внеэкранный контекст.
-
Если формат слоя YUV, или подобный – вы чаще всего будете записывать данные непосредственно в буфер (например для воспроизведения видеопотока).
-
Если вы захватили слой для эксклюзивного использования вам необходимо снять блокировку при помощи PgUnlockLayer() прежде, чем ваше приложение выйдет.
Программа, приведенная ниже показывает использование программного интерфейса слоев.
Пример
#include <errno.h>
#include <stdio.h>
#include <Ph.h>
int
FindFormatIndex(int layer, unsigned int format)
{
PgLayerCaps_t caps;
int format_idx = 0;
while (PgGetLayerCaps(layer, format_idx, &caps) != -1) {
if (caps.format == format)
return format_idx;
format_idx++;
}
return -1;
}
int
main(int argc, char **argv)
{
/*
* For best results, these values should match your video mode.
*/
#define LAYER_FORMAT Pg_LAYER_FORMAT_ARGB8888
#define SURFACE_WIDTH 1024
#define SURFACE_HEIGHT 768
struct _Ph_ctrl *ph;
PgLayerCaps_t caps;
PdOffscreenContext_t *surf;
PdOffscreenContext_t *scr = NULL;
PhDrawContext_t *olddc;
PhRid_t driver_rid = -1;
int layer_idx = -1;
int format_idx = -1;
int active = 1;
int i;
/*
* Arguments:
* -d <driver region>
* -l <layer index>
*/
while ((i = getopt(argc, argv, "d:l:")) != -1) {
switch(i) {
case 'd': /* driver region */
driver_rid = atol(optarg);
break;
case 'l': /* layer index */
layer_idx = atoi(optarg);
break;
default:
break;
}
}
if (layer_idx == -1) {
printf("Specify layer index.\n");
exit(-1);
}
if (driver_rid == -1) {
printf("Specify graphics driver region.\n");
exit(-1);
}
ph = PhAttach(NULL, NULL);
if (ph == NULL) {
perror("PhAttach");
exit(-1);
}
if (-1 == PdSetTargetDevice(PhDCGetCurrent(), driver_rid)) {
perror("PdSetTargetDevice");
exit(-1);
}
/* Check if the layer supports the required format */
format_idx = FindFormatIndex(layer_idx, LAYER_FORMAT);
if (format_idx == -1) {
printf("Layer does not support format\n");
exit(-1);
}
/* Get the layer capabilities */
PgGetLayerCaps(layer_idx, format_idx, &caps);
if (caps.caps & Pg_LAYER_CAP_MAIN_DISPLAY) {
/* Save a reference to the current display surface */
scr = PdCreateOffscreenContext(0, 0, 0, Pg_OSC_MAIN_DISPLAY);
}
/* Allocate a surface for the layer */
surf = PgCreateLayerSurface(layer_idx, 0, format_idx,
SURFACE_WIDTH, SURFACE_HEIGHT, Pg_OSC_MEM_PAGE_ALIGN);
if (surf == NULL)
exit(-1);
/* Draw some stuff on the surface */
olddc = PhDCSetCurrent(surf);
PgSetFillColor(Pg_BLACK);
PgDrawIRect(0, 0, SURFACE_WIDTH-1, SURFACE_HEIGHT-1, Pg_DRAW_FILL);
PgSetFillColor(Pg_YELLOW);
PgDrawIRect(0, 0, 100, 100, Pg_DRAW_FILL);
PgSetFillColor(PgRGB(255,180, 0));
PgDrawIRect(70, 80, 600, 500, Pg_DRAW_FILL);
PhDCSetCurrent(olddc);
/* Lock the layer */
if (-1 == PgLockLayer(layer_idx))
exit(-1);
/* Start configuring arguments */
PgSetLayerArg(layer_idx, Pg_LAYER_ARG_LIST_BEGIN, 0, 0);
/* Select the layer format */
PgSetLayerArg(layer_idx, Pg_LAYER_ARG_FORMAT_INDEX,
&format_idx, sizeof(int));
/* This changes the current display surface */
PgSetLayerSurface(layer_idx, 0, surf);
PgSetLayerArg(layer_idx, Pg_LAYER_ARG_ACTIVE,
&active, sizeof(int));
/* Configure other arguments ... */
/* End configuration */
PgSetLayerArg(layer_idx, Pg_LAYER_ARG_LIST_END, 0, 0);
/* Application continues ... */
sleep(3);
/* Finished using layer; Restore the current display surface */
if (caps.caps & Pg_LAYER_CAP_MAIN_DISPLAY) {
PgSetLayerArg(layer_idx, Pg_LAYER_ARG_LIST_BEGIN, 0, 0);
PgSetLayerSurface(layer_idx, 0, scr);
PgSetLayerArg(layer_idx, Pg_LAYER_ARG_LIST_END, 0, 0);
}
PgUnlockLayer(layer_idx);
if (scr) PhDCRelease(scr);
PhDCRelease(surf);
PhDetach(ph);
exit(0);
}
Глава 19. Шрифты
Хотя библиотеки Photon'а и предлагают ряд функций по работе с шрифтами (см. главу "Pf – сервер шрифта" в "Справочнике библиотечных функций Photon'а"), большинство из них являются подпрограммами низкого уровня и, вероятно, Вам не понадобится их использовать. Эта глава описывает основы использования шрифтов.
Эта глава включает:
-
Метрики символа
-
Имена шрифтов
-
Написание текста в прямоугольной области
-
Пропорциональный текст, приводящий к ошибкам восстановления повреждений.
Метрики символа
Давайте начнём с некоторых определений:
Рис. 19-1. Метрики символа
Advance | Продвижение. Величина, на которую продвигается по оси x перо после прорисовки литеры. Это может быть не полная ширина литеры (особенно для курсивного шрифта) для обеспечения кернинга (т.е. создания выносного элемента литеры). |
Ascender | Верхний элемент литеры. Высота от базовой линии до вершины литеры. |
Bearing x or left bearing | Выноска по х или левая выноска. Размер литеры слева оттуда, где символ считается начинающимся. |
Descender | Подстрочный элемент литеры. Высота от низа литеры до базовой линии. |
Extent | Протяжённость литеры. Ширина литеры. Зависит от шрифта, она может включать определённое чистое пространство. |
Origin | Начало. Нижний левый угол литеры. |
X Max | Ширина символа, не включая выноски по х. |
Для экономии времени и памяти, кернинг не поддерживается.
Имена шрифтов
Шрифт идентифицируется по своему имени, которое может иметь одну из следующих форм:
Имя лигатуры (foundry name) | Имя, задаваемое именем лигатуры для идентификации семейства шрифтов, такие как Helvetica, Comic Sans MS или Prima Sans BT. Обратите внимание на использование заглавных букв. Имя лигатуры не включает информацию о стиле (напр., жирный, наклонный) или размере. Это имя является универсальным для всего операционного окружения (напр., X, Photon). |
Имя основы | Уникальный идентификатор, включающий сокращение от имени лигатуры, а также стиль (напр., b, i) и размер. Например, helv12 является именем основы 12-пунктного шрифта Helvetica, а helv12b – имя основы 12-пунктного жирного шрифта Helvetica. |
Чтобы задать шрифт в API Photon'а, обычно используется имя основы. Вы должны рассматривать имена основы как постоянные идентификаторы, а не как модифицируемые строки.
Вы можете жёстко прописать все ссылки на шрифты в приложении Photon'а. Но Ваше приложение может быть более гибким, если Вы используете лигатурное имя, чтобы иметь возможность выбора наилучшего совпадения из всех доступных шрифтов. При таком подходе нет проблемы, если какой-то конкретный шрифт со временем окажется переименован, удалён или перемещён. Например, в следующем вызове функции PtAlert() используется жёстко прописанное имя основы helv14, задающее 14-точечный шрифт Helvetica:
answer = PtAlert(base_wgt, NULL, "File Not Saved", NULL,
"File has not been saved.\nSave it?",
"helv14", 3, btns, NULL, 1, 3, Pt_MODAL );
Доступные имена основы Вы можете получить из имён файлов в ${PHOTON_PATH}/font – просто удалите имеющееся расширение файла (напр., .phf).
Иной способ заключается в том, что если у Вас есть директория $HOME/.ph, просмотреть директорию $HOME/.ph/font/. МикроGUI Photon'а создаёт этот локальный файл только при необходимости, как скажем, когда Вы запускаете утилиту fontadmin (см. "Справочник утилит QNX 6") для создания Вашей собственной персональной конфигурации. Пока локальный файл не создан, микроGUI использует глобальный файл.
Запрос доступных шрифтов
В приведенном выше примере используется сокращённое наименование жёстко прописанного имени основы (helv14). И, как любое приближение, оно допускает компромиссы. Прежде всего имена основы являются объектами для изменений. Что более важно, все версии Photon'овского микроGUI, вплоть до версии 1.13 включительно, позволяют использовать для имени основы не более 16 символов. Этого не всегда достаточно, чтобы дать каждому шрифту уникальную основу. Текущая версия микроGUI Photon'а допускает до 80 символов.