Слои, поверхности и контексты

В этой главе будет продемонстрировано использование слоёв, поверхностей и контекстов библиотеки Graphics Framework

Подразделы:

Слои
Видимость
Яркость, контраст, насыщенность и оттенок
Окна обзора
Альфа- и хрома- смешивание
Поверхности
Контексты
Альфа-смешивание
Хрома ключ
Цвета рисования и фона
Атрибуты линий
Обрезка изображения
Растровые операции (ROP)
Сглаживание
Матрицы трансформации и смещение

Слои

После подключения к дисплею появляется возможность получить дескрипторы слоёв конкретного дисплея. Для этого используется функция gf_layer_attach(), которой передается индекс интересующего слоя. При подключении к дисплею с помощью gf_display_attach() будет заполнена структура gf_display_info_t. Некоторые ее поля содержат информацию о поддерживаемых слоях:

После подключения к слою следует запросить перечень доступных пиксельных форматов через gf_layer_query(). Эта информация требуется для создания и последующего подключения к слою поверхности (области отображаемой памяти) поддерживаемого формата. Пример запроса этой информации:

static int find_rgb_format( gf_layer_t layer )
{
gf_layer_info_t info;
int i;
for ( i = 0; gf_layer_query( layer, i, &info ) == GF_ERR_OK; ++i )
{
switch ( info.format )
{
case GF_FORMAT_PKLE_ARGB1555:
case GF_FORMAT_PKLE_RGB565:
case GF_FORMAT_BGR888:
case GF_FORMAT_BGRA8888:
return info.format;
default:
continue;
}
}
return (-1);
}

Все возможные форматы охватывает тип gf_format_t. Упакованные форматы в структуре gf_layer_info_t, заполняемой функцией gf_layer_query(), являются не endian-специфичными. Где это возможно, следует придерживаться отсутствия этой специфичности (например, при создании поверхностей через gf_surface_create()). Единственным исключением является функция gf_surface_attach(), которая получает на вход уже выделенный блок памяти.


Note: Речь идет об упакованных форматах. Endian-специфичные варианты других форматов отсутствуют.

Структура gf_layer_info_t, заполненная функцией gf_layer_query(), также содержит сведения о возможностях слоёв, включая:

После создания поддерживаемой поверхности ее можно ассоциировать со слоем, используя gf_layer_set_surfaces(). Для некоторых пиксельных форматов может требоваться несколько одновременно используемых поверхностей, например, для YUV.

Видимость

По умолчанию видимым является только основной слой дисплея. Слоя можно скрыть и отобразить используя gf_layer_disable() и gf_layer_enable() соответственно. Основной слой, однако, скрыть нельзя.

Проверить поддерживают ли слои регулировку видимости можно, проанализировав наличие флага GF_LAYER_CAP_DISABLE в поле caps структуры gf_layer_info_t.

Яркость, контраст, насыщенность и оттенок

Регулирование указанных параметров для YUV слоёв осуществляется при использовании функций gf_layer_set_brightness(), gf_layer_set_contrast(), gf_layer_set_saturation(), и gf_layer_set_hue(). Они принимают на вход целочисленное значение в диапазоне от -128 до 127, где 0 является состоянием слоя по умолчанию. Если регулировка параметров не поддерживается, последующий вызов gf_layer_update() вернет отличное от GF_ERR_OK значение.


Note: Регулировка параметров применяется только после вызова gf_layer_update().

Проверить поддерживают ли слои регулировку яркости, контрасти, насыщенности и оттенка можно, проанализировав наличие флагов GF_LAYER_CAP_SET_BRIGHTNESS, GF_LAYER_CAP_SET_CONTRAST, GF_LAYER_CAP_SET_SATURATION и GF_LAYER_CAP_SET_HUE в поле caps структуры gf_layer_info_t.

Окна обзора

Окном обзора называется часть поверхности (прямоугольная область), отображаемая в слое. Функция gf_layer_set_src_viewport() устанавливает область поверхности, которая подлежит отображению и характеризует исходное окно отображение. Функция gf_layer_set_dst_viewport() характеризует результирующее окно отображения — область дисплея, куда будет выводиться изображение области поверхности.

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


