V lekci 24 jsem vßm ukßzal cestu, jak nahrßvat nekomprimovanΘ 24/32 bitovΘ TGA obrßzky. Jsou velmi u₧iteΦnΘ, kdy₧ pot°ebujete alfa kanßl, ale nesmφte se starat o jejich velikost, proto₧e byste je ihned p°estali pou₧φvat. K diskovΘmu mφstu nejsou zrovna ÜetrnΘ. ProblΘm velikosti vy°eÜφ nahrßvßnφ obrßzk∙ komprimovan²ch metodou RLE. K≤d pro loading a hlaviΦkovΘ soubory jsou odd∞leny od hlavnφho projektu, aby mohly b²t snadno pou₧ity i jinde.
ZaΦneme dv∞ma hlaviΦkov² soubory. Texture.h, prvnφ z nich, popisuje strukturu textury. Ka₧d² hlaviΦkov² soubor by m∞l obsahovat ochranu proti vφcenßsobnΘmu vlo₧enφ. ZajiÜ¥ujφ ji p°φkazy preprocesoru jazyka C. Pokud nenφ definovanß symbolickß konstanta __TEXTURE_H__, nadefinujeme ji a do stejnΘho bloku podmφnky vepφÜeme zdrojov² k≤d. P°i nßsledujφcφm pokusu o inkludovßnφ hlaviΦkovΘho souboru existence konstanty oznßmφ preprocesoru, ₧e u₧ byl soubor jednou vlo₧en, a tudφ₧ ho nemß vklßdat podruhΘ.
#ifndef __TEXTURE_H__
#define __TEXTURE_H__
Budeme pot°ebovat strukturu informacφ o obrßzku, ze kterΘho se vytvß°φ textura. Ukazatel imageData obsahuje data obrßzku, bpp barevnou hloubku, width a height rozm∞ry. TexID je identifikßtorem OpenGL textury, kter² se p°edßvß funkci glBindTexture(). Type urΦuje typ textury - GL_RGB nebo GL_RGBA.
typedef struct// Struktura textury
{
GLubyte* imageData;// Data
GLuint bpp;// Barevnß hloubka v bitech
GLuint width;// èφ°ka
GLuint height;// V²Üka
GLuint type;// Typ (GL_RGB, GL_RGBA)
GLuint texID;// ID textury
} Texture;
#endif
Druh² hlaviΦkov² soubor, tga.h, je specißln∞ urΦen pro loading TGA. Op∞t zaΦneme oÜet°enφm vφcenßsobnΘho inkludovßnφ, potΘ vlo₧φme hlaviΦkov² soubor textury.
#ifndef __TGA_H__
#define __TGA_H__
#include "texture.h"// HlaviΦkov² soubor textury
Strukturu TGAHeader p°edstavuje pole dvanßcti byt∙, kterΘ uklßdajφ hlaviΦku obrßzku. Druhß struktura obsahuje pomocnΘ prom∞nnΘ pro nahrßvßnφ - nap°. velikost dat, barevnou hloubku a podobn∞.
typedef struct// HlaviΦka TGA souboru
{
GLubyte Header[12];// Dvanßct byt∙
} TGAHeader;
typedef struct// Struktura obrßzku
{
GLubyte header[6];// èest u₧iteΦn²ch byt∙ z hlaviΦky
GLuint bytesPerPixel;// Barevnß hloubka v bytech
GLuint imageSize;// Velikost pam∞ti pro obrßzek
// GLuint temp;// P°ekl.: nikde nenφ pou₧itß
GLuint type;// Typ
GLuint Height;// V²Üka
GLuint Width;// èφ°ka
GLuint Bpp;// Barevnß hloubka v bitech
} TGA;
Deklarujeme instance prßv∞ vytvo°en²ch struktur, abychom je mohli pou₧φt v programu.
TGAHeader tgaheader;// TGA hlaviΦka
TGA tga;// TGA obrßzek
Nßsledujφcφ dv∞ pole pomohou urΦit validitu nahrßvanΘho souboru. Pokud se hlaviΦka obrßzku neshoduje s n∞kterou z nich, neumφme ho nahrßt.
GLubyte uTGAcompare[12] = { 0,0, 2,0,0,0,0,0,0,0,0,0 };// TGA hlaviΦka nekomprimovanΘho obrßzku
GLubyte cTGAcompare[12] = { 0,0,10,0,0,0,0,0,0,0,0,0 };// TGA hlaviΦka komprimovanΘho obrßzku
Ob∞ funkce nahrßvajφ TGA - jedna nekomprimovan² druhß komprimovan².
bool LoadUncompressedTGA(Texture*, char*, FILE*);// Nekomprimovan² TGA
bool LoadCompressedTGA(Texture*, char*, FILE*);// Komprimovan² TGA
#endif
P°esuneme se k souboru TGALoader.cpp, kter² implementuje nahrßvacφ funkce. Prvnφm °ßdkem k≤du vlo₧φme hlaviΦkov² soubor. Inkludujeme pouze tga.h, proto₧e texture.h jsme u₧ vlo₧ili v n∞m.
#include "tga.h"// HlaviΦkov² soubor TGA
Funkce LoadTGA() je ta, kterou v programu volßme, abychom nahrßli obrßzek. V parametrech se jφ p°edßvß ukazatel na texturu a °et∞zec diskovΘ cesty. Nic dalÜφho nepot°ebuje, proto₧e si vÜechny ostatnφ parametry detekuje sama (ze souboru). Deklarujeme handle souboru a otev°eme ho pro Φtenφ v binßrnφm m≤du. Pokud n∞co sel₧e, nap°. soubor neexistuje, vypφÜeme chybovou zprßvu a vrßtφme false jako indikaci chyby.
bool LoadTGA(Texture* texture, char* filename)// Nahraje TGA soubor
{
FILE* fTGA;// Handle souboru
fTGA = fopen(filename, "rb");// Otev°e soubor
if(fTGA == NULL)// Nepoda°ilo se ho otev°φt?
{
MessageBox(NULL, "Could not open texture file", "ERROR", MB_OK);
return false;
}
Zkusφme naΦφst hlaviΦku obrßzku (prvnφch 12 byt∙ souboru), kterß urΦuje jeho typ. V²sledek se ulo₧φ do prom∞nnΘ tgaheader.
if(fread(&tgaheader, sizeof(TGAHeader), 1, fTGA) == 0)// NaΦte hlaviΦku souboru
{
MessageBox(NULL, "Could not read file header", "ERROR", MB_OK);
if(fTGA != NULL)
{
fclose(fTGA);
}
return false;
}
Prßv∞ naΦtenou hlaviΦku porovnßme s hlaviΦkou nekomprimovanΘho obrßzku. Jsou-li shodnΘ nahrajeme obrßzek funkcφ LoadUncompressedTGA(). Pokud shodnΘ nejsou zkusφme, jestli se nejednß o komprimovan² obrßzek. V tomto p°φpad∞ pou₧ijeme pro nahrßvßnφ funkci LoadCompressedTGA(). S jin²mi typy soubor∙ pracovat neumφme, tak₧e jedinΘ, co m∙₧eme ud∞lat, je oznßmenφ ne·sp∞chu a ukonΦenφ funkce.
P°ekl.: M∞la by se jeÜt∞ testovat nßvratovß hodnota, proto₧e, jak uvidφte dßle, funkce v mnoha p°φpadech vracejφ false. Program by si bez kontroly niΦeho nevÜiml a pokraΦoval dßle.
if(memcmp(uTGAcompare, &tgaheader, sizeof(tgaheader)) == 0)// Nekomprimovan²
{
LoadUncompressedTGA(texture, filename, fTGA);
// P°ekl.: Testovat nßvratovou hodnotu !!!
// if(!LoadUncompressedTGA(texture, filename, fTGA))// Test nßvratovΘ hodnoty
// {
// return false;
// }
}
else if(memcmp(cTGAcompare, &tgaheader, sizeof(tgaheader)) == 0)// Komprimovan²
{
LoadCompressedTGA(texture, filename, fTGA);
// P°ekl.: Testovat nßvratovou hodnotu !!!
// if(!LoadCompressedTGA(texture, filename, fTGA))// Test nßvratovΘ hodnoty
// {
// return false;
// }
}
else// Ani jeden z nich
{
MessageBox(NULL, "TGA file be type 2 or type 10 ", "Invalid Image", MB_OK);
fclose(fTGA);
return false;
}
Pokud dosud nenastala ₧ßdnß chyba, m∙₧eme oznßmit volajφcφmu k≤du, ₧e obrßzek byl v po°ßdku nahrßn a ₧e m∙₧e z jeho dat vytvo°it texturu.
return true;// VÜe v po°ßdku
}
P°istoupφme k opravdovΘmu nahrßvßnφ obrßzk∙, zaΦneme nekomprimovan²mi. Tato funkce je z velkΘ Φßsti zalo₧ena na tΘ z lekce 24, moc novinek v nφ nenajdete. Zkusφme naΦφst dalÜφch Üest byt∙ ze souboru a ulo₧φme je do tga.header.
bool LoadUncompressedTGA(Texture* texture, char* filename, FILE* fTGA)// Nahraje nekomprimovan² TGA
{
if(fread(tga.header, sizeof(tga.header), 1, fTGA) == 0)// èest u₧iteΦn²ch byt∙
{
MessageBox(NULL, "Could not read info header", "ERROR", MB_OK);
if(fTGA != NULL)
{
fclose(fTGA);
}
return false;
}
Mßme dost informacφ pro urΦenφ v²Üky, Üφ°ky a barevnΘ hloubky obrßzku. Ulo₧φme je do obou struktur - textury i obrßzku.
texture->width = tga.header[1] * 256 + tga.header[0];// èφ°ka
texture->height = tga.header[3] * 256 + tga.header[2];// V²Üka
texture->bpp = tga.header[4];// Barevnß hloubka v bitech
// Kopφrovßnφ dat do struktury obrßzku
tga.Width = texture->width;
tga.Height = texture->height;
tga.Bpp = texture->bpp;
Otestujeme, jestli mß obrßzek alespo≥ jeden pixel a jestli je barevnß hloubka 24 nebo 32 bit∙.
// PlatnΘ hodnoty?
if((texture->width <= 0) || (texture->height <= 0) || ((texture->bpp != 24) && (texture->bpp != 32)))
{
MessageBox(NULL, "Invalid texture information", "ERROR", MB_OK);
if(fTGA != NULL)
{
fclose(fTGA);
}
return false;
}
Nynφ nastavφme typ obrßzku. V p°φpad∞ 24 bit∙ je jφm GL_RGB, u 32 bit∙ mß obrßzek i alfa kanßl, tak₧e pou₧ijeme GL_RGBA.
if(texture->bpp == 24)// 24 bitov² obrßzek?
{
texture->type = GL_RGB;
}
else// 32 bitov² obrßzek
{
texture->type = GL_RGBA;
}
SpoΦφtßme barevnou hloubku v BYTECH a celkovou velikost pam∞ti pot°ebnou pro data. Vzßp∞tφ se ji pokusφme alokovat.
tga.bytesPerPixel = (tga.Bpp / 8);// BYTY na pixel
tga.imageSize = (tga.bytesPerPixel * tga.Width * tga.Height);// Velikost pam∞ti
texture->imageData = (GLubyte *)malloc(tga.imageSize);// Alokace pam∞ti pro data
if(texture->imageData == NULL)// Alokace ne·sp∞Ünß
{
MessageBox(NULL, "Could not allocate memory for image", "ERROR", MB_OK);
fclose(fTGA);
return false;
}
Pokud se poda°ila alokace pam∞ti, nahrajeme do nφ data obrßzku.
// Pokusφ se nahrßt data obrßzku
if(fread(texture->imageData, 1, tga.imageSize, fTGA) != tga.imageSize)
{
MessageBox(NULL, "Could not read image data", "ERROR", MB_OK);
if(texture->imageData != NULL)
{
free(texture->imageData);// Uvoln∞nφ pam∞ti
}
fclose(fTGA);
return false;
}
Formßt TGA se od formßtu OpenGL liÜφ tφm, ₧e mß v pixelech p°ehozenΘ R a B slo₧ky barvy (BGR mφsto RGB). Musφme tedy zam∞nit prvnφ a t°etφ byte v ka₧dΘm pixelu. Abychom tuto operace urychlili, provedeme t°i binßrnφ operace XOR. V²sledek je stejn² jako p°i pou₧itφ pomocnΘ prom∞nnΘ.
// P°evod BGR na RGB
for(GLuint cswap = 0; cswap < (int)tga.imageSize; cswap += tga.bytesPerPixel)
{
texture->imageData[cswap] ^= texture->imageData[cswap+2] ^=
texture->imageData[cswap] ^= texture->imageData[cswap+2];
}
Obrßzek jsme ·sp∞Ün∞ nahrßli, tak₧e zav°eme soubor a vrßcenφm true oznßmφme ·sp∞ch.
fclose(fTGA);// Zav°enφ souboru
return true;// ┌sp∞ch
// Pam∞¥ dat obrßzku se uvol≥uje a₧ po vytvo°enφ textury
}
Nynφ p°istoupφme k nahrßvßnφ obrßzku komprimovanΘho metodou RLE (RunLength Encoded). ZaΦßtek je stejn² jako u nekomprimovanΘho obrßzku - naΦteme v²Üku, Üφ°ku a barevnou hloubku, oÜet°φme neplatnΘ hodnoty a spoΦφtßme velikost pot°ebnΘ pam∞ti, kterou op∞t alokujeme. VÜimn∞te si, ₧e velikost po₧adovanΘ pam∞ti je takovß, aby do nφ mohla b²t ulo₧ena data PO DEKOMPRIMOV┴N═, ne p°ed dekomprimovßnφm.
bool LoadCompressedTGA(Texture* texture, char* filename, FILE* fTGA)// Nahraje komprimovan² obrßzek
{
if(fread(tga.header, sizeof(tga.header), 1, fTGA) == 0)// èest u₧iteΦn²ch byt∙
{
MessageBox(NULL, "Could not read info header", "ERROR", MB_OK);
if(fTGA != NULL)
{
fclose(fTGA);
}
return false;
}
texture->width = tga.header[1] * 256 + tga.header[0];// èφ°ka
texture->height = tga.header[3] * 256 + tga.header[2];// V²Üka
texture->bpp = tga.header[4];// Barevnß hloubka v bitech
// Kopφrovßnφ dat do struktury obrßzku
tga.Width = texture->width;
tga.Height = texture->height;
tga.Bpp = texture->bpp;
// PlatnΘ hodnoty?
if((texture->width <= 0) || (texture->height <= 0) || ((texture->bpp != 24) && (texture->bpp != 32)))
{
MessageBox(NULL, "Invalid texture information", "ERROR", MB_OK);
if(fTGA != NULL)
{
fclose(fTGA);
}
return false;
}
if(texture->bpp == 24)// 24 bitov² obrßzek?
{
texture->type = GL_RGB;
}
else// 32 bitov² obrßzek
{
texture->type = GL_RGBA;
}
tga.bytesPerPixel = (tga.Bpp / 8);// BYTY na pixel
tga.imageSize = (tga.bytesPerPixel * tga.Width * tga.Height);// Velikost pam∞ti
texture->imageData = (GLubyte *)malloc(tga.imageSize);// Alokace pam∞ti pro data (po dekomprimovßnφ)
if(texture->imageData == NULL)// Alokace ne·sp∞Ünß
{
MessageBox(NULL, "Could not allocate memory for image", "ERROR", MB_OK);
fclose(fTGA);
return false;
}
Dßle pot°ebujeme zjistit p°esn² poΦet pixel∙, ze kter²ch je obrßzek slo₧en. JednoduÜe vynßsobφme v²Üku obrßzku se Üφ°kou. TakΘ musφme znßt, na kterΘm pixelu se prßv∞ nachßzφme a kam do pam∞ti zapisujeme.
GLuint pixelcount = tga.Height * tga.Width;// PoΦet pixel∙
GLuint currentpixel = 0;// Aktußlnφ naΦφtan² pixel
GLuint currentbyte = 0;// Aktußlnφ naΦφtan² byte
Alokujeme pomocnΘ pole t°φ nebo Φty° byt∙ (podle barevnΘ hloubky) k ulo₧enφ jednoho pixelu. P°ekl.: M∞la by se testovat sprßvnost alokace pam∞ti!
GLubyte* colorbuffer = (GLubyte *)malloc(tga.bytesPerPixel);// Pam∞¥ pro jeden pixel
// P°ekl.: Test ·sp∞Ünosti alokace pam∞ti !!!
// if(colorbuffer == NULL)// Alokace ne·sp∞Ünß
// {
// MessageBox(NULL, "Could not allocate memory for color buffer", "ERROR", MB_OK);
// fclose(fTGA);
// return false;
// }
V hlavnφm cyklu deklarujeme prom∞nnou k ulo₧enφ bytu hlaviΦky, kter² definuje, jestli je nßsledujφcφ sekce obrßzku ve formßtu RAW nebo RLE a jak dlouhß je. Pokud je byte hlaviΦky menÜφ nebo roven 127, jednß se o RAW hlaviΦku. Hodnota, v nφ ulo₧enß, urΦuje poΦet pixel∙ mφnus jedna, kterΘ vzßp∞tφ naΦteme a zkopφrujeme do pam∞ti. Po t∞chto pixelech se v souboru vyskytuje dalÜφ byte hlaviΦky. Pokud je byte hlaviΦky v∞tÜφ ne₧ 127, p°edstavuje toto Φφslo (zmenÜenΘ o 127), kolikrßt se mß nßsledujφcφ pixel v dekomprimovanΘm obrßzku opakovat. Hned po n∞m se bude vyskytovat dalÜφ hlaviΦkov² byte. NaΦteme hodnoty tohoto pixelu a zkopφrujeme ho do imageData tolikrßt, kolikrßt pot°ebujeme.
Podstatu komprese RLE tedy u₧ znßte, podφvejme se na k≤d. Jak jsem ji₧ zmφnil, zalo₧φme cyklus p°es cel² soubor a pokusφme se naΦφst byte prvnφ hlaviΦky.
do// Prochßzφ cel² soubor
{
GLubyte chunkheader = 0;// Byte hlaviΦky
if(fread(&chunkheader, sizeof(GLubyte), 1, fTGA) == 0)// NaΦte byte hlaviΦky
{
MessageBox(NULL, "Could not read RLE header", "ERROR", MB_OK);
if(fTGA != NULL)
{
fclose(fTGA);
}
if(texture->imageData != NULL)
{
free(texture->imageData);
}
// P°ekl.: Uvoln∞nφ dynamickΘ pam∞ti !!!
// if(colorbuffer != NULL)
// {
// free(colorbuffer);
// }
return false;
}
Pokud se jednß o RAW hlaviΦku, p°iΦteme k bytu jedniΦku, abychom zφskali poΦet pixel∙ nßsledujφcφch po hlaviΦce. Potom zalo₧φme dalÜφ cyklus, kter² naΦφtß vÜechny po₧adovanΘho pixely do pomocnΘho pole colorbuffer a vzßp∞tφ je ve sprßvnΘm formßtu uklßdß do imageData.
if(chunkheader < 128)// RAW Φßst obrßzku
{
chunkheader++;// PoΦet pixel∙ v sekci p°ed v²skytem dalÜφho bytu hlaviΦky
for(short counter = 0; counter < chunkheader; counter++)// JednotlivΘ pixely
{
// NaΦφtßnφ po jednom pixelu
if(fread(colorbuffer, 1, tga.bytesPerPixel, fTGA) != tga.bytesPerPixel)
{
MessageBox(NULL, "Could not read image data", "ERROR", MB_OK);
if(fTGA != NULL)
{
fclose(fTGA);
}
if(colorbuffer != NULL)
{
free(colorbuffer);
}
if(texture->imageData != NULL)
{
free(texture->imageData);
}
return false;
}
P°i kopφrovßnφ do imageData prohodφme po°adφ byt∙ z formßtu BGR na RGB. Pokud je v obrßzku i alfa kanßl, zkopφrujeme i Φtvrt² byte. Abychom se p°esunuli na dalÜφ pixel pop°. byte hlaviΦky, zv∞tÜφme aktußlnφ byte o barevnou hloubku (+3 nebo +4). Inkrementujeme takΘ poΦet naΦten²ch pixel∙.
// Zßpis do pam∞ti, prohodφ R a B slo₧ku barvy
texture->imageData[currentbyte] = colorbuffer[2];
texture->imageData[currentbyte + 1] = colorbuffer[1];
texture->imageData[currentbyte + 2] = colorbuffer[0];
if(tga.bytesPerPixel == 4)// 32 bitov² obrßzek?
{
texture->imageData[currentbyte + 3] = colorbuffer[3];// Kopφrovßnφ alfy
}
currentbyte += tga.bytesPerPixel;// Aktualizuje byte
currentpixel++;// P°esun na dalÜφ pixel
Zjistφme, jestli je po°adovß Φφslo aktußlnφho pixelu v∞tÜφ ne₧ celkov² poΦet pixel∙. Pokud ano, je soubor obrßzku poÜkozen nebo je v n∞m n∞kde chyba. Jak jsme na to p°iÜli? Mßme naΦφtat dalÜφ pixel, ale defakto je u₧ mßme vÜechny naΦtenΘ, proto₧e aktußlnφ hodnota je v∞tÜφ ne₧ maximßlnφ. NestaΦila by alokovanß pam∞¥ pro dekomprimovanou verzi obrßzku. Tuto skuteΦnost musφme ka₧dopßdn∞ oÜet°it.
if(currentpixel > pixelcount)// Jsme za hranicφ obrßzku?
{
MessageBox(NULL, "Too many pixels read", "ERROR", NULL);
if(fTGA != NULL)
{
fclose(fTGA);
}
if(colorbuffer != NULL)
{
free(colorbuffer);
}
if(texture->imageData != NULL)
{
free(texture->imageData);
}
return false;
}
}
}
Vy°eÜili jsme Φßst RAW, nynφ implementujeme sekci RLE. Ze vÜeho nejd°φve od bytu hlaviΦky odeΦteme Φφslo 127, abychom zφskali kolikrßt se mß nßsledujφcφ pixel opakovat.
else// RLE Φßst obrßzku
{
chunkheader -= 127;// PoΦet pixel∙ v sekci
NaΦteme jeden pixel po hlaviΦce a potom ho po₧adovan∞-krßt vlo₧φme do imageData. Op∞t zam∞≥ujeme formßt BGR za RGB. Stejn∞ jako minule inkrementujeme aktußlnφ byte i pixel a oÜet°ujeme p°eteΦenφ.
if(fread(colorbuffer, 1, tga.bytesPerPixel, fTGA) != tga.bytesPerPixel)// NaΦte jeden pixel
{
MessageBox(NULL, "Could not read from file", "ERROR", MB_OK);
if(fTGA != NULL)
{
fclose(fTGA);
}
if(colorbuffer != NULL)
{
free(colorbuffer);
}
if(texture->imageData != NULL)
{
free(texture->imageData);
}
return false;
}
for(short counter = 0; counter < chunkheader; counter++)// Kopφrovßnφ pixelu
{
// Zßpis do pam∞ti, prohodφ R a B slo₧ku barvy
texture->imageData[currentbyte] = colorbuffer[2];
texture->imageData[currentbyte + 1] = colorbuffer[1];
texture->imageData[currentbyte + 2] = colorbuffer[0];
if(tga.bytesPerPixel == 4)// 32 bitov² obrßzek?
{
texture->imageData[currentbyte + 3] = colorbuffer[3];// Kopφrovßnφ alfy
}
currentbyte += tga.bytesPerPixel;// Aktualizuje byte
currentpixel++;// P°esun na dalÜφ pixel
if(currentpixel > pixelcount)// Jsme za hranicφ obrßzku?
{
MessageBox(NULL, "Too many pixels read", "ERROR", NULL);
if(fTGA != NULL)
{
fclose(fTGA);
}
if(colorbuffer != NULL)
{
free(colorbuffer);
}
if(texture->imageData != NULL)
{
free(texture->imageData);
}
return false;
}
}
}
Hlavnφ cyklus opakujeme tak dlouho, dokud v souboru zb²vajφ nenaΦtenΘ pixely. Po konci loadingu soubor zav°eme a vrßcenφm true indikujeme ·sp∞ch.
} while(currentpixel < pixelcount);// PokraΦuj dokud zb²vajφ pixely
// P°ekl.: Uvoln∞nφ dynamickΘ pam∞ti !!!
// if(colorbuffer != NULL)
// {
// free(colorbuffer);
// }
fclose(fTGA);// Zav°enφ souboru
return true;// ┌sp∞ch
// Pam∞¥ dat obrßzku se uvol≥uje a₧ po vytvo°enφ textury
}
Nynφ jsou data obrßzku p°ipravena pro vytvo°enφ textury a to u₧ jist∞ zvlßdnete sami. V tomto tutorißlu nßm Ülo p°edevÜφm o nahrßvßnφ TGA obrßzk∙. UkßzkovΘ demo bylo vytvo°eno jen proto, abyste vid∞li, ₧e k≤d opravdu funguje.
A jak je to s ·sp∞Ünostφ komprimace metody RLE? Je jasnΘ, ₧e nejmenÜφ pam∞¥ bude zabφrat obrßzek s rozsßhl²mi plochami stejn²ch pixel∙ (na °ßdcφch). Pokud chcete Φφsla, tak si vezmeme na pomoc obrßzky pou₧itΘ v tomto demu: oba jsou 128x128 pixel∙ velikΘ, nekomprimovan² zabφrß na disku 48,0 kB a komprimovan² pouze 5,29 kB. Na obou je sice n∞co jinΘho, ale devφtinßsobnΘ zmenÜenφ velikosti mluvφ za vÜe.
napsal: Evan Pipho - Terminate
p°elo₧il: Michal Turek - Woq