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.
![](https://i.iinfo.cz/images/49/rpi3-1-prev.png)
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.
![](https://i.iinfo.cz/images/375/rpi3-2-prev.png)
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; }
![](https://i.iinfo.cz/images/588/rpi3-3-prev.png)
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?
![](https://i.iinfo.cz/images/90/rpi3-4-prev.png)
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.
![](https://i.iinfo.cz/images/14/rpi3-7-prev.png)
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:
![](https://i.iinfo.cz/images/464/rpi3-5-prev.png)
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 */
![](https://i.iinfo.cz/images/102/rpi3-6-prev.png)
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/