Note:
  • Исходное окно обзора ограничено размером поверхности, ассоциированной со слоем.
  • Регулировка окон обзора применяется только после вызова gf_layer_update().

Проверить поддерживают ли слои различные операции с окнами обзора можно по наличию следующих флагов в поле caps структуры gf_layer_info_t:

Альфа- и хрома- смешивание

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


Note: Оборудование должно поддерживать слои и соответствующие аппаратные операции для использования данных вызовов.

В некоторых случаях смешивание на уровне слоёв может быть более эффективным, нежели смешивание при операциях рендеринга изображения (см. функцию gf_context_set_alpha()). Например, в некоторых приложениях HMI и контент рендерятся различными процессами. В общем случае они могут адресоваться к различным аппаратным слоям, дабы исключить проблемы синхронизации доступа к поверхностям. Объединяя слои средствами контроллер дисплеев достижима простейшая композиция графических окружений. Обратный пример — однократный рендеринг нескольких статических объектов. В этом случае не требуется повторное выполнение операций смешивания с частотой обновления экрана и, соответственно, смешивание на уровне слоев будет являться не эффективным выбором.

Натсройка смешивания в операциях рендеринга рассмотрена ниже в разделе Контексты -> Альфа-смешивание.


Note: Регулировка параметров смешивания применяется только после вызова gf_layer_update().

Поверхности

Поверхностью является область памяти (преимущественно графической), доступная для рендеринга через GF API. Поверхность может быть как ассоциирована со слоем (в общем случае весь контент поверхности становится видимым в слое), так и являться самостоятельным объектом рендеринга. В последнем случае имеют место offscreen-рендеринг и одноименные поверхности.

Регион памяти, в котором размещаются данные поверхности сильно зависит от возможностей оборудования и набора флагов, использованных при создании поверхности. Если установлен флаг GF_SURFACE_CREATE_CPU_FAST_ACCESS при вызове gf_surface_create(), библиотека произведет попытку оптимизации поверхности по доступу со стороны CPU, что в основном означает использование для поверхности системной памяти (RAM). По умолчанию библиотека стремится оптимизировать поверхности исключительно для аппаратного доступа и акселерации. Это, в свою очередь, эквивалентно использованию видео памяти (VRAM).

Данные функции позволяют создавать поверхности:


Note: Для создания поверхностей, являющихся альфа-картами (см. gf_alpha_t structure), используется вызов gf_surface_create() с флагом GF_SURFACE_CREATE_ALPHA_MAP. Формат данных такой поверхности должен соответствовать GF_FORMAT_BYTE.

Все поверхности освобождаются менеджером io-display при завершении приложения. Хорошим тоном, однако, является использование функции gf_surface_free() для освобождения ресурсов.


Caution: Обратите внимание, что библиотека GF не освобождает память, ассоциированную с поверхностью черех gf_surface_attach().

Для определения параметров поверхности может использоваться функция gf_surface_get_info(). Она заполняет структуру gf_surface_info_t следующей информацией:

Для получения дескриптора графического устройства, к которому относится поверхность, можно использовать функйию gf_surface_get_dev().

Контексты

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

Для создания контекста используется функция gf_context_create(). После создания следует ассоциировать с ним поверхность, используя gf_context_set_surface(). После того, как контекст утрачивает значимость его следует удалить, используя gf_context_free(), или повторно инициализировать исходными параметрами (см. gf_context_init()).

У контекстов масса параметров, которые будут детально рассматриваться ниже.


Note: Многие функции с префиксом “set” имеют соответствующие вызовы “disable”. Например, gf_context_set_alpha() и gf_context_disable_alpha().

Альфа-смешивание

Настройка альфа-смешивания для последующих операций рендеринга осуществляется функцией gf_context_set_alpha(). На фход функции передается структура gf_alpha_t, определяющая параметры смешивания.

Альфа-канал содержит дополнительную информацию, используемую для изменения выводимого пикселя. В основном для достижения различных эффектов полупрозрачности. У канала есть три аспекта:

  1. один или несколько битов в каждом пикселе (альфа-канал в пикселе). В 16-битном формате 1555 под альфа-канал отводится только один бит (включен/выключен) и по пять бит на канал цвета RGB. В 32-битном формате 8888 все каналы, включая альфа, имеют восеми-битное кодирование.
  2. отдельное 8-битное изображение (поверхность), именуемая альфа-картой
  3. глобальный коэффициент альфа-смешивания

