В этой главе будет продемонстрировано использование основных функций библиотеки Graphics Framework
Подразделы:
Рассмотрим шаги, которые должно выполнить приложение для использования библиотеки GF
для рисования 2D графики.
Graphics
Framework
все дисплеи имеют хотя бы один аппаратный слой, имеющий индекс 0.
Для управления параметрами слоев требуется его дескриптор. К параметрам слоев относятся яркость, контрастность, насыщенность, режим хрома и альфа смешивания, а также окна обзора (от англ. "viewports"). Дескриптор также используется для присоединения к слою отображаемой поверхности.
Никогда не передавайте NULL вместо дескрипторов графического устройства, дисплея, слоя, поверхности или контекста рисования в функции, которые их используют. Подобная практика приведет к аварийному заврешению приложения. |
Рассмотрим эти шаги детально.
В следующем примере производится присоединение к первому устройству в директории /dev/io-display
(определяется декларацией GF_DEVICE_INDEX(0)
— также можно присоединять устройство по имени). После этого производится подключение к каждому имеющемуся дисплею графического устройства и вывод информации об их разрешениях, частотах обновления и количестве имеющихся слоев. При завершении приложения является хорошей практикой использовать функцию gf_dev_detach() для освобождения ресурсов, затраченных на присоединение устройства.
Вызов gf_dev_detach() не является обязательным, поскольку менеджер графической подсистемы io-display освободит ресурсы автоматически при терминировании приложения. Однако, это является хорошей практикой. |
gf_dev_t gdev;gf_dev_info_t gdev_info;gf_display_t display;gf_display_info_t display_info;if ( gf_dev_attach( &gdev, GF_DEVICE_INDEX( 0 ), &gdev_info ) != GF_ERR_OK ){printf( "gf_dev_attach() failed\n" );return (-1);}printf( "Number of displays: %d\n", gdev_info.ndisplays );for ( i = 0; i < gdev_info.ndisplays; i++; ){printf( "Display %d: ", i );if ( gf_display_attach( &display, gdev, i, &display_info ) == GF_ERR_OK ){printf( "%dX%d, refresh = %dHz\n", display_info.xres, display_info.yres, display_info.refresh );printf( "Number of layers: %d\n", display_info.nlayers );} else {printf( "gf_display_attach() failed\n" );}}...gf_dev_detach( gdev );
Этот фрагмент кода производится подключение к основному слою (определяется полем main_layer_index
структуры, содержащей информацию о дисплее) дисплея, который был подключен функцией gf_display_attach():
if ( display != NULL ){gf_layer_t layer;if ( gf_layer_attach( &layer, display, main_layer_index, 0 ) == GF_ERR_OK ){...} else {printf( "gf_layer_attach() failed\n" );}}
В этом фрагменте кода приложение создает поверхность для присоединенного слоя, используя функцию gf_surface_create_layer(). Эта поверхность имеет тоже самое разрешение, что и основной дисплей (используются поля xres
и yres
структуры display_info
), а также пиксельный формат (можно получить список поддерживаемых пиксельных форматов присоединенного слоя, используя функцию gf_layer_query(), и анализируя поле format информационной структуры). Поверхность будет являться отображаемой после успешного вызова функции gf_layer_set_surfaces().
gf_surface_t surface;width = display_info.xres;height = display_info.yres;...if ( gf_surface_create_layer( &surface, &layer, 1, 0, width, height, layer_format, NULL, 0 ) == GF_ERR_OK ){gf_layer_set_surfaces( layer, &surface, 1 );/* draw stuff here */} else {printf( "gf_surface_create_layer() failed\n" );}
После успешного создания поверхности возможно запросить у GF
структуру gf_surface_info
, содержащую информацию о поверхности (включая ID поверхности), используя gf_surface_get_info().
Заключительный шаг включает создание контекста, используемого для вызова функций рисования и ассоциирование его с созданной ранее поверхностью.
if ( gf_context_create( &context ) != GF_ERR_OK ){fprintf( stderr, "gf_context_create failed\n" );return (-1);}if ( gf_context_set_surface( context, surface[cursurf] ) != GF_ERR_OK ){fprintf( stderr, "gf_context_set_surface failed\n" );return (-1);}
Начиная с этого момента можно вызывать функции рисования (рассматриваются далее). Для того, чтобы что-то нарисовать необходимо:
Хорошей практикой является освобождение задействованных ресурсов перед завершением приложения. Хотя это не обязательно, вы можете:
Если приложение терминируется экстренно или не освобождает ресурсы, io-display автоматически выполнит необходимые операции по освобождению ресурсов. |
Для любой функции, использующей координаты прямоугольных областей (таких как, например gf_draw_rect(), gf_draw_blit*() и gf_context_set_clipping()), важен порядок следования координат. Координаты, левого-верхнего угла области должны следовать первыми (параметры x1
, y1
), за ними должны располагаться координаты правого-нижнего угла (параметры x2
, y2
). Это ограничение сокращает время выполнения и размер кода. Если порядок следования координат изменен, функции рисования не будут работать и вернут код ошибки GF_ERR_PARM
.
Необходимо также заметить, что координаты прямоугольных областей включают их границы. Например, рисование прямоугольника из точки (0,0) в точку (0,0) с помощью функции gf_draw_rect() отобразит на экране один пиксель.
Функция gf_draw_rect() позволяет нарисовать заполненный (залитый) прямоугольник. Функция принимает дескриптор контекста рисования, координаты левого-верхнего и правого-нижнего углов прямоугольника (включительно). Цвет заливки соответствует фронтальному цвету (англ.: "foreground"), заданному в контексте рисования.
Пример использования данной функции включает рисование малого прямоугольника внутри более крупного:
Все нижеследующие примеры исходного кода подразумевают, что выполнены все описанные шаги по инициализации библиотеки GF и ее ресурсов. |
gf_context_set_fgcolor( context, 0xFFFFFF );gf_draw_rect( context, 10, 10, 50, 50 );gf_context_set_fgcolor( context, 0x000000 );gf_draw_rect( context, 20, 20, 40, 40 );
Используйте функции gf_draw_polyline() и gf_draw_poly_fill() для рисования ломаных линий и полигонов.
Обе эти функции принимают на вход следующие аргументы: контекст рисования, массив точек, его размер. Функция gf_draw_polyline() также принимает флаг, позволяющий нарисовать замкнутую ломаную линию. Если это флаг не устанавливать (например, передав функции 0) будет отображена не замкнутая поли-линия. В примере отображаются два треугольника, используя эти функции:
gf_context_set_fgcolor( context, 0xFFFFFF );pts[0].x = 20;pts[0].y = 20;pts[1].x = 10;pts[1].y = 30;pts[2].x = 40;pts[2].y = 30;/* draws two lines, the first two sides of the triangle set theflags to GF_DRAW_POLYLINE_CLOSED to complete the triangle: */gf_draw_polyline( context, pts, 3, 0 );/* draws a complete, filled triangle: */gf_draw_poly_fill( context, pts, 3 );
Битовая карта в терминологии библиотеки GF
- это поток байт, определяющих изображение, имеющее единственный отображаемый цвет. Изображения в других форматах “битовых карт” должны обрабатываться специализированным библиотеками.
Функция gf_draw_bitmap() позволяет нарисовать обсуждаемую битовую карту.
uint8_t image[] = { 0x08, 0x1C, 0x3E, 0x7F,0x3E, 0x1C, 0x08, 0x00 };gf_bitmap_t btmap;btmap.image = image;btmap.stride = 1;btmap.bit0_offset = 0;btmap.transparent = 0;btmap.dim.w = 8;btmap.dim.h = 8;gf_context_set_bgcolor( context, 0xFF0000 );gf_context_set_fgcolor( context, 0xFFFFFF );gf_draw_bitmap( context, &btmap, 10, 10 );
Для блиттинга в пределах одной поверхности используется функция gf_draw_blit1(). Для блиттинга между двумя поверхностями предназначена функция gf_draw_blit2(). Если при блитинге необходимо использовать две поверхности с различными размерами, возможно осуществление блиттинга с масштабированием при помощи функции gf_draw_blitscaled().
Пример вызова этих функций для битовой карты из предыдущего фрагмента кода:
gf_draw_blit1( context, 10, 10, 18, 18, 25, 25 );gf_draw_blitscaled( context, NULL, NULL, 10, 10, 18, 18, 30, 30, 60, 60 );
Необходимо отметить, что в качестве поверхности-источника и поверхности-приемника в вызове gf_draw_blitscaled() передан NULL
. Это позволяет адресоваться к поверхности, ассоциированной с контекстом рисования.
Если необходимо выполнить блиттинг между двумя поверхностями с различными пиксельными форматами, и графический контроллер не поддерживает CSC (от англ.: "color-space conversion"), вам следует разместить поверхность-источник в системную память (RAM) для повышения производительности. Для этого необходимо установить флаг GF_SURFACE_CREATE_CPU_FAST_ACCESS при создании поверхности-источника в фукнуции gf_surface_create(). |
Библиотека GF
является потоко-базопасной. Она подразумевает использование эксклюзивного доступа к оборудованию посредством функций gf_draw_begin() и gf_draw_end(), которые позволяют сохранять предсказуемость поведения графического контроллера.
Потоки, независимо от того к какому процессу они принадлежат, могут адресоваться к одной и той же поверхности. Когда такие потоки находятся в различных процессах, только один поток должен присоединять соответствующие дисплей и слой, а также ассоциировать разделяемую поверхность с ними. Для разделения поверхности между процессами один из потоков должен создать поверхность, определить ее идентификатор (англ.: "SID"; поле sid структуры, заполняемой функцией gf_surface_get_info()) и передать ее в другой процесс. Второй поток должен вызвать функцию gf_surface_attach_by_sid() с полученным SID
. Необходимо отметить, что требуется дополнительная синхронизация при параллельной работе потоков с этой поверхностью, если библиотека GF
не привлекается для рендеринга содержимого.
Потоки в пределах одного процесса могут разделять соединение с графическим устройством для минимизации потребляемых ресурсов на аллоцирование дескрипторов. Первый поток идентифицирует себя в качестве клиента графической подсистемы с помощью функции gf_dev_attach(), остальные потоки могут вызвать gf_dev_register_thread() для регистрации себя в качестве таковых.
Для облегчения отладки приложений можно линковать приложение с отладочной версией библиотеки GF
- libgf_g
- вместо стандартной версии libgf
.
Пока используется отладочная версия библиотеки допустимо появление дополнительных накладных расходов на этапе выполнения приложения, что, однако, облегчает диагностирование проблем использования GF
API. Например, попытки использования функций рисования без получения эксклюзивного доступа к устройству текущим потоком или вызов функций, которые могут привести к неразрешимым блокировкам (англ.: "deadlock") при наличии действующей эксклюзивной блокировки в текущем потоке.
Отладочная версия библиотеки libgf
собраны с включением дополнительного отладочного вывода и проверками. Если дополнительные проверки завершаются с ошибками, посылается отладочное сообщение в стандартный файл ошибок и преднамеренно разыменовывается указатель NULL
для аварийного терминирования приложения. Это позволяет сгенерировать отладочную трассу и стэк вызовов (например, при использовании утилиты dumper или отладчика) для определения источника проблемы и ошибочного вызова. Кроме подобных сообщений в этой версии библиотеки могут быть также добавлены дополнительные диагностические сообщения, не сопровождающиеся принудительной генерацией исключений.
Необходимо отметить, что использование утилиты dumper или отладчика при получения отладочной информации приложения, работающего с некоторыми драйверами, может приводить к неразрешимым блокировкам (англ.: "deadlock") на уровне графического контроллера и/или чипсета. Объясняется это доступностью регистров контроллера в адресном пространстве приложения и наличием у контроллера неразрешенных для чтения регистров. Подобные ограничения игнорируются отладчиком и утилитой dumper, что приводит к деструктивным последствиям. К счастью, это достаточно легко диагностируется и решается запретом на исполнение указанных утилит для решения задач отладки графически х приложений. |
Предыдущий раздел: Библиотека Graphics Framework