e-mail    Debatní kniha    Mapa stránek    Hlavní  
 perličky 
 

True colors na obyčejné VGA? To přece není možné! Aneb Barevný dithering?

Vojtěch Kresl

Vážení programátoři, dovoluji si vám předložit algoritmus, pomocí kterého se z vaší VGA karty stane karta téměř True Color (16,7 miliónu SOUČASNĚ zobrazitelných barev). Jinak řečeno -- pomocí tohoto algoritmu získáte možnost pracovat s body obrazu specifikovanými třemi bajty, které určují barevné složky R, G a B.

Použitelné výsledky dostanete již ve standardním módu VGA (640 x 480 x 16 barev), ale zářivý svět téměř pravých barev se vám otevře až při použití režimů SuperVGA. Při rozlišení 640 x 400 x 256 budou výsledky velmi slušné a v grafice 800 x 600 x 256 vynikající a od True Colors k nerozeznámí.

Prohlížeč formátu Targa
Vezmete-li uvedený program tak, jak je, máte prohlížeč obrázků ve formátu Targa (nekomprimovaný, 16,7 miliónu barev). Použijete-li rutinu PutPixel() jinde, můžete pracovat ve 24-bitové grafice, kreslit čáry, zobrazovat fotografie a ladit své programy pro karty True Colors, které již tu a tam k vidění jsou.

Princip
Algoritmus vychází z černobílého ditheringu. Výhodou této metody je přímý přístup k bodu, na rozdíl např. od Floyd-Steinbergova algoritmu. Z tohoto důvodu je možné kreslit čáry a další útvary s tloušťkou 1 pixel.

Algoritmus potřebuje pro svou práci standardizovanou paletu barev -- funkce BestPal(). Vystačí již s osmi barevnými registry (black, blue, green, cyan, red, magenta, yellow, white). Uvedené barvy jsou umístěny v rozích RGB krychle a barva, kterou požadujeme, je někde uvnitř. Algoritmem je v závislosti na požadované barvě R, G, B a na souřadnicích x, y vybrán patřičný registr (obvykle pokaždé jiný) tak, že shluk bodů různých barev dává dojem požadované barvy.

Je-li k dispozici více než osm registrů, situace se pozměňuje v tom smyslu, že oněch RGB krychlí (resp. kvádrů) je k dispozici více. Pak se nejprve zvolí krychle/kvádr a pomocí jejích/jeho osmi barev se namíchá patřičný odstín. O kvádru lze mluvit ve chvíli, kdy některým složkám RGB věnujeme více registrů než ostatním.

Program je napsán v C++, takže lze s výhodou využít předdefinované parametry. Implicitně je použito osm registrů -- po dvou pro každou složku (2*2*2=8). V režimech SVGA je výhodná kombinace 6R, 6G, 6B (6*6*6=216 registrů), nebo lze využít poznatku, že na modrou složku je oko méně citlivé a volba kombinace 8R, 8G a 4B (ošidíme modrou).

Přenositelnost
Program je snadno přenositelný do jiných prostředí, neboť potřebné funkce InitDitMatrix(), BestPal() a PutPixel() jsou hardwarově nezávislé, dodáte-li vlastní funkce pro nastavení palety -- SetPalette() -- a pro vykreslení bodu -- putpixel().

Optimalizace
Pokud nebude vyhovovat rychlost programu, při optimalizaci se zaměřte hlavně na standardní funkci putpixel(), která je nehorázně pomalá. Na vlastním vykreslení bodu potřebuje program zdaleka nejvíc času, jak ostatně ukáže Turbo Profiler. Pro kreslení čar se nabízí napsat vlastní rutinu putpixel() (lze urychlit až 10-krát). Pro zobrazení obrázků je výhodné vykreslení do paměti a přesun celého bloku do videoRAM.

Závěr
Mou snahou bylo zakomponovat algoritmus do použitelného celku, ač je takhle trochu delší. Zato je v něm vidět nastavení módů SVGA a práce s paletou (16 i 256 barev), ve stručnosti formát Targa a vlastní použití algoritmu prostřednictvím třídy TDit.