Также существуют source и destination факторы, укзаанные для определения способа корректировки данных исходного и результирующего пикселей при смешивании.

Рассмотрим типичную операцию смешивания: смешивание исходного пикселя с результирующим, используя альфа-канал пикселя. Для простоты нормализуем 8-битные каналы цвета ARGB формата (альфа и-канал и компоненты цвета) в диапазоне от 0 до 1. Допустим, что исходный и результирующий пиксели имеют данные (.5, 0, 1, 0) и (1, 1, 0, 0) соответственно. Исходный пиксель является зеленым и наполовину прозрачным, а результирующий красный и абсолютно непрозрачный. В нашем случае нужно использовать альфа-канал исходного пикселя для регулирования полупрозрачности. Это соовтетствует флагу GF_ALPHA_M1_SRC_PIXEL_ALPHA поля gf_alpha_t.mode. Поскольку нам нужно мультиплицировать исходный пиксель лишь с его альфа-компонентом, установим следующий фактор для исходного пикселя: GF_BLEND_SRC_M1. При простейшем блендинге объем смешивания каждого пикселя должен равняться 1 (или 100%). Таким образом фактор результирующего пикселя есть GF_BLEND_DST_1mM1. В результате каждый канал цвета исходного пикселя (включая альфа) умножается на значение его альфа-канала (0.5), что приводит к: (.25, 0, .5, 0). Каждый канал цвета исходного пикселя результирующего пикселя умножается на 1 - 0.5: (.5, .5, 0, 0). На место результирующего пикселя записыватеся сумма этих значений: (.75, .5, .5, 0).

Код, имплементирующий эту операцию:

gf_alpha_t alpha = { 0 };
alpha.mode = GF_ALPHA_M1_SRC_PIXEL_ALPHA | GF_BLEND_SRC_M1 | GF_BLEND_DST_1mM1;
gf_context_set_alpha( context, &alpha );
/* do rendering here */
gf_context_disable_alpha( context );

Пример, затрагивающий импортирование изображение с полупрозрачностью. Данный код реализует загрузку изображения через библиотеку Image и последующий вывод средствами Graphics Framework:

if ( img.flags & IMG_TRANSPARENCY )
{
gf_chroma_t chroma;
memset( &chroma, 0, sizeof chroma );
chroma.mode = GF_CHROMA_OP_SRC_MATCH | GF_CHROMA_OP_NO_DRAW;
if ( img.format & IMG_FMT_PALETTE )
chroma.color0 = img.palette[img.transparency.index];
else
if ( IMG_FMT_BPP(img.format ) < 24)
chroma.color0 = img.transparency.rgb16;
else
chroma.color0 = img.transparency.rgb32;
gf_context_set_chroma( setup.context, &chroma );
}
if ( img.format & IMG_FMT_ALPHA )
{
gf_alpha_t alpha;
memset( &alpha, 0, sizeof alpha );
alpha.mode = GF_ALPHA_M1_SRC_PIXEL_ALPHA | GF_BLEND_SRC_M1 | GF_BLEND_DST_1mM1;
gf_context_set_alpha( setup.context, &alpha );
}
/* render the loaded image */
gf_draw_blit2( setup.context, img_surf, NULL, 0, 0,
img.w - 1, img.h - 1, setup.x1, setup.y1 );

Хрома ключ

Настройка операций с хрома-ключем осуществляется при использовании gf_context_set_chroma(). Функция принимает стурктуру gf_chroma_t с информацией о желаемом режиме смешивания.

Хрома-ключ подразумевает сравнение цвета пикселя с некоторым эталоном для принятия решения о последующем выводе данных в слой. Тип gf_chroma_t позволяет задать комбинацию из двух флагов:

и одного из:

Для того, что бы запретить вывод белых исходных пикселей (0x00FFFFFF) или сделать их прозрачными, дсотаточно установить gf_chroma_t.color0 = 0x00FFFFFF и gf_chroma_t.mode = GF_CHROMA_OP_SRC_MATCH | GF_CHROMA_OP_NO_DRAW:

