Obsah
1. Operace s framebufferem na Raspberry Pi (vykreslování složitějších objektů)
2. Vylepšení vykreslování pixelů do framebufferů s formáty RGBA a BGRA (true color)
3. Demonstrační příklad fpi_fb7.c: vylepšená operace typu putpixel
4. Slavný Bresenhamův algoritmus pro vykreslení úsečky
5. Adaptace Bresenhamova algoritmu s voláním funkce putpixel
6. Úplný zdrojový kód dnešního druhého demonstračního příkladu fpi_fb8.c
7. Urychlení vykreslování úseček: rychlejší výpočet barev a adres pixelů
8. Bresenhamův algoritmus pro framebuffery s formáty RGBA a BGRA (true color)
9. Bresenhamův algoritmus pro framebuffery s formátem hi-color 565
10. Úplný zdrojový kód dnešního třetího demonstračního příkladu fpi_fb9.c
11. Repositář s demonstračními příklady
1. Operace s framebufferem na Raspberry Pi (vykreslování složitějších objektů)
Způsob zápisu barev jednotlivých pixelů do framebufferu, s nímž jsme se seznámili ve druhé části tohoto seriálu, je možné použít i pro vykreslování složitějších objektů. V některých knihovnách, jmenujme například populární SDL, je množina objektů (či geometrických tvarů), které je možné přímo vykreslit, poměrně malá a omezuje se vlastně jen na vyplněné osově orientované obdélníky (funkce SDL_FillRect). Pro vykreslování složitějších objektů je nutné použít nějaké nadstavbové knihovny postavené nad SDL. Podobně je koncipována i nová verze konkurenční knihovny Allegro, v níž se složitější objekty vykreslují s využitím přídavného modulu nazvaného allegro_primitives.
Obrázek 1: Pro tvorbu 2D a 3D grafů je ve skutečnosti zapotřebí implementovat vykreslování jen minimálního počtu grafických objektů – bodů, úseček a znaků.
Dnes si ukážeme, jakým způsobem je možné implementovat funkce pro vykreslování úseček. Tento geometrický tvar byl zvolen z toho důvodu, že si při jeho vykreslování již nevystačíme s pouhou operací typu putpixel, resp. přesněji řečeno je použití této funkce při kreslení úseček velmi pomalé (a to i na Raspberry Pi). Ovšem druhý důvod, proč jsem vybral zrovna algoritmus pro vykreslování úseček, je praktičtější: setkal jsem se již s několika projekty, v nichž by bylo vhodné využít Raspberry Pi nejenom k řízení nějakých zařízení či ke zjišťování různých údajů z připojených čidel (teplota atd.), ale i k zobrazení naměřených či vypočtených hodnot formou grafu na připojený monitor. A právě při kresbě os grafů, uživatelského rozhraní i vlastních průběhů hodnot může být rychlý algoritmus pro kreslení úseček užitečný, zejména po doplnění knihovny funkcemi pro práci s fonty.
Obrázek 2: I s naprostým minimem kreslicích funkcí je možné vytvořit působivé retro hry (zde screenshot z původní varianty slavné automatovky Lunar Lander).
2. Vylepšení vykreslování pixelů do framebufferů s formáty RGBA a BGRA (true color)
Před popisem algoritmu vykreslení úseček se ještě na chvíli zastavme u způsobu kódování barev u pixelů zapisovaných do framebufferů podporujících tzv. „pravé barvy“ neboli „true-color“. U takto nakonfigurovaných framebufferů se pro zakódování barvy pixelu používají buď tři bajty nebo bajty čtyři, přičemž druhá varianta sice vede k nárůstu kapacity framebufferu o 25%, ovšem předností je uchování hodnoty (barvy) pixelu v jediném 32bitovém slově, což může znamenat značné urychlení některých operací (to se týká i dnes prezentovaného algoritmu pro vykreslení úsečky). Problém je jen jeden – jak správně reagovat na různé způsoby ukládání barev pixelů. U těch framebufferů, kde se pro uložení barvy jednoho pixelu používají tři bajty, se setkáme s posloupností R-G-B či B-G-R. Zajímavější (čti komplikovanější) je situace v případě, že je jeden pixel reprezentován čtyřmi bajty. Zde se mohou vyskytnout kombinace R-G-B-A, B-G-R-A, A-R-G-B či A-B-G-R (A je buď alfa kanál nebo hodnota, která se nijak neinterpretuje).
V příkladu, který jsme si ukázali v závěru předchozího článku, se pro rozhodování, jak zapisovat barvové složky do framebufferu, používala poměrně jednoduchá funkce, v níž se rozhodovalo pouze na základě bitové hloubky framebufferu:
/*
* Funkce, ktera vraci korektni funkci pro operaci putpixel().
*/
PutpixelFunction getProperPutpixelFunction(int bits_per_pixel, int type, int visual)
{
/* umime rozeznat pouze format bez bitovych rovin a bez palety */
if (type == FB_TYPE_PACKED_PIXELS && visual == FB_VISUAL_TRUECOLOR) {
if (bits_per_pixel == 16) {
return putpixel565;
}
if (bits_per_pixel == 32) {
/* toto neni zcela korektni, bylo by nutne rozlisit RGBA, ABGR, ARGB atd.*/
/* (ukol pro vazene ctenare :) */
return putpixelRGBA;
}
}
return putpixelNull;
}
Obrázek 3: Zápis barvových složek do framebufferu na Raspberry Pi ve špatném pořadí (viz demonstrační příklad z minulého článku).
Aby se tato funkce chovala správně minimálně na Raspberry Pi, je nutné ji nepatrně upravit:
/*
* Funkce, ktera vraci korektni funkci pro operaci putpixel().
*/
PutpixelFunction getProperPutpixelFunction(int bits_per_pixel, int type, int visual, int redOffset)
{
/* umime rozeznat pouze format bez bitovych rovin a bez palety */
if (type == FB_TYPE_PACKED_PIXELS && visual == FB_VISUAL_TRUECOLOR) {
if (bits_per_pixel == 16) {
return putpixel565;
}
if (bits_per_pixel == 32) {
if (redOffset == 16) {
return putpixelBGRA;
}
else {
return putpixelRGBA;
}
}
}
return putpixelNull;
}
Záměr je zde zřejmý – u framebufferů s bitovou hloubkou 16bpp se stále používá stejná funkce putpixel565, zatímco u framebufferů s hloubkou 32bpp se na základě pozice (offsetu) červené barvové složky rozhodne, zda se jedná o formát RGBA či BGRA. Otázka pro čtenáře: co se stane v případě, že se program spustí na počítači s framebufferem, který má formát ABGR či ARGB? Resp. jinak: postačuje skutečně testovat pouze offset červené složky?
Obrázek 4: Zápis barvových složek do framebufferu na Raspberry Pi ve špatném pořadí (viz demonstrační příklad v další kapitole).
Doplňková funkce pro vykreslení pixelů je velmi jednoduchá:
/*
* Funkce putpixel platna pouze pro graficke rezimy true-color
* s formatem 8-8-8-8 (popr. muze byt alfa kanal ignorovan).
* Funkcni pro Raspberry Pi s poradim bajtu R,G,B,A.
*/
void putpixelRGBA(const int x, const int y,
const char r, const char g, const char b,
char *pixels, const int line_length)
{
putpixelBGRA(x, y, b, g, r, pixels, line_length);
}
3. Demonstrační příklad fpi_fb7.c: vylepšená operace typu putpixel
Úplný zdrojový kód demonstračního příkladu, v němž se použije upravený algoritmus pro výběr správné funkce pro vykreslení pixelů, vypadá následovně a naleznete ho samozřejmě i na GitHubu. Tento příklad je funkční na Raspberry Pi jak při výběru 16bitové hloubky framebufferu, tak i při použití hloubky 32bitové:
/* Framebuffer na jednodeskovem mikropocitaci Raspberry Pi */
/* Autor: Pavel Tisnovsky, 2016 */
/* Demonstracni priklad cislo 7: rozdil mezi formatem RGBA a BGRA
* pri vykreslovani pixelu. */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
/*
* Datova struktura, do niz se ulozi informace o framebufferu.
* Blizsi informace o teto strukture je mozne nalezt v hlavickovem souboru
* dostupnem v adresari "/usr/include/linux/fb.h"
*/
typedef struct fb_var_screeninfo FramebufferInfo;
/*
* Druha datova struktura popisujici zbyvajici vlastnosti framebufferu.
* Blizsi informace o teto strukture je mozne nalezt v hlavickovem souboru
* dostupnem v adresari "/usr/include/linux/fb.h"
*/
typedef struct fb_fix_screeninfo ModeInfo;
/*
* Precteni vsech relevantnich informaci zjistenych o framebufferu. Pro korektni
* funkci je zapotrebi, aby mel uzivatel pristup k zarizeni /dev/fb0
* (postacuje byt ve skupine 'video' ci pouziti su/sudo)
*/
int readFramebufferInfo(int framebufferDevice,
FramebufferInfo *framebufferInfoPtr,
ModeInfo *modeInfoPtr)
{
/* Pokud operace ioctl probehne v poradku, vrati se 0 */
if (ioctl(framebufferDevice, FBIOGET_VSCREENINFO, framebufferInfoPtr)) {
perror("Nelze precist informace o framebufferu");
return 0;
}
/* Pokud operace ioctl probehne v poradku, vrati se 0 */
if (ioctl(framebufferDevice, FBIOGET_FSCREENINFO, modeInfoPtr)) {
perror("Nelze precist informace o rezimu");
return 0;
}
return 1;
}
/*
* Funkce putpixel platna pro nezname graficke rezimy.
*/
void putpixelNull(const int x, const int y,
const char r, const char g, const char b,
char *pixels, const int line_length)
{
}
/*
* Funkce putpixel platna pouze pro graficke rezimy true-color
* s formatem 8-8-8-8 (popr. muze byt alfa kanal ignorovan).
* Funkcni napriklad pro graficke karty Intel.
*/
void putpixelBGRA(const int x, const int y,
const char r, const char g, const char b,
char *pixels, const int line_length)
{
/* vypocet adresy zapisu dat */
unsigned int index = (x<<2) + y*line_length;
/* << 2 nahrazuje nasobeni ctyrmi */
/* vlastni provedeni zapisu */
*(pixels+index) = b;
index++;
*(pixels+index) = g;
index++;
*(pixels+index) = r;
}
/*
* Funkce putpixel platna pouze pro graficke rezimy true-color
* s formatem 8-8-8-8 (popr. muze byt alfa kanal ignorovan).
* Funkcni pro Raspberry Pi s poradim bajtu R,G,B,A.
*/
void putpixelRGBA(const int x, const int y,
const char r, const char g, const char b,
char *pixels, const int line_length)
{
putpixelBGRA(x, y, b, g, r, pixels, line_length);
}
/*
* Funkce putpixel platna pouze pro graficke rezimy hi-color
* s formatem 5-6-5.
*/
void putpixel565(const int x, const int y,
const char r, const char g, const char b,
char *pixels, const int line_length)
{
#define RED_OFFSET 11
#define GREEN_OFFSET 5
#define BLUE_OFFSET 0
#define RED_LOST_BITS 3
#define GREEN_LOST_BITS 2
#define BLUE_LOST_BITS 3
/* vypocet barvy pixelu, v zavorce nejdrive snizime bitovou sirku
* rezervovanou pro jednotlive barvove slozky a posleze bity, ktere
* reprezentuji barvovou slozku posuneme do spravne pozice ve slove */
unsigned int pixel_value = (r >> RED_LOST_BITS) << RED_OFFSET |
(g >> GREEN_LOST_BITS) << GREEN_OFFSET |
(b >> BLUE_LOST_BITS) << BLUE_OFFSET;
/* prevod na dvojici bajtu */
unsigned char byte1 = pixel_value & 0xff;
unsigned char byte2 = pixel_value >> 8;
/* vypocet adresy zapisu dat */
unsigned int index = (x<<1) + y*line_length;
/* << 1 nahrazuje nasobeni dvema */
/* vlastni provedeni zapisu */
*(pixels+index) = byte1;
index++;
*(pixels+index) = byte2;
}
/*
* Novy datovy typ - ukazatel na (libovolnou) funkci putpixel.
*/
typedef void (*PutpixelFunction)(const int, const int,
const char, const char, const char,
char*, const int);
/*
* Funkce, ktera vraci korektni funkci pro operaci putpixel().
*/
PutpixelFunction getProperPutpixelFunction(int bits_per_pixel, int type, int visual, int redOffset)
{
/* umime rozeznat pouze format bez bitovych rovin a bez palety */
if (type == FB_TYPE_PACKED_PIXELS && visual == FB_VISUAL_TRUECOLOR) {
if (bits_per_pixel == 16) {
return putpixel565;
}
if (bits_per_pixel == 32) {
if (redOffset == 16) {
return putpixelBGRA;
}
else {
return putpixelRGBA;
}
}
}
return putpixelNull;
}
/*
* Vykresleni testovaciho obrazku s vyuzitim funkce putpixel.
*/
void drawTestImage(int framebufferDevice,
FramebufferInfo *framebufferInfoPtr,
ModeInfo *modeInfoPtr)
{
#define OFFSET 300
/* casto pouzivane konstanty */
const int buffer_length = modeInfoPtr->smem_len;
const int xres = framebufferInfoPtr->xres;
const int yres = framebufferInfoPtr->yres;
/* ziskame spravnou verzi funkce putpixel */
PutpixelFunction putpixel = getProperPutpixelFunction(framebufferInfoPtr->bits_per_pixel,
modeInfoPtr->type,
modeInfoPtr->visual,
framebufferInfoPtr->red.offset);
/* ziskat primy pristup do framebufferu */
char *pixels = (char*)mmap(0, buffer_length,
PROT_READ | PROT_WRITE,
MAP_SHARED, framebufferDevice,
0);
if (pixels != MAP_FAILED) {
int x, y;
int r, g, b;
/* nejprve vymazeme cely framebuffer */
memset(pixels, 0, buffer_length);
/* vykreslime nekolik ctvercu o velikosti 256x256 pixelu
* s gradientnim barevnym prechodem */
for (y=0; y<256; y++) {
for (x=0; x<256; x++) {
/* prvni rada - gradientni prechody */
if (yres > 256) {
/* cerveny gradient */
if (xres > 256) {
r=y; g=0; b=0;
putpixel(x, y, r, g, b, pixels, modeInfoPtr->line_length);
}
/* zeleny gradient */
if (xres > 256 + OFFSET) {
r=0; g=y; b=0;
putpixel(OFFSET+x, y, r, g, b, pixels, modeInfoPtr->line_length);
}
/* modry gradient */
if (xres > 256 + OFFSET*2) {
r=0; g=0; b=y;
putpixel(OFFSET*2+x, y, r, g, b, pixels, modeInfoPtr->line_length);
}
/* grayscale gradient */
if (xres > 256 + OFFSET*3) {
r=y; g=y; b=y;
putpixel(OFFSET*3+x, y, r, g, b, pixels, modeInfoPtr->line_length);
}
}
/* druha rada - palety */
if (yres > 256 + OFFSET) {
if (xres > 256) {
r=x; g=y; b=0;
putpixel(x, OFFSET+y, r, g, b, pixels, modeInfoPtr->line_length);
}
if (xres > 256 + OFFSET) {
r=x; g=y; b=255;
putpixel(OFFSET+x, OFFSET+y, r, g, b, pixels, modeInfoPtr->line_length);
}
if (xres > 256 + OFFSET*2) {
r=255; g=x; b=y;
putpixel(OFFSET*2+x, OFFSET+y, r, g, b, pixels, modeInfoPtr->line_length);
}
if (xres > 256 + OFFSET*3) {
r=y; g=255; b=x;
putpixel(OFFSET*3+x, OFFSET+y, r, g, b, pixels, modeInfoPtr->line_length);
}
}
}
}
getchar();
munmap(pixels, buffer_length);
}
else {
perror("Nelze pristupovat k framebufferu");
}
}
/* Vstupni bod do demonstracniho prikladu... :) */
int main(int argc, char **argv)
{
FramebufferInfo framebufferInfo;
ModeInfo modeInfo;
int framebufferDevice = 0;
/* Ze zarizeni potrebujeme cist i zapisovat.*/
framebufferDevice = open("/dev/fb0", O_RDWR);
/* Pokud otevreni probehlo uspesne, nacteme
* a nasledne vypiseme informaci o framebufferu.*/
if (framebufferDevice != -1) {
/* Precteni informaci o framebufferu a test, zda se vse podarilo */
if (readFramebufferInfo(framebufferDevice, &framebufferInfo, &modeInfo)) {
drawTestImage(framebufferDevice, &framebufferInfo, &modeInfo);
}
close(framebufferDevice);
return 0;
}
/* Otevreni se nezadarilo, vypiseme tudiz pouze chybove hlaseni.*/
else {
perror("Nelze otevrit ovladac /dev/fb0");
return 1;
}
}
/* finito */
4. Slavný Bresenhamův algoritmus pro vykreslení úsečky
Pro vykreslení úseček bylo vyvinuto několik algoritmů, ovšem nejrychlejší (a v minulosti taktéž nejpoužívanější) je algoritmus navržený Jackem Eltonem Bresenhamem už v roce 1962. Tento algoritmus pro vykreslení úsečky, který je velmi podrobně popsán na Wikipedii, využívá pouze celočíselné operace a současně jsou v něm eliminovány aritmetické operace pro násobení a dělení, což je i dnes poměrně důležité (zejména na ARMech). Všechny výpočty se tak zjednoduší na aritmetický posun, sčítání, odčítání a podmíněné skoky. Právě díky těmto vlastnostem se Bresenhamův algoritmus stále v některých aplikacích používá, i když v případě požadavků na co nejvyšší kvalitu vykreslování se někdy přechází na pomalejší algoritmy s antialiasingem. Navíc, jak si ukážeme dále, se může dalšími optimalizacemi ještě více urychlit přístup k framebufferu, když se namísto souřadnic x a y pouze počítá offset od začátku framebufferu.
Obrázek 5: Typické „schody“ vykreslované Bresenhamovým algoritmem.
5. Adaptace Bresenhamova algoritmu s voláním funkce putpixel
Pokud se při vykreslování úseček spokojíme s menší rychlostí celého programu, je možné Bresenhamův algoritmus implementovat poměrně přímočarým způsobem, což je ostatně patrné z výpisu kódu, který naleznete pod tímto odstavcem. Pomocné lokální proměnné sx a sy slouží pro posuny souřadnice vykreslovaného pixelu, čímž bylo možné eliminovat rozepsání tohoto algoritmu pro všech osm oktantů (díky existenci sx a sy se jakoby pohybujeme pouze v prvním oktantu, i když ve skutečnosti může vykreslování probíhat v jiném směru). Pomocné proměnné dx a dy společně s proměnnou err se používají k určení směru vykreslování. Povšimněte si, že vykreslování vždy začíná v prvním vrcholu [x1, y1] a končí přesně ve druhém vrcholu [x2, y2], nezávisle na vzájemné pozici těchto vrcholů (může se jednat i o jediný bod):
/*
* Funkce pro vykresleni usecky.
*/
void line(const int x1, const int y1, const int x2, const int y2,
const char r, const char g, const char b,
char *pixels, const int line_length, PutpixelFunction putpixel)
{
#define ABS(x) ((x)<0 ? -(x) : (x))
int x = x1;
int y = y1;
/* zrcadleni algoritmu pro dalsi oktanty */
int dx = ABS(x2-x1), sx = x1<x2 ? 1 : -1;
int dy = ABS(y2-y1), sy = y1<y2 ? 1 : -1;
int err = (dx>dy ? dx : -dy)/2, e2;
while (1) {
putpixel(x, y, r, g, b, pixels, line_length);
if (x==x2 && y==y2) {
break;
}
e2 = err;
if (e2 >-dx) {
/* prepocet kumulovane chyby */
err -= dy;
/* posun na predchozi ci dalsi pixel na radku */
x += sx;
}
if (e2 < dy) {
/* prepocet kumulovane chyby */
err += dx;
/* posun na predchozi ci nasledujici radek */
y += sy;
}
}
}
Pro vykreslení každého pixelu je volána funkce předaná v parametru putpixel (přesněji řečeno se předává ukazatel na funkci).
6. Úplný zdrojový kód druhého demonstračního příkladu fpi_fb8.c
Podívejme se nyní, jak je výše uvedená implementace Bresenhamova algoritmu přidána do dalšího demonstračního příkladu, po jehož spuštění by se měl na obrazovce objevit následující vzorek:
Obrázek 6: Vzorek vykreslený demonstračním příkladem fpi_fb8.c
Úplný zdrojový kód tohoto příkladu vypadá následovně:
/* Framebuffer na jednodeskovem mikropocitaci Raspberry Pi */
/* Autor: Pavel Tisnovsky, 2016 */
/* Demonstracni priklad cislo 8: pomale vykreslovani usecek. */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
/*
* Datova struktura, do niz se ulozi informace o framebufferu.
* Blizsi informace o teto strukture je mozne nalezt v hlavickovem souboru
* dostupnem v adresari "/usr/include/linux/fb.h"
*/
typedef struct fb_var_screeninfo FramebufferInfo;
/*
* Druha datova struktura popisujici zbyvajici vlastnosti framebufferu.
* Blizsi informace o teto strukture je mozne nalezt v hlavickovem souboru
* dostupnem v adresari "/usr/include/linux/fb.h"
*/
typedef struct fb_fix_screeninfo ModeInfo;
/*
* Precteni vsech relevantnich informaci zjistenych o framebufferu. Pro korektni
* funkci je zapotrebi, aby mel uzivatel pristup k zarizeni /dev/fb0
* (postacuje byt ve skupine 'video' ci pouziti su/sudo)
*/
int readFramebufferInfo(int framebufferDevice,
FramebufferInfo *framebufferInfoPtr,
ModeInfo *modeInfoPtr)
{
/* Pokud operace ioctl probehne v poradku, vrati se 0 */
if (ioctl(framebufferDevice, FBIOGET_VSCREENINFO, framebufferInfoPtr)) {
perror("Nelze precist informace o framebufferu");
return 0;
}
/* Pokud operace ioctl probehne v poradku, vrati se 0 */
if (ioctl(framebufferDevice, FBIOGET_FSCREENINFO, modeInfoPtr)) {
perror("Nelze precist informace o rezimu");
return 0;
}
return 1;
}
/*
* Funkce putpixel platna pro nezname graficke rezimy.
*/
void putpixelNull(const int x, const int y,
const char r, const char g, const char b,
char *pixels, const int line_length)
{
}
/*
* Funkce putpixel platna pouze pro graficke rezimy true-color
* s formatem 8-8-8-8 (popr. muze byt alfa kanal ignorovan).
* Funkcni napriklad pro graficke karty Intel.
*/
void putpixelBGRA(const int x, const int y,
const char r, const char g, const char b,
char *pixels, const int line_length)
{
/* vypocet adresy zapisu dat */
unsigned int index = (x<<2) + y*line_length;
/* << 2 nahrazuje nasobeni ctyrmi */
/* vlastni provedeni zapisu */
*(pixels+index) = b;
index++;
*(pixels+index) = g;
index++;
*(pixels+index) = r;
}
/*
* Funkce putpixel platna pouze pro graficke rezimy true-color
* s formatem 8-8-8-8 (popr. muze byt alfa kanal ignorovan).
* Funkcni pro Raspberry Pi s poradim bajtu R,G,B,A.
*/
void putpixelRGBA(const int x, const int y,
const char r, const char g, const char b,
char *pixels, const int line_length)
{
putpixelBGRA(x, y, b, g, r, pixels, line_length);
}
/*
* Funkce putpixel platna pouze pro graficke rezimy hi-color
* s formatem 5-6-5.
*/
void putpixel565(const int x, const int y,
const char r, const char g, const char b,
char *pixels, const int line_length)
{
#define RED_OFFSET 11
#define GREEN_OFFSET 5
#define BLUE_OFFSET 0
#define RED_LOST_BITS 3
#define GREEN_LOST_BITS 2
#define BLUE_LOST_BITS 3
/* vypocet barvy pixelu, v zavorce nejdrive snizime bitovou sirku
* rezervovanou pro jednotlive barvove slozky a posleze bity, ktere
* reprezentuji barvovou slozku posuneme do spravne pozice ve slove */
unsigned int pixel_value = (r >> RED_LOST_BITS) << RED_OFFSET |
(g >> GREEN_LOST_BITS) << GREEN_OFFSET |
(b >> BLUE_LOST_BITS) << BLUE_OFFSET;
/* prevod na dvojici bajtu */
unsigned char byte1 = pixel_value & 0xff;
unsigned char byte2 = pixel_value >> 8;
/* vypocet adresy zapisu dat */
unsigned int index = (x<<1) + y*line_length;
/* << 1 nahrazuje nasobeni dvema */
/* vlastni provedeni zapisu */
*(pixels+index) = byte1;
index++;
*(pixels+index) = byte2;
}
/*
* Novy datovy typ - ukazatel na (libovolnou) funkci putpixel.
*/
typedef void (*PutpixelFunction)(const int, const int,
const char, const char, const char,
char*, const int);
/*
* Funkce, ktera vraci korektni funkci pro operaci putpixel().
*/
PutpixelFunction getProperPutpixelFunction(int bits_per_pixel, int type, int visual, int redOffset)
{
/* umime rozeznat pouze format bez bitovych rovin a bez palety */
if (type == FB_TYPE_PACKED_PIXELS && visual == FB_VISUAL_TRUECOLOR) {
if (bits_per_pixel == 16) {
return putpixel565;
}
if (bits_per_pixel == 32) {
if (redOffset == 16) {
return putpixelBGRA;
}
else {
return putpixelRGBA;
}
}
}
return putpixelNull;
}
/*
* Funkce pro vykresleni usecky.
*/
void line(const int x1, const int y1, const int x2, const int y2,
const char r, const char g, const char b,
char *pixels, const int line_length, PutpixelFunction putpixel)
{
#define ABS(x) ((x)<0 ? -(x) : (x))
int x = x1;
int y = y1;
/* zrcadleni algoritmu pro dalsi oktanty */
int dx = ABS(x2-x1), sx = x1<x2 ? 1 : -1;
int dy = ABS(y2-y1), sy = y1<y2 ? 1 : -1;
int err = (dx>dy ? dx : -dy)/2, e2;
while (1) {
putpixel(x, y, r, g, b, pixels, line_length);
if (x==x2 && y==y2) {
break;
}
e2 = err;
if (e2 >-dx) {
/* prepocet kumulovane chyby */
err -= dy;
/* posun na predchozi ci dalsi pixel na radku */
x += sx;
}
if (e2 < dy) {
/* prepocet kumulovane chyby */
err += dx;
/* posun na predchozi ci nasledujici radek */
y += sy;
}
}
}
/*
* Vykresleni testovaciho obrazku s vyuzitim funkci line a putpixel.
*/
void drawTestImage(int framebufferDevice,
FramebufferInfo *framebufferInfoPtr,
ModeInfo *modeInfoPtr)
{
#define OFFSET 300
/* casto pouzivane konstanty */
const int buffer_length = modeInfoPtr->smem_len;
const int pitch = modeInfoPtr->line_length;
/* ziskame spravnou verzi funkce putpixel */
PutpixelFunction putpixel = getProperPutpixelFunction(framebufferInfoPtr->bits_per_pixel,
modeInfoPtr->type,
modeInfoPtr->visual,
framebufferInfoPtr->red.offset);
/* ziskat primy pristup do framebufferu */
char *pixels = (char*)mmap(0, buffer_length,
PROT_READ | PROT_WRITE,
MAP_SHARED, framebufferDevice,
0);
if (pixels != MAP_FAILED) {
int i;
int r, g, b;
/* nejprve vymazeme cely framebuffer */
memset(pixels, 0, buffer_length);
/* vykreslime nekolik usecek s ruznym sklonem a barvou*/
for (i=0; i<256; i++) {
r = i;
g = i;
b = 256-i;
line(i*3, 0, i*3, 100, r, g, b, pixels, pitch, putpixel);
r = 255;
g = i;
b = 255-i;
line(i*4, 150, i*5, 250, r, g, b, pixels, pitch, putpixel);
}
for (i=0; i<=300; i+=10) {
line(0, 300 + i, i, 300 + 300, 255, 255, 255, pixels, pitch, putpixel);
line(300, 300, 600, 300 + i, 128, 128, 255, pixels, pitch, putpixel);
}
getchar();
munmap(pixels, buffer_length);
}
else {
perror("Nelze pristupovat k framebufferu");
}
}
/* Vstupni bod do demonstracniho prikladu... :) */
int main(int argc, char **argv)
{
FramebufferInfo framebufferInfo;
ModeInfo modeInfo;
int framebufferDevice = 0;
/* Ze zarizeni potrebujeme cist i zapisovat.*/
framebufferDevice = open("/dev/fb0", O_RDWR);
/* Pokud otevreni probehlo uspesne, nacteme
* a nasledne vypiseme informaci o framebufferu.*/
if (framebufferDevice != -1) {
/* Precteni informaci o framebufferu a test, zda se vse podarilo */
if (readFramebufferInfo(framebufferDevice, &framebufferInfo, &modeInfo)) {
drawTestImage(framebufferDevice, &framebufferInfo, &modeInfo);
}
close(framebufferDevice);
return 0;
}
/* Otevreni se nezadarilo, vypiseme tudiz pouze chybove hlaseni.*/
else {
perror("Nelze otevrit ovladac /dev/fb0");
return 1;
}
}
/* finito */
7. Urychlení vykreslování úseček: rychlejší výpočet barev a adres pixelů
Samotný Bresenhamův algoritmus je sice velmi rychlý, ovšem naše původní implementace zcela jistě velkou rychlostí vykreslování neoplývala. Je tomu tak z jednoduchého důvodu – i když se souřadnice jednotlivých pixelů ležících na úsečce počítají bez použití výpočetně náročných operací pro násobení a dělení, nakonec se zavolá funkce typu putpixel, kde se již tyto operace používají. Co je ještě horší – barva se ve funkci putpixel počítá stále znovu, což znamená, že se pro jedinou úsečku o délce řekněme 500 pixelů spočte 500× barva a 500× offset, na němž je zapotřebí provést zápis do framebufferu. Aby se celá operace urychlila, je možné provést tyto úpravy:
- Eliminovat volání funkce putpixel v Bresenhamově algoritmu. To se provede jednoduše: přesunem kódu přímo do funkce pro vykreslení úsečky.
- Výpočet barvy provést jen jednou. Z předchozího bodu plyne, že po přesunu kódu se výpočet barvy skutečně může provést ještě před vlastním vykreslováním.
- Výpočet offsetu ze souřadnic x a y taktéž provést jen jedenkrát. Toto je asi nejzajímavější část. Při posunu pixelu horizontálním směrem se vlastně mění offset o hodnotu ±bpp (2 či 4 bajty), při posunu pixelu vertikálním směrem pak o ± délku řádku (tu známe).
- Optimalizace samotného zápisu (nezapisovat po bajtech, ale po slovech).
8. Bresenhamův algoritmus pro framebuffery s formáty RGBA a BGRA (true color)
Podívejme se, jak lze první tři body aplikovat u framebufferů s formáty RGBA či BGRA. Uvedeme si řešení pouze pro jeden z těchto formátů, protože úprava spočívající v prohození barvových složek je triviální (a není ji vlastně zapotřebí moc řešit, protože se ta stejná funkce zavolá s mírně pozměněnými parametry):
/*
* Funkce line platna pouze pro graficke rezimy true-color
* s formatem 8-8-8-8 (popr. muze byt alfa kanal ignorovan).
* Funkcni naproklad pro graficke karty Intel.
*/
void lineBGRA(const int x1, const int y1, const int x2, const int y2,
const char r, const char g, const char b,
char *pixels, const int line_length)
{
/* vypocet adresy zapisu dat */
unsigned int index = (x1<<2) + y1*line_length;
/* << 2 nahrazuje nasobeni ctyrmi */
/* vlastni provedeni vykresleni usecky */
int x = x1;
int y = y1;
/* zrcadleni algoritmu pro dalsi oktanty */
int dx = ABS(x2-x1), sx = x1<x2 ? 1 : -1;
int dy = ABS(y2-y1), sy = y1<y2 ? 1 : -1;
int err = (dx>dy ? dx : -dy)/2, e2;
/* pri posunu po x-ove ose se index musi zvysit ci snizit o 4 (bajty) */
int offsetX = x1<x2 ? 4: -4;
/* pri posunu po y-ove ose se index musi zvysit ci snizit o delku radku (v bajtech) */
int offsetY = y1<y2 ? line_length : - line_length;
while (1) {
/* vlastni provedeni zapisu barvy pixelu */
*(pixels+index) = b;
*(pixels+index+1) = g;
*(pixels+index+2) = r;
if (x==x2 && y==y2) {
break;
}
e2 = err;
if (e2 >-dx) {
/* prepocet kumulovane chyby */
err -= dy;
/* posun na predchozi ci dalsi pixel na radku */
x += sx;
index += offsetX;
}
if (e2 < dy) {
/* prepocet kumulovane chyby */
err += dx;
/* posun na predchozi ci nasledujici radek */
y += sy;
index += offsetY;
}
}
}
Do původní implementace Bresenhamova algoritmu jsme zasáhli jen na dvou místech: namísto volání putpixel se přímo provede vykreslení a kromě posunu x-ové a y-ové složky souřadnic pixelu se mění i hodnota offsetu.
Stále se však při změně barvy pixelů provádí tři zápisy, a – což je možná ještě horší – zapisují se bajty a nikoli širší slova. To lze relativně snadno upravit, a to tak, že se na framebuffer nebudeme dívat jako na pole bajtů, ale jako na pole 32bitových hodnot. Ukazatelová aritmetika se nám tak paradoxně poněkud zjednoduší, pouze na začátku budeme muset vypočítat barvu pixelu a uložit ji do 32bitového slova:
/*
* Funkce line platna pouze pro graficke rezimy true-color
* s formatem 8-8-8-8 (popr. muze byt alfa kanal ignorovan).
* Funkcni naproklad pro graficke karty Intel.
*/
void lineBGRA(const int x1, const int y1, const int x2, const int y2,
const unsigned char r, const unsigned char g, const unsigned char b,
char *pixels, const int line_length)
{
/* vypocet adresy zapisu dat */
/* pocitame v 32bitovych slovech, tj. line_length je nutne podelit ctyrmi */
unsigned int index = x1 + (y1*line_length >> 2);
/* >> 2 nahrazuje deleni ctyrmi */
/* vlastni provedeni vykresleni usecky */
int x = x1;
int y = y1;
/* zrcadleni algoritmu pro dalsi oktanty */
int dx = ABS(x2-x1), sx = x1<x2 ? 1 : -1;
int dy = ABS(y2-y1), sy = y1<y2 ? 1 : -1;
int err = (dx>dy ? dx : -dy)/2, e2;
/* uplna barva v jednom slove */
__u32 color = (r<<16) | (g<<8) | b;
__u32 *pixels32 = (__u32*)pixels;
/* pri posunu po x-ove ose se index musi zvysit ci snizit o 4 (bajty) tj. o jedno 32-bitove slovo */
int offsetX = x1<x2 ? 1: -1;
/* pri posunu po y-ove ose se index musi zvysit ci snizit o delku radku (ve slovech) */
int offsetY = y1<y2 ? (line_length >> 2) : - (line_length >> 2);
while (1) {
/* vlastni provedeni zapisu barvy pixelu */
*(pixels32+index) = color;
if (x==x2 && y==y2) {
break;
}
e2 = err;
if (e2 >-dx) {
/* prepocet kumulovane chyby */
err -= dy;
/* posun na predchozi ci dalsi pixel na radku */
x += sx;
index += offsetX;
}
if (e2 < dy) {
/* prepocet kumulovane chyby */
err += dx;
/* posun na predchozi ci nasledujici radek */
y += sy;
index += offsetY;
}
}
}
V algoritmu bylo nutné udělat jednu změnu: barvové složky jsou zde explicitně popsány typem unsigned char a nikoli pouze char, protože s nimi provádíme bitové posuny (při pouhém zápisu bajtů vlastně bylo jedno, jakého jsou typu).
9. Bresenhamův algoritmus pro framebuffery s formátem hi-color 565
Pro framebuffer s formátem hi-color 565, tj. s formátem, který je pro Raspberry Pi a Raspbian výchozí, si ukážeme pouze jednu variantu Bresenhamova algoritmu pro vykreslování úseček. V této variantě se pro každý pixel provedou dva zápisy (horní a dolní bajt). Úpravu stylem, s nímž jsme se seznámili na konci předchozí kapitoly, ponechám laskavému a zvídavému čtenáři za domácí úkol :-) [v případě zájmu samozřejmě stačí počkat, až commitnu změny do https://github.com/tisnik/presentations/blob/master/rpi_framebuffer/rpi_fb10.c]:
/*
* Funkce line platna pouze pro graficke rezimy hi-color
* s formatem 5-6-5.
*/
void line565(const int x1, const int y1, const int x2, const int y2,
const char r, const char g, const char b,
char *pixels, const int line_length)
{
#define RED_OFFSET 11
#define GREEN_OFFSET 5
#define BLUE_OFFSET 0
#define RED_LOST_BITS 3
#define GREEN_LOST_BITS 2
#define BLUE_LOST_BITS 3
/* vypocet barvy pixelu, v zavorce nejdrive snizime bitovou sirku
* rezervovanou pro jednotlive barvove slozky a posleze bity, ktere
* reprezentuji barvovou slozku posuneme do spravne pozice ve slove */
unsigned int pixel_value = (r >> RED_LOST_BITS) << RED_OFFSET |
(g >> GREEN_LOST_BITS) << GREEN_OFFSET |
(b >> BLUE_LOST_BITS) << BLUE_OFFSET;
/* prevod na dvojici bajtu */
unsigned char byte1 = pixel_value & 0xff;
unsigned char byte2 = pixel_value >> 8;
/* vypocet adresy zapisu dat */
unsigned int index = (x1<<1) + y1*line_length;
/* << 1 nahrazuje nasobeni dvema */
/* vykresleni usecky */
int x = x1;
int y = y1;
/* zrcadleni algoritmu pro dalsi oktanty */
int dx = ABS(x2-x1), sx = x1<x2 ? 1 : -1;
int dy = ABS(y2-y1), sy = y1<y2 ? 1 : -1;
int err = (dx>dy ? dx : -dy)/2, e2;
/* pri posunu po x-ove ose se index musi zvysit ci snizit o 2 (bajty) */
int offsetX = x1<x2 ? 2: -2;
/* pri posunu po y-ove ose se index musi zvysit ci snizit o delku radku (v bajtech) */
int offsetY = y1<y2 ? line_length : - line_length;
while (1) {
/* vlastni provedeni zapisu barvy pixelu */
*(pixels+index) = byte1;
*(pixels+index+1) = byte2;
if (x==x2 && y==y2) {
break;
}
e2 = err;
if (e2 >-dx) {
/* prepocet kumulovane chyby */
err -= dy;
/* posun na predchozi ci dalsi pixel na radku */
x += sx;
index += offsetX;
}
if (e2 < dy) {
/* prepocet kumulovane chyby */
err += dx;
/* posun na predchozi ci nasledujici radek */
y += sy;
index += offsetY;
}
}
}
Poznámky k předchozímu kódu:
- Barvy vykreslovaných pixelů jsou opět vypočteny před vstupem do programové smyčky, zapisované hodnoty jsou uloženy do proměnných pojmenovanýchbyte1 a byte2.
- Při posunu na další (předchozí) pixel na řádku se adresa změní o ±1: viz proměnnou offsetX naplňovanou opět před smyčkou.
- Při posunu na další (předchozí) řádek se adresa změní o hodnotu line_length: viz proměnnou offsetY.
- Proč je podmínka pro ukončení smyčky umístěna přesně tam kde je? Druhá otázka za domácí úkol :-)
10. Úplný zdrojový kód třetího demonstračního příkladu fpi_fb9.c
Bez dalších komentářů se podívejme, jak se předchozí dvě funkce staly součástí demonstračního příkladu https://github.com/tisnik/presentations/blob/master/rpi_framebuffer/rpi_fb9.c:
/* Framebuffer na jednodeskovem mikropocitaci Raspberry Pi */
/* Autor: Pavel Tisnovsky, 2016 */
/* Demonstracni priklad cislo 9: vykreslovani usecek: rychlejsi varianta
* nepouzivajici funkci putpixel. */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#define ABS(x) ((x)<0 ? -(x) : (x))
/*
* Datova struktura, do niz se ulozi informace o framebufferu.
* Blizsi informace o teto strukture je mozne nalezt v hlavickovem souboru
* dostupnem v adresari "/usr/include/linux/fb.h"
*/
typedef struct fb_var_screeninfo FramebufferInfo;
/*
* Druha datova struktura popisujici zbyvajici vlastnosti framebufferu.
* Blizsi informace o teto strukture je mozne nalezt v hlavickovem souboru
* dostupnem v adresari "/usr/include/linux/fb.h"
*/
typedef struct fb_fix_screeninfo ModeInfo;
/*
* Precteni vsech relevantnich informaci zjistenych o framebufferu. Pro korektni
* funkci je zapotrebi, aby mel uzivatel pristup k zarizeni /dev/fb0
* (postacuje byt ve skupine 'video' ci pouziti su/sudo)
*/
int readFramebufferInfo(int framebufferDevice,
FramebufferInfo *framebufferInfoPtr,
ModeInfo *modeInfoPtr)
{
/* Pokud operace ioctl probehne v poradku, vrati se 0 */
if (ioctl(framebufferDevice, FBIOGET_VSCREENINFO, framebufferInfoPtr)) {
perror("Nelze precist informace o framebufferu");
return 0;
}
/* Pokud operace ioctl probehne v poradku, vrati se 0 */
if (ioctl(framebufferDevice, FBIOGET_FSCREENINFO, modeInfoPtr)) {
perror("Nelze precist informace o rezimu");
return 0;
}
return 1;
}
/*
* Funkce line() platna pro nezname graficke rezimy.
*/
void lineNull(const int x1, const int y1, const int x2, const int y2,
const char r, const char g, const char b,
char *pixels, const int line_length)
{
}
/*
* Funkce line platna pouze pro graficke rezimy true-color
* s formatem 8-8-8-8 (popr. muze byt alfa kanal ignorovan).
* Funkcni naproklad pro graficke karty Intel.
*/
void lineBGRA(const int x1, const int y1, const int x2, const int y2,
const char r, const char g, const char b,
char *pixels, const int line_length)
{
/* vypocet adresy zapisu dat */
unsigned int index = (x1<<2) + y1*line_length;
/* << 2 nahrazuje nasobeni ctyrmi */
/* vlastni provedeni vykresleni usecky */
int x = x1;
int y = y1;
/* zrcadleni algoritmu pro dalsi oktanty */
int dx = ABS(x2-x1), sx = x1<x2 ? 1 : -1;
int dy = ABS(y2-y1), sy = y1<y2 ? 1 : -1;
int err = (dx>dy ? dx : -dy)/2, e2;
/* pri posunu po x-ove ose se index musi zvysit ci snizit o 4 (bajty) */
int offsetX = x1<x2 ? 4: -4;
/* pri posunu po y-ove ose se index musi zvysit ci snizit o delku radku (v bajtech) */
int offsetY = y1<y2 ? line_length : - line_length;
while (1) {
/* vlastni provedeni zapisu barvy pixelu */
*(pixels+index) = b;
*(pixels+index+1) = g;
*(pixels+index+2) = r;
if (x==x2 && y==y2) {
break;
}
e2 = err;
if (e2 >-dx) {
/* prepocet kumulovane chyby */
err -= dy;
/* posun na predchozi ci dalsi pixel na radku */
x += sx;
index += offsetX;
}
if (e2 < dy) {
/* prepocet kumulovane chyby */
err += dx;
/* posun na predchozi ci nasledujici radek */
y += sy;
index += offsetY;
}
}
}
/*
* Funkce line platna pouze pro graficke rezimy true-color
* s formatem 8-8-8-8 (popr. muze byt alfa kanal ignorovan).
* Funkcni pro Raspberry Pi s poradim bajtu R,G,B,A.
*/
void lineRGBA(const int x1, const int y1, const int x2, const int y2,
const char r, const char g, const char b,
char *pixels, const int line_length)
{
lineBGRA(x1, y1, x2, y2, b, g, r, pixels, line_length);
}
/*
* Funkce line platna pouze pro graficke rezimy hi-color
* s formatem 5-6-5.
*/
void line565(const int x1, const int y1, const int x2, const int y2,
const char r, const char g, const char b,
char *pixels, const int line_length)
{
#define RED_OFFSET 11
#define GREEN_OFFSET 5
#define BLUE_OFFSET 0
#define RED_LOST_BITS 3
#define GREEN_LOST_BITS 2
#define BLUE_LOST_BITS 3
/* vypocet barvy pixelu, v zavorce nejdrive snizime bitovou sirku
* rezervovanou pro jednotlive barvove slozky a posleze bity, ktere
* reprezentuji barvovou slozku posuneme do spravne pozice ve slove */
unsigned int pixel_value = (r >> RED_LOST_BITS) << RED_OFFSET |
(g >> GREEN_LOST_BITS) << GREEN_OFFSET |
(b >> BLUE_LOST_BITS) << BLUE_OFFSET;
/* prevod na dvojici bajtu */
unsigned char byte1 = pixel_value & 0xff;
unsigned char byte2 = pixel_value >> 8;
/* vypocet adresy zapisu dat */
unsigned int index = (x1<<1) + y1*line_length;
/* << 1 nahrazuje nasobeni dvema */
/* vykresleni usecky */
int x = x1;
int y = y1;
/* zrcadleni algoritmu pro dalsi oktanty */
int dx = ABS(x2-x1), sx = x1<x2 ? 1 : -1;
int dy = ABS(y2-y1), sy = y1<y2 ? 1 : -1;
int err = (dx>dy ? dx : -dy)/2, e2;
/* pri posunu po x-ove ose se index musi zvysit ci snizit o 2 (bajty) */
int offsetX = x1<x2 ? 2: -2;
/* pri posunu po y-ove ose se index musi zvysit ci snizit o delku radku (v bajtech) */
int offsetY = y1<y2 ? line_length : - line_length;
while (1) {
/* vlastni provedeni zapisu barvy pixelu */
*(pixels+index) = byte1;
*(pixels+index+1) = byte2;
if (x==x2 && y==y2) {
break;
}
e2 = err;
if (e2 >-dx) {
/* prepocet kumulovane chyby */
err -= dy;
/* posun na predchozi ci dalsi pixel na radku */
x += sx;
index += offsetX;
}
if (e2 < dy) {
/* prepocet kumulovane chyby */
err += dx;
/* posun na predchozi ci nasledujici radek */
y += sy;
index += offsetY;
}
}
}
/*
* Novy datovy typ - ukazatel na (libovolnou) funkci line.
*/
typedef void (*LineFunction)(const int, const int, const int, const int,
const char, const char, const char,
char*, const int);
/*
* Funkce, ktera vraci korektni funkci pro operaci line().
*/
LineFunction getProperLineFunction(int bits_per_pixel, int type, int visual, int redOffset)
{
/* umime rozeznat pouze format bez bitovych rovin a bez palety */
if (type == FB_TYPE_PACKED_PIXELS && visual == FB_VISUAL_TRUECOLOR) {
if (bits_per_pixel == 16) {
return line565;
}
if (bits_per_pixel == 32) {
if (redOffset == 16) {
return lineBGRA;
}
else {
return lineRGBA;
}
}
}
return lineNull;
}
/*
* Vykresleni testovaciho obrazku s vyuzitim funkce line.
*/
void drawTestImage(int framebufferDevice,
FramebufferInfo *framebufferInfoPtr,
ModeInfo *modeInfoPtr)
{
#define OFFSET 300
/* casto pouzivane konstanty */
const int buffer_length = modeInfoPtr->smem_len;
const int pitch = modeInfoPtr->line_length;
/* ziskame spravnou verzi funkce line */
LineFunction line = getProperLineFunction(framebufferInfoPtr->bits_per_pixel,
modeInfoPtr->type,
modeInfoPtr->visual,
framebufferInfoPtr->red.offset);
/* ziskat primy pristup do framebufferu */
char *pixels = (char*)mmap(0, buffer_length,
PROT_READ | PROT_WRITE,
MAP_SHARED, framebufferDevice,
0);
if (pixels != MAP_FAILED) {
int i;
int r, g, b;
/* nejprve vymazeme cely framebuffer */
memset(pixels, 0, buffer_length);
/* vykreslime nekolik usecek s ruznym sklonem a barvou*/
for (i=0; i<256; i++) {
r = i;
g = i;
b = 256-i;
line(i*3, 0, i*3, 100, r, g, b, pixels, pitch);
r = 255;
g = i;
b = 255-i;
line(i*4, 150, i*5, 250, r, g, b, pixels, pitch);
}
for (i=0; i<=300; i+=10) {
line(0, 300 + i, i, 300 + 300, 255, 255, 255, pixels, pitch);
line(300, 300, 600, 300 + i, 128, 128, 255, pixels, pitch);
}
getchar();
munmap(pixels, buffer_length);
}
else {
perror("Nelze pristupovat k framebufferu");
}
}
/* Vstupni bod do demonstracniho prikladu... :) */
int main(int argc, char **argv)
{
FramebufferInfo framebufferInfo;
ModeInfo modeInfo;
int framebufferDevice = 0;
/* Ze zarizeni potrebujeme cist i zapisovat.*/
framebufferDevice = open("/dev/fb0", O_RDWR);
/* Pokud otevreni probehlo uspesne, nacteme
* a nasledne vypiseme informaci o framebufferu.*/
if (framebufferDevice != -1) {
/* Precteni informaci o framebufferu a test, zda se vse podarilo */
if (readFramebufferInfo(framebufferDevice, &framebufferInfo, &modeInfo)) {
drawTestImage(framebufferDevice, &framebufferInfo, &modeInfo);
}
close(framebufferDevice);
return 0;
}
/* Otevreni se nezadarilo, vypiseme tudiz pouze chybove hlaseni.*/
else {
perror("Nelze otevrit ovladac /dev/fb0");
return 1;
}
}
/* finito */
Obrázek 7: Výsledek běhu dnešního posledního demonstračního příkladu na počítači s framebufferem nakonfigurovaným pro použití true color formátu RGBA.
11. Repositář s demonstračními příklady
Všechny tři demonstrační příklady, s nimiž jsme se v dnešním článku seznámili, byly uloženy do Git repositáře umístěného na GitHubu na adrese (https://github.com/tisnik/presentations):
Pro překlad obou demonstračních příkladů je zapotřebí mít nainstalován překladač GNU C (či Clang), linker a vývojářskou verzi libc.
12. Odkazy na Internetu
- Seriál Grafické karty a grafické akcelerátory
http://www.root.cz/serialy/graficke-karty-a-graficke-akceleratory/ - Grafika na osmibitových počítačích firmy Sinclair II
http://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-sinclair-ii/ - Grafické čipy v osmibitových počítačích Atari
http://www.root.cz/clanky/graficke-cipy-v-osmibitovych-pocitacich-atari/ - Osmibitové počítače Commodore a čip VIC-II
http://www.root.cz/clanky/osmibitove-pocitace-commodore-a-cip-vic-ii/ - Grafika na osmibitových počítačích firmy Apple
http://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-apple/ - Počátky grafiky na PC: grafické karty CGA a Hercules
http://www.root.cz/clanky/pocatky-grafiky-na-pc-graficke-karty-cga-a-hercules/ - Karta EGA: první použitelná barevná grafika na PC
http://www.root.cz/clanky/karta-ega-prvni-pouzitelna-barevna-grafika-na-pc/ - Grafické karty MCGA a VGA
http://www.root.cz/clanky/graficke-karty-mcga-a-vga/ - Grafický subsystém počítačů Amiga
http://www.root.cz/clanky/graficky-subsystem-pocitacu-amiga/ - Grafický subsystém počítačů Amiga II
http://www.root.cz/clanky/graficky-subsystem-pocitacu-amiga-ii/ - Raspberry Pi pages
https://www.raspberrypi.org/ - BCM2835 registers
http://elinux.org/BCM2835_registers - VideoCore (archiv stránek společnosti Alphamosaic)
http://web.archive.org/web/20030209213838/www.alphamosaic.com/videocore/ - VideoCore (Wikipedia)
https://en.wikipedia.org/wiki/Videocore - RPi lessons: Lesson 6 Screen01
http://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/screen01.html - Raspberry Pi forum: Bare metal
https://www.raspberrypi.org/forums/viewforum.php?f=72 - C library for Broadcom BCM 2835 as used in Raspberry Pi
http://www.airspayce.com/mikem/bcm2835/ - Raspberry Pi Hardware Components
http://elinux.org/RPi_Hardware#Components - (Linux) Framebuffer
http://wiki.linuxquestions.org/wiki/Framebuffer - (Linux) Framebuffer HOWTO
http://tldp.org/HOWTO/Framebuffer-HOWTO/ - Linux framebuffer (Wikipedia)
https://en.wikipedia.org/wiki/Linux_framebuffer - RPi Framebuffer
http://elinux.org/RPi_Framebuffer - HOWTO: Boot your Raspberry Pi into a fullscreen browser kiosk
http://blogs.wcode.org/2013/09/howto-boot-your-raspberry-pi-into-a-fullscreen-browser-kiosk/ - Zdrojový kód fb.c pro RPI
https://github.com/jncronin/rpi-boot/blob/master/fb.c - RPiconfig
http://elinux.org/RPi_config.txt - Mailbox framebuffer interface
https://github.com/raspberrypi/firmware/wiki/Mailbox-framebuffer-interface - Seriál Grafické formáty
http://www.root.cz/serialy/graficke-formaty/