Lekce 33

Lekce 33 - Nahrßvßnφ komprimovan²ch i nekomprimovan²ch obrßzk∙ TGA

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

ZdrojovΘ k≤dy

Lekce 33

<<< Lekce 32 | Lekce 34 >>>