gf_chroma_t chroma = { 0 };
...
chroma.mode = GF_CHROMA_OP_SRC_MATCH | GF_CHROMA_OP_NO_DRAW;
chroma.color0 = 0x00FFFFFF;
gf_context_set_chroma( context, &chroma );
gf_draw_blit2( context, img_surface, surface, 0, 0,
w - 1, h - 1, 5 + w + 5, 30 );

Другой пример. Отображение изображения только поверх тех пикселей результирующего изображения, которые соовтетствуют color0: gf_chroma_t.mode = GF_CHROMA_OP_DRAW | GF_CHROMA_OP_DST_MATCH.

Цвета рисования и фона

Основной цвет рисования применяется для большинства поддерживаемых примитивов, включая прямоугольники, полигоны, битовые карты и полилинии. Он устанавливается с помощью gf_context_set_fgcolor(). Фоновый цвет рендеринга настраивается функцией gf_context_set_bgcolor() и применяется как вторичный цвет заполнения, например, как дополнительный цвет для штрихованной линии.

Атрибуты линий

Кроме цветов полилинии также имеют такие атрибуты, как ширина, стиль соединения сегментов, стиль штриховки.

Ширина задается в gf_context_set_penwidth(). Следует учитывать, что широкие линии поддерживаются только в случае, если их поддерживает графический контроллер. Предел ширины также является параметром оборудования. При установке значения 0 будет принудительно использоваться ширина пера в 1 пиксель.

Стиль соединения сегментов полилинии задается с помощью функции gf_context_set_linejoin():

joints.gif
Рисунок 1. Стиль соединения линий

Пунктир придает полилиниям эффект штриховки. Для этого имеется функция gf_context_set_linedash(). Пример:

gf_point_t p[] = {{50, 250},
{w - 100, 250}};
gf_context_set_fgcolor( context, 0x00ffff );
gf_context_set_bgcolor( context, 0xffffff );
gf_context_set_linedash( context, 0x000ffff, 0, 32, GF_CONTEXT_LINEDASH_BACKFILL );
gf_context_set_penwidth( context, 10 );
gf_draw_polyline( context, p, 2, 0 );

Обрезка изображения

Область обрезки ограничивает доступный для рендеринга прямоугольник в пределах размера поверхности, ассоциированной с контекстом. Все операции, выполняемые вне границ данной области не будут отображены. Область обрезки регулируется при помощи gf_context_set_clipping(). Пример:

gf_context_set_fgcolor( context, 0xffff00 );
gf_draw_rect( context, 0, 0, w, h );
gf_context_set_clipping( context, 50, 50, 400, 400 );
{
gf_point_t p[][4] = {{80, 8, 150, 150, 80, 80, 40, 160},
{240, 8, 290, 80, 360, 10, 290, 160},
{40, 190, 150, 270, 50, 320, 90, 270},
{210, 260, 450, 190, 370, 260, 450, 340}};
gf_context_set_fgcolor( context, 0x0000ff );
gf_context_set_penwidth( context, 1 );
gf_draw_poly_fill( context, &p[0][0], 4 );
gf_context_set_fgcolor( context, 0x00ff00 );
gf_context_set_penwidth( context, 5 );
gf_draw_polyline( context, &p[1][0], 4, GF_DRAW_POLYLINE_CLOSED );
gf_context_set_fgcolor( context, 0xf00f00 );
gf_context_set_penwidth( context, 10 );
gf_draw_poly_fill( context, &p[2][0], 4 );
gf_context_set_fgcolor( context, 0x00ffff );
gf_context_set_penwidth( context, 20 );
gf_draw_polyline( context, &p[3][0], 4, 0 );
}

Растровые операции (ROP)

Растровые операции (ROP) настраиваются при помощи следующих функций:

uint8_t pattern[] = {0x00, 0x01, 0x03, 0x07,
0x0f, 0x1f, 0x3f, 0x7f};
unsigned short rop = GF_ROP_PDSnaon;
gf_context_set_fgcolor( context, 0xff0000 );
gf_context_set_bgcolor( context, 0x0000ff );
gf_context_set_pattern( context, pattern, 0, 0, GF_CONTEXT_PATTERN_BACKFILL );
gf_context_set_rop( context, rop );
gf_draw_rect( context, 50, 50, 200, 200 );
gf_draw_flush( context);

