Knihovna pro ASCII grafiku je hitovou záležitostí. Ačkoliv je její užitečnost v době výkonných grafických karet víceméně sporná (zapřísáhlí uživatelé příkazové řádky, nekamenujte mě), o to víc jde o krásné lákadlo a zajímavou hračku.
Jistě jste už alespoň slyšeli o tom, že lze například hrát Dooma nebo koukat na televizi v textovém režimu. S nějvětší pravděpodobností za tím vším stojí právě AA-lib. O co se tedy jedná?
AA-lib je knihovna poskytující grafické funkce, která ke své práci ale žádné grafické zařízení nepotřebuje. Ve skutečnosti žádný skutečný grafický výstup ani není možný. Knihovna nabízí programátorské rozhraní, díky němuž můžete vytvářet obrázky složené jen ze znaků ASCII tabulky. Interface knihovny definuje jakousi virtuální plochu a sadu základních funkcí. Pomocí nich můžete kreslit tak, jak jste zvyklí z jiných grafických knihoven. Výsledek je pak speciálními (a překvapivě efektními) algoritmy renderován na výstupní matici ASCII znaků.
Malá ukázka toho, co AA-lib umí:
-.:.-.:.-.:.-.:.-.:.- - - :.-.:.-.:.-.:.-.:.-. .:..:..:.:..:..:.: __. -..:.:..:..:.:..:. -.--.--.-.--.--.- +~- :...--.--.-.--.- .:.-.:.:.-.-.:.: . . :.:.--.:.-.:.-. -.-.:..-.:.:.-.. _qgw ,ggmm, :.-.:..:.:..:. .:.:..:.:..-.:.:. ] _]f V' ^]h .-.:..:..-.--.- -.-.--.-.--.:..-. "\RXoZbuXssV' :.-.:.---.:.-. .:.:.--.:.--.--.: =nXXZXSSX12s[ -.:.-.-.--.-.: -.-.-.--.-.--.--- ]s%Y1Xs%xXooL,-[( ..:.:.:.-.:.- .:.:.:.-.:.:.--.. ]mmXaaaXmmQQQQ/ :...-.-.:..: -.-.-.-.:..-.- _wWQWmmmmQWWWWWWQ/ :.:.:..:.- .:.:.-.:..:. JQWWWWWWWQWWWWWW$Q -..:.-.: -.-.-.:..:. . jmQWWQWWWWWWQWWmmmmm/- . :.-.:. .:.:.:..:. . jQWWWWWWWWWQWWWWWWWWmm/ ;:. :.:.. -.-.-.--. .-_mQQQWWQWQWWQWWQWQWQWWQQW . .:..: .:.:.-.- jQWQWWQWmWWWWWQWWWWWWQWWW/ . -.:. -.-.-.: . WWWWWQWWWWWWWQWWWQWQWWWWQf . -.-. .:.:.:._s_/'$WWWQWWWmWWWWWWQWWWWWQWQW' ~ - . .:.: -.-.-.xoXZ#X,"9$WWQWQWWWWQWWWQWQWW3SX} aX,:.. .ss_uoXXXXXX#X, )?WWWWWWQWWQWWWWWXuXon]___aoXXL:.: )S2ZZZSXXXXXXXS, 3WWQWWWQWWQWQQXln222oXXXXZXXaa )oXXZXXXXddXXXXXs,wWWWWWQWWWQWWW?'xvXXXZZZXZXXXdXX )oXXXdXXXdXXZXXXXo7?WWQWWWQP?? ioSXXXXdXXXXXX2" "I1122oXSSXXXXdXo1i; .xxXSXXSX22!^`-.: :-.:.-~+~++lII1li|~. :........ :=iIl1II|`...:-.. |
Poznáváte maskota Linuxu? (Obrázek byl vyroben programem aview
.)
AA-lib je v současné době součástí mnohem ambicióznějšího AA-projectu, takže chcete-li zjistit o AA-lib něco bližšího, je vhodné začít právě tam. (Ale až po přečtení tohoto článku. :-)
Zkusme si teď napsat nějaký prográmek, ve kterém světu poodhalíme naše umělecké vlohy na poli ASCII-artu.
#include <aalib.h>
#include <stdio.h>
#include <math.h>
/* konstanta pi */
#define PI 3.141592654
/* platno, na ktere budeme malovat */ aa_context *context; /* pozor, zaciname */ int main(void) { float x, y, r, t, sx, sy; int keyb_ok; /* inicializace grafiky */ context = aa_autoinit(&aa_defparams); /* inicializace ovladace klavesnice */ keyb_ok = aa_autoinitkbd(context, 0); /* obsluha chyb */ if ((context == NULL) || !keyb_ok) { fprintf(stderr, "Nelze inicializovat AA-lib!"); exit(1); } /* stred kruznice */ sx = aa_imgwidth(context) / 2; sy = aa_imgheight(context) / 2; /* polomer kruznice */ r = 20; /* kreslime... */ for (t = 0; t < PI/2; t += 0.01) { x = 2 * r * sin(t); y = r * cos(t); aa_putpixel(context, (int) (sx + x), (int) (sy + y), 255); aa_putpixel(context, (int) (sx - x), (int) (sy + y), 255); aa_putpixel(context, (int) (sx + x), (int) (sy - y), 255); aa_putpixel(context, (int) (sx - x), (int) (sy - y), 255); } /* renderuj obrazek */ aa_fastrender(context, 0, 0, aa_scrwidth(context), aa_scrheight(context)); /* vykresli obrazek */ aa_flush(context); /* cekej na stisk klavesy */ aa_getevent(context, 1); /* konec prace s grafikou */ aa_close(context); }
Máte-li knihovnu AA-lib správně nainstalovánu, uvedený kód můžete zkompilovat příkazem:
gcc -laa -lm kruz.c -o kruz.o
Po spuštění by se na obrazovce měla objevit asi takováto elipsa:
qaaaaaaaaaaaap qaP?? ??4ap qa?' )?ap qy? ?bp a? ?a y' )b y' )b ]' )f P 4 f ] f ] f ] b y ]p qf 4p qP 4p qP ?a a? )4a aP' )?ap qa?' )?baa aay?' )??????P?????' |
Proberme si naši ukázku trochu podrobněji. Na začátku zavádíme různé hlavičkové soubory. Protože budeme pracovat i s knihovnou AA-lib, nesmíme zapomenout includovat její aalib.h
.
Knihovna AA-lib pracuje na bázi tzv. grafických kontextů. Grafický kontext (typ aa_context
) si můžeme pro jednoduchost představit jako virtuální malířské plátno. Chceme-li něco nakreslit, musíme si nejprve nějakou kreslicí plochu obstarat. Děti vytáhnou skicák, sprejeři najdou hezkou zeď a my si zavedeme globální proměnnou context
, která je počítačovou reprezentací našeho malířského plátna.
Na samotném začátku programu se kreslicí plocha inicializuje. Za tímto účelem voláme funkci aa_autoinit()
, která se automatickou detekcí hardwaru počítače postará o vytvoření vhodné pracovní plochy. AA-lib umí zobrazovat grafiku na ASCII terminál, do okna X-Window, nebo ji uložit do textového souboru. Autodetekce vybere nejvhodnější možnost. Jako parametr se funkci předává konfigurační záznam. Nám vyhovují defaultní hodnoty, předáváme proto pointer na strukturu aa_defparams
.
Aby program po vykreslení ihned neskončil, ale počkal na stisk libovolné klávesy, musíme inicializovat i ovladač klávesnice. Voláme funkci aa_autoinitkbd()
, která se o to postará. Jejími parametry musí být grafický kontext (u nás proměnná context
) a mód zpracování stisku kláves. Mód 0 znamená normální klávesnici – to je naše volba. Pro pořádek uvedu, že ještě existuje mód AA_SENDRELEASE
, který informuje i o události uvolnění klávesy.
Proces inicializace je náročná záležitost, která nemusí vždy dopadnout dobře. V programu se na takovou situaci musíme připravit a o případné chybě uživatele informovat.
Další část programu realizuje samotné kreslení. Do proměnných sx
a sy
se vypočítává střed obrazovky. Využíváme k tomu makra aa_imgwidth()
a aa_imgheight()
, které vrací šířku a výšku pracovní plochy v „pixelech“.
Každá kreslicí plocha je tvořena polem bajtů, do kterých se ukládá číslo určující stupeň šedi. Každému pixelu odpovídá jeden bajt tohoto pole. Programátor tedy může psát algoritmy pro kreslení naprosto stejně, jak je zvyklý z klasických grafických knihoven. Výsledkem, který se v konečné fázi na obrazovce objeví, je matice znaků, v níž každému z nich odpovídá jedna čtveřice sousedních pixelů.
Samotné nakreslení elipsy je klasický algoritmus, který tady rozebírat nebudu. Postupně se vypočítávají jednotlivé pixely tvořící obvod a ty se zobrazují pomocí volání aa_putpixel()
. Makro aa_putpixel()
vyžaduje čtyři parametry. Prvním z nich je grafický kontext, kterého se operace týká, následují x-ová a y-ová souřadnice a odstín šedi v intervalu 0 (černá) až 255 (bílá).
V současnosti knihovna AA-lib nedisponuje žádnými dalšími grafickými funkcemi, vykreslení pixelu je jediná věc, kterou umí. Naprogramování dalších obvyklých algoritmů (jako třeba kreslení čar, oblouků a podobně) jistě nebude pro pilného čtenáře problémem.
Na tomto místě je vhodné se zmínit o tom, jak AA-lib pracuje s barvami: nijak. Veškeré kreslení se odehrává v odstínech šedi. Pro snazší přechod z „barevných“ programů je v AA-lib zapracován mechanizmus barevných palet, do kterých se jednotlivé barvy ukládají pod indexy. Barvy ukládané do palety můžete specifikovat v modelu RGB. AA-lib si je na šedou převede sama. V naší jednoduché ukázce jsme práci s paletami vynechali a kreslili jsme jenom „bílou“ barvou ( 255
).
Odstín pixelu má vliv na to, jak bude obrázek nakonec vypadat. Pro intenzivnější odstíny použije AA-lib jiné (silnější) znaky a naopak.
Na začátku jsem o svém produktu hrdě prohlásil, že se jedná o elipsu. Ve skutečnosti vypadá spíše jako kružnice. Celá věc s sebou totiž nese ještě jeden problém. Jelikož AA-lib umožňuje používat různá zařízení, nemůžeme předem vědět, jak vysoké každé písmeno vlastně je. No a tady vzniká široký prostor pro deformaci obrázků. Naše elipsa má být podle rovnic spíše širší než delší. V Xovém okně také tak vypadala. Když jsem si ji ale zobrazil v terminálu, byla spíše protáhlá na výšku. Naštěstí zde existuje způsob, jak se deformacím vyhnout. Pomocí několika funkcí knihovny můžete zjistit deformační poměr a kreslení obrázku tomu přizpůsobit. Vše záleží jenom na vaší šikovnosti.
Samotné použití makra aa_putpixel()
k zobrazení výsledku na obrazovku ale nestačí. Předtím musíme celý výtvor vyrenderovat (převést na ASCII-znaky). V naší ukázce se to dělá funkcí aa_fastrender()
, která je nejrychlejší a má nejjednodušší použití. Kromě ní ještě existují podobné funkce aa_render()
a aa_renderpalette()
, které se liší hlavně kvalitou výsledku. Pomocí nich můžete knihovnu AA-lib přimět, aby při konstrukci výsledného ASCII obrazu používala dithering, lze také měnit kontrast a dělat další parádičky.
Máme-li kresbu vyrenderovanou, nezbývá nic jiného, než ji poslat na výstup. Dělá se to pomocí funkce aa_flush()
. Až po provedení této rutiny se uživateli obrázek vykreslí!
V naší ukázce následuje po aa_flush()
už jen funkce aa_getevent()
, která zachycuje událost stisku klávesy. Jednička ve druhém parametru jí říká, že na událost má čekat a nepustit chod programu dále, dokud se nějaká neobjeví.
Po stisku klávesy se program hne k poslednímu volání aa_close()
, kterým se práce s grafikou ukončí a uvolní se paměť přiřazená grafickému kontextu.
Tak co, líbí? Mně nezbývá než konstatovat, že AA-lib je prostě kouzelná. A to jsem se nezmínil o dalších funkcích. AA-lib umí zachycovat události myši, podporuje různá uživatelská nastavení z příkazové řádky, a tak dále.
Jestli se chcete podívat na nějaký hotový software pracující s AA-lib, zkuste třeba tyto odkazy:
- Homepage AA-projectu
- Dumb – klon Dooma
- Aview – ASCII prohlížeč obrázků a animací
- aatv – televize v ASCII módu
Poznámka redakce:
Věhlasný vývojář Honza Hubička dal na srozuměnou, že v současné době pracuje na nové verzi AA-libu, do níž během tří let práce implementoval spoustu vzrušujících vylepšení (včetně možnosti hrát Quake) a také nový klávesový driver, který dokáže shodit celý systém. Na stránce aa-project.sourceforge.net lze údajně nalézt dostatečně vyvinutou verzi, která již nyní funguje daleko lépe, než verze předchozí :).
Johanka