Musím říct, že jsem několikrát ve svém životě potřeboval z DB vygenerovat nějaký report a pokaždé když došlo na potřebu grafické reprezentace, snažil jsem se tomu zuby nehty vyhnout. Jenže obrázky jsou sexy, ne? Takže můj cíl je nyní generovat obrázky z databáze. Zatím pouze obrázky a pouze *.BMP. Mě by se totiž mnohem více hodila komprimovaná videa, nicméně je třebas dekomponovat. Ostatně generování *.BMP bude mít jednu krásnou vlastnost – bude možné vidět případný sub-výsledek až bych třebas psal JPEG komprimaci v PL/SQL. Mnohem rychlejší by bylo napsat to v Javě a javu si provolat či jí přímo loadnout do Oracle – funkční, rychlé, efektivní – ale nudné.
Vím o jednom problému, který mi čeká a o kterém ještě nemám úplnou představu jak ho řešit – je třeba do obrázku mimo kreslení psát, což znamená mít představu jako takové písmeno (“A”) bude v bitmapě vypadat a zatímco v Javě to stačí drawStringnout na Canvas (či Canvas2d) tady to bude vyžadovat mnohem více. Což třebas povede na nějaké čtení fontů(?) a loadování do DB a o to to bude zajímavějšejší, inu, uvidíme.
Takže hurá do hébičkování !
Bitmapa (jako BMP) vypadá jako soubor pixelů a nic víc. Nicméně i tak má BMP soubor nějakou hlavičku, kterou je třebas zapsat: Při čtení článku na wiki jsem dokonce zjistil, že BMP umožňuje Huffamanovo kódování a tedy bezztrátovou komprimaci v jisté omezené podobě, což jsem do dneška netušil.
http://en.wikipedia.org/wiki/File:BMPfileFormat.png
Což znamená, že bílý jednopixelový (1×1) obrázek nejsou 3 bajty (24 bitů – 8×3 barvy), ale 58 bajtů:
Pro pochopení jsem si vyzkoušel nějakou úpravu (modře) – konkrétně jsem z 01 přepsal pixely na 02 (2x 2x) a přidal 12 bajtů:
Fajn, to by zafungovalo, takže společně s obrázkem z wikipedie už je asi pomalu možné nějaké BMP začít tvořit. Nejprve nastřelení nějakého minimálního kódu, že je to správný směr:
1) Založení adresáře, kam se budou cpát BMP soubory:
CREATE DIRECTORY output_bmp AS 'F:\bmp_output';
2) Úplně minimální kód na zkoušku:
declare
bmp_file utl_file.file_type;
un_used_1 RAW(4):=hextoraw('42')||hextoraw('4d');
begin
bmp_file:=utl_file.fopen('OUTPUT_BMP','test.bmp','wb');
utl_file.put_raw(bmp_file,un_used_1,true);
if utl_file.is_open(bmp_file) then
utl_file.fclose_all;
end if;
end;
Je to opravdu minimální kód, který do souboru test.bmp zapíše pouze 42 a 4D (hex), což odpovídá ascii textu ‘BM‘ a je to první část hlavičky bitmapového souboru. Což v hexaeditoru (použivám WinHex 17.0 a vřele doporučuji – umí přímý zápis na disk i do RAM), vypadá takto:
Fajn, to by bylo a nyní minimální kód, pro vygenerování nejjednoduššího BMP – hlavičku, jsem sprostě opráskl z wikipedie:
declare
/* uvodni BMP hlavička BMP */
static_magic_header RAW(2):=hextoraw('42')||hextoraw('4d');
/* velikost obrázku v B */
variable_image_size RAW(4):=hextoraw('46')||hextoraw('00')||hextoraw('00')||hextoraw('00');
/* aplication specific - takze se podepišu AZOR - AZ */
static_aplication_1 RAW(2):=hextoraw('41')||hextoraw('5A');
/* aplication specific - takze se podepišu AZOR - OR */
static_aplication_2 RAW(2):=hextoraw('4F')||hextoraw('52');
/* kde končí hlavička */
static_head_end RAW(4):=hextoraw('36')||hextoraw('00')||hextoraw('00')||hextoraw('00');
/* délka DIB hlavičky */
static_dib_size RAW(4):=hextoraw('28')||hextoraw('00')||hextoraw('00')||hextoraw('00');
/* sířka obrázku */
variable_image_width RAW(4):=hextoraw('02')||hextoraw('00')||hextoraw('00')||hextoraw('00');
/* výška obrázku */
variable_image_height RAW(4):=hextoraw('02')||hextoraw('00')||hextoraw('00')||hextoraw('00');
/* počet planes */
static_image_planes RAW(2):=hextoraw('01')||hextoraw('00');
/* počet barev - 24 bitová paleta */
static_image_bits RAW(2):=hextoraw('18')||hextoraw('00');
/* bi_rgb */
static_bi_rgb RAW(4):=hextoraw('00')||hextoraw('00')||hextoraw('00')||hextoraw('00');
/* velikost pixel array (dat) - a BEZ KOMPRSE*/
variable_raw_size RAW(4):=hextoraw('10')||hextoraw('00')||hextoraw('00')||hextoraw('00');
/* pixels/metters - horizontalne i vertikalne pouziju jednu promennou */
static_pix_metter RAW(4):=hextoraw('13')||hextoraw('0B')||hextoraw('00')||hextoraw('00');
/* nezbytné barvy - nulla */
static_colors RAW(4):=hextoraw('00')||hextoraw('00')||hextoraw('00')||hextoraw('00');
/* A KONEČNĚ JEDNOTLIVÉ PIXELY (prvně však separátor) */
static_pix_separator RAW(2):= hextoraw('00')||hextoraw('00');
data_pix_1_1 RAW(3):=hextoraw('00')||hextoraw('FF')||hextoraw('FF');
data_pix_1_2 RAW(3):=hextoraw('00')||hextoraw('00')||hextoraw('FF');
data_pix_2_1 RAW(3):=hextoraw('FF')||hextoraw('00')||hextoraw('FF');
data_pix_2_2 RAW(3):=hextoraw('FF')||hextoraw('00')||hextoraw('FF');
bmp_file utl_file.file_type;
begin
bmp_file:=utl_file.fopen('OUTPUT_BMP','test_2.bmp','wb');
/* postupné zapisování všech hlaviček */
utl_file.put_raw(bmp_file,static_magic_header,true);
utl_file.put_raw(bmp_file,variable_image_size,true);
utl_file.put_raw(bmp_file,static_aplication_1,true);
utl_file.put_raw(bmp_file,static_aplication_2,true);
utl_file.put_raw(bmp_file,static_head_end,true);
utl_file.put_raw(bmp_file,static_dib_size,true);
utl_file.put_raw(bmp_file,variable_image_width,true); -- šířka
utl_file.put_raw(bmp_file,variable_image_height,true); -- výška
utl_file.put_raw(bmp_file,static_image_planes,true);
utl_file.put_raw(bmp_file,static_image_bits,true);
utl_file.put_raw(bmp_file,static_bi_rgb,true);
utl_file.put_raw(bmp_file,variable_raw_size,true);
utl_file.put_raw(bmp_file,static_pix_metter,true);
utl_file.put_raw(bmp_file,static_pix_metter,true);
utl_file.put_raw(bmp_file,static_colors,true);
utl_file.put_raw(bmp_file,static_colors,true);
/* a konečně data */
utl_file.put_raw(bmp_file,data_pix_1_1,true);
utl_file.put_raw(bmp_file,data_pix_1_2,true);
/* separátor řádku */
utl_file.put_raw(bmp_file,static_pix_separator,true);
/* druhý řádek */
utl_file.put_raw(bmp_file,data_pix_2_1,true);
utl_file.put_raw(bmp_file,data_pix_2_2,true);
/* ukončení souboru */
utl_file.put_raw(bmp_file,static_pix_separator,true);
/* a zavřít soubor */
if utl_file.is_open(bmp_file) then
utl_file.fclose_all;
end if;
end;
A wohoho – 4pixelový výsledek uložený v souboru test_2.bmp vypadá po zvětšení takto :

Vygenerované BMP
Bohužel jsem změnil bezhlavě barvu a vypadla z toho růžová :/, zrovna taková hloupá barva. Kód rozhodně není hezký a v nějakém dalším díle ho upravím. Zatím jsem vytipoval části hlaviček, které nebudu měnit (prefix static_) a ty které se s velikostí obrázku budou muset měnit a přepočítávat (prefix variable_). Vygenerovaný obrázek vypadá pak v hexaeditoru:

První BMP v Oracle zobrazená v WinHex
Tak a to by bylo pro tento díl všechno V druhém to navrhuji vykrášlit a udělat nějakou funkci, která mi z BLOBu vrátí přímo hlavičku. A taky funkci, která vygeneruje prázdný obrázek. Ještě s tím bude spousta legrace.