Сглаживание

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


Note: Данная опция не применяется к изображениям и шрифтам. Для шрифтов следует применять альфа-смешивание.

Матрицы трансформации и смещение

Существует возможность задать смещение в виде параметров x и y, добавляемое к координатам вершин полигонов, линий и полилиний непосредственно перед их рендерингом. Матрица трансформации представляет собой двумерную матрицу, перемножаемую с координатами вершин полигонов, линий и полилиний непосредственно перед их рендерингом. Изменение этих характеристик позволяет достигать эффектов поворота, масштабирования, отражения и параллельного переноса. Осуществить это можно с помощью функций gf_context_set_transform() и gf_context_set_translation().

Пример ниже использует обе функции. Настройка окружения GF выполянется в подразумеваемой функции gf_setup(), чей код не представлен.

int main( int argc, char *argv[] )
{
gf_setup_t setup;
bool rotate = TRUE,
translate = TRUE;
unsigned dispno = 0,
fc = 0;
const char *dev_name = NULL;
int layer_idx = GF_SETUP_LAYER_MAIN,
rc;
float c, s;
int theta = 0,
dtheta = 1,
tx = 0,
dx = 1,
ty = 0,
dy = 1;
gf_fixed_t xform[] = {1 << 16, 0, 0, 1 << 16};
const gf_point_t points[] = {{ 0, -100},
{ 59, 81},
{-95, -31},
{ 95, -31},
{-59, 81}};
while ( (rc = getopt(argc, argv, "d:D:l:rtx:y:")) != -1 )
{
switch ( rc )
{
case 'd':
if ( isdigit( *optarg ) )
dev_name = GF_DEVICE_INDEX( atoi( optarg ) );
else
dev_name = optarg;
break;
case 'D':
dispno = atoi( optarg );
break;
case 'l':
layer_idx = atoi( optarg );
break;
case 'r':
rotate = FALSE;
break;
case 't':
translate = FALSE;
break;
case 'x':
tx = atoi( optarg );
break;
case 'y':
ty = atoi( optarg );
break;
}
}
if ( (rc = gf_setup( &setup, dev_name, dispno, layer_idx, GF_SETUP_FLAG_DBLBUFFER )) != GF_ERR_OK )
{
fprintf( stderr, "gf_setup() failed: %d\n", rc );
return (-1);
}
gf_context_set_penwidth( setup.context, 3 );
gf_context_set_antialias( setup.context, GF_CONTEXT_ANTIALIAS_LINES );
for ( ; ; )
{
gf_surface_t surface = (fc++ & 1) ? setup.surface2 : setup.surface1;
gf_context_set_surface( setup.context, surface );
if ( gf_draw_begin( setup.context ) == GF_ERR_OK )
{
gf_context_set_fgcolor( setup.context, 0xffffff );
gf_draw_rect( setup.context, 0, 0, INT_MAX, INT_MAX );
gf_context_set_fgcolor( setup.context, 0x000000 );
gf_draw_polyline( setup.context, points, 5, GF_DRAW_POLYLINE_CLOSED );
gf_draw_end( setup.context );
}
gf_layer_set_surfaces( setup.layer, &surface, 1 );
gf_layer_update( setup.layer, 0 );
tx += dx;
ty += dy;
if ( tx >= setup.display_info.xres )
{
dx = -dx;
tx = (setup.display_info.xres << 1) - tx;
} else
if ( tx < 0 )
{
dx = -dx;
tx = -tx;
}
if ( ty >= setup.display_info.yres )
{
dy = -dy;
ty = (setup.display_info.yres << 1) - ty;
} else
if ( ty < 0 )
{
dy = -dy;
ty = -ty;
}
theta += dtheta;
if ( theta >= 360 )
theta -= 360;
s = sinf( theta * 2 * M_PI / 360 );
c = cosf( theta * 2 * M_PI / 360 );
xform[0] = c * 0x10000;
xform[1] = s * 0x10000;
xform[2] = -s * 0x10000;
xform[3] = xform[0];
if ( rotate )
gf_context_set_transform( setup.context, xform );
if ( translate )
gf_context_set_translation( setup.context, tx, ty );
}
return (0);
}




Предыдущий раздел: Руководство разработчика библиотеки Graphics Framework