// Color dithering // --------------- // Vojtěch Kresl, 1991 - algoritmus, 1993 - realizace // 16,7 mil. barev pro kartu VGA (16 barev), SVGA (256 b.) // Pozn.: Pro SVGA mód je třeba SVGA.BGI ovladač gr. karty #include <graphics.h> // putpixel(), initgraph(), ... #include <dos.h> // geninterrupt(), delay() #include <stdlib.h> // exit() #include <stdio.h> // fread(), fgetc(), ... #define DIT 16 // Velikost dith. matice #define MXC 255 #define ERROR(x) {closegraph (); printf (x); exit (1);} typedef unsigned char BYTE; typedef enum // Gr. módy pro SVGA a standardní VGA { SVGA_320_200 = 0, SVGA_640_400 = 1, SVGA_640_480 =2, SVGA_800_600 = 3, SVGA_1024_768 = 4, VGA_640_480_16=VGA } TVGA; class TDit { int nR, nG, nB, nR1, nG1, nB1, fReg; unsigned char dit[DIT][DIT]; // Dithering matice TVGA vga; // VGA či SVGA flag void InitGraph (TVGA vga); void InitDitMatrix (void); void BestPal (BYTE nR, BYTE nG, BYTE nB, int fReg); void SetPal (unsigned char pal[][3], int fReg); public: // Konstruktor: implicitně standardní VGA a 8 reg. TDit (int nr = 2, int ng = 2, int nb = 2, TVGA Vga = VGA_640_480_16, int firstReg = 0); inline void PutPixel(int x,int y,BYTE r,BYTE g,BYTE b); ~TDit (void) { closegraph (); } }; static int videomode; // Nezbytná fce pro detekci karty SVGA, // volaná v InitGraph. int huge DetectVGA(void) { int gd, gm; detectgraph(&gd, &gm); if (gd != VGA) ERROR ("Chyba: Vaše karta nepodporuje SVGA mód.\n"); return videomode; } // Inicializace grafického modu - bud VGA nebo SVGA void TDit::InitGraph (TVGA vga) { int gd = DETECT, gm; if (vga != VGA_640_480_16) // Pro stand.VGA není třeba { videomode = (int)vga; // komunikace s DetectVGA() gm = installuserdriver("SVGA256", DetectVGA); } initgraph(&gd, &gm, ""); int errcode; if ((errcode = graphresult())!= grOk) { printf("Chyba: %s\n", grapherrormsg(errcode)); ERROR (""); } } // Inicializace dit. matice velikosti DIT*DIT void TDit::InitDitMatrix (void) { int i, j, e = 1; dit[0][0] = 0; while(e != DIT) // Matici plní iterační proces jistými { // stanovenými hodnotami (viz DITHERING) for (i = 0; i < e; i++) for (j = 0; j < e; j++) { dit[i][j] *= 4; dit[i+e][j] = dit[i][j]+2; dit[i][j+e] = dit[i][j]+3; dit[i+e][j+e] = dit[i][j]+1; } e *= 2; } } // Nastaveni optimální palety pro barevný dithering void TDit::BestPal (BYTE nR, BYTE nG, BYTE nB, int fReg) { unsigned char rgb_array[MXC+1][3]; int i, j, k, index = 0, d = (vga==VGA_640_480_16 ? 1 : 4); for (i = 0; i < nR; i++) for (j = 0; j < nG; j++) for (k = 0; k < nB; k++) { rgb_array[index][0] = (i*MXC/nR1)/d; rgb_array[index][1] = (j*MXC/nG1)/d; rgb_array[index][2] = (k*MXC/nB1)/d; index++; } SetPal(rgb_array, fReg); } // Nastavení palety barev počínaje registrem fReg void TDit::SetPal (unsigned char pal[][3], int fReg) { if (vga == VGA_640_480_16) // Stand. VGA v 16 barvách { // ! mapování registrů kompatibilní s EGA int ega[16] = { 0,1,2,3,4,5,20,7,56,57,58,59,60,61,62,63 }; for (int i = 0; i < nR*nG*nB; i++) setrgbpalette (ega[fReg+i], pal[i][0], pal[i][1], pal[i][2]); } else // SVGA lze nastavit službou BIOSu { // _ES:_DX .. adr., _BX .. první reg, _CX počet reg int n = nR*nG*nB; // !!! nelze přiřadit do _CX přímo _ES = FP_SEG (pal); _DX = FP_OFF (pal); _AX = 0x1012; _BX = fReg; _CX = n; geninterrupt (0x10); } } // Constructor: nr, ng a nb v rozsahu <2 .. 64> TDit::TDit (int nr, int ng, int nb,TVGA Vga,int firstReg) { nR = nr; nG = ng; nB = nb; fReg = firstReg; nR1 = nR-1; nG1 = nG-1; nB1 = nB-1; vga = Vga; // Je vstup O.K.? if ((vga == VGA_640_480_16 && nR*nG*nB > 16) || (vga != VGA_640_480_16 && nR*nG*nB > 256)) ERROR ("Chyba: Požadováno příliš barev.\n"); InitGraph (vga); InitDitMatrix (); BestPal (nR, nG, nB, fReg); } // Toto je ZÁKLADNÍ FUNKCE pro barevný dithering !!! void TDit::PutPixel (int x, int y, BYTE r,BYTE g,BYTE b) { unsigned char d = dit[x%DIT][y%DIT];// pravděpodobnost int reg = fReg + (b*nB1)/MXC + nB*((g*nG1)/MXC) + nG*nB*((r*nR1)/MXC); // Máme vybranou barevnou krychli a teď vrchol krychle if ((nB1*b % MXC) > d) reg ++; if ((nG1*g % MXC) > d) reg += nB; if ((nR1*r % MXC) > d) reg += nB*nG; putpixel (x, y, reg); // Když optimalizovat, pak zde!!! } typedef struct // Hlavička TGA - zkráceno { BYTE idf, cmt, itc; //itc==2 -> TGA RGB, nekomprimována short cmo, cml; BYTE cme; short xo, yo, w, h; BYTE ips, id; } TGAhead; void OpenTGA (char *name, TDit & DitTGA) { FILE *f; TGAhead head; if ((f = fopen (name, "rb")) == NULL) ERROR ("Chyba: Nelze otevřít vstupní soubor.\n"); fread (&head, 1, 18, f); if (head.itc != 2) ERROR ("Chyba: Nepodporovaný formát TGA.\n"); for (int y = 0; y < head.h; y++) for (int x = 0; x < head.w; x++) { BYTE b = (BYTE)fgetc(f), g = (BYTE)fgetc(f), r = (BYTE)fgetc(f); DitTGA.PutPixel (x, head.h - y, r, g, b); } fclose (f); } // Main: zobrazí obr. DEMO.TGA v režimech VGA a SVGA (3x) void main (void) { // Standardní VGA 640x480x16 by měla fungovat všude. // Při potížích přezkoušejte přítomnost EGAVGA.BGI. { // Na konci bloku destruktor vypne grafiku TDit DitLo; // Využití impl. par. --> stand. VGA OpenTGA("DEMO.TGA", DitLo); delay (3000); } // SuperVGA mód byl odzkoušen na kartě TRIDENT. // Máte-li nekompatib. kartu, zvolte jiný BGI ovladač. { TDit DitHi (7,6,6, SVGA_640_480); //Využití 7*6*6 bar. OpenTGA("demo.tga", DitHi); delay (3000); } // Tento SuperVGA mód by měl fungovat. { TDit DitHi (7,6,6, SVGA_800_600); //Využití 7*6*6 bar. OpenTGA("demo.tga", DitHi); delay (3000); } // Rozlišení 1024x768 vyžaduje kartu s 1 MB RAM. // Nemáte-li ji, program se ukonči detekovanou chybou. TDit DitSuper (7,6,6, SVGA_1024_768);//Máte 1 MB VRAM? OpenTGA("demo.tga", DitSuper); delay (7000); }


Pascal - hlavní
Překladače
Vlastní články
Převzaté články
Věci na stáhnutí
Odkazy k tématu
BP7 buglist
Chyba Run-time 200

BASIC