Bitmapy

Bitmapa

Objekt typu TBitmap - skrátene bitmapa:

* predstavuje rastrový obrázok - t.j. obrázok zložený z matice pixlov,
* je uložená v pamäti počítača,
* umožňuje pracovať so súbormi typu *.bmp,
* má vlastnú grafickú plochu (Canvas), do ktorej môžeme kresliť známymi príkazmi,
* bitmpu môžeme nakresliť do inej grafickej plochy (aj do inej bitmapy).

Deklarujeme smerník na objekt typu TBitmap:

Graphics::TBitmap *b;

1. Čo znamená Graphics::

Operátor :: sa používa pri určení oboru viditeľnosti metódy, triedy, premennej a podobne. Trieda TBitmap je definovaná v knižnici Graphics. Úplne iný typ, ale s rovnakým názvom je definovaný aj v knižnici Windows. Pritom sa tieto knižnice štandarde používajú v našich programoch. Ak napíšeme iba:
TBitmap b; ... vznikne chyba: Ambiguity between 'TBitmap' and 'Windows::TBitmap'
To znamená, že kompilátor sa nevie rozhodnúť, ktorý typ TBitmap chceme používať. Operátorom :: určíme typ presne.

2. Prečo je deklarovaný smerník na objekt a nie objekt:

Kvôli kompatibilite objektov s inými programovacími jazykmi. Objekty typu TBitmap nesmú byť statické, ale musia sa vytvárať ako dynamické premenné. Preto deklarujeme smerník na objekt typu TBitmap, samotný objekt vytvoríme ako dynamickú premennú. Smerník použijeme na to, aby sme s objektom mohli pracovať.

Graphics::TBitmap *b;

void __fastcall TForm1::FormCreate(TObject *Sender) // udalosť OnCreate
{
b=new Graphics::TBitmap; // vytvoríme bitmapu
b->Width=256; // nastavíme rozmery bitmapy: šírka
b->Height=256; // výška (inak by mala bitmapa nulové rozmery)
int x, y;
for (y=0; y<256; y++)
for (x=0; x<256; x++)
b->Canvas->Pixels[x][y]=TColor(RGB (x, y, 0));
// bitmapa má svoju grafickú plochu, do ktorej sa dá kresliť
}

void __fastcall TForm1::FormDestroy(TObject *Sender) // udalosť OnDestroy
{
delete b; // pri skončení zrušíme bitmapu
}

void __fastcall TForm1::Image1MouseMove(TObject *Sender,
TShiftState Shift, int X, int Y)
{
if (!Shift.Contains(ssLeft)) return;
Image1->Canvas->Draw(X-128, Y-128 ,b); // do Image1 sa nakreslí bitmapa
}

Bitmapu môžeme triviálnym spôsobom uložiť do súboru alebo prečítať zo súboru:

* b->SaveToFile("farby.bmp" ); // ulož do súboru
* b->LoadFromFile("farby.bmp&quo t;); // prečítaj zo súboru


Animované obrázky

Graphics::TBitmap *b[8];
int Faza=0;
int X=300;

void __fastcall TForm1::FormCreate(TObject *Sender)
{
int i;
for (i=0; i<8; i++) {
b[i]=new Graphics::TBitmap;
b[i]->LoadFromFile("zajo" +IntToStr(i)+".bmp");
b[i]->Transparent=true;
}
}

void __fastcall TForm1::FormDestroy(TObject *Sender)
{
int i;
for (i=0; i<8; i++) delete b[i];
}

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
Image1->Canvas->FillRect(Image1-&g t;ClientRect);
Image1->Canvas->Draw(X, 100, b[Faza]);
Faza=(Faza+1)%8;
X=X-15;
}

Kompletný program aj s obrázkami


Konštruktor a deštruktor objektu

Bitmapy budeme často používať ako súčasť nejakého objektu:

class TCosi {
private:
Graphics::TBitmap *b;
public:
void Inic();
void Uprac();
...
};

void TCosi::Inic()
{
b=new Graphics::TBitmap;
b->LoadFromFile("obrazok.bmp&q uot;);
}

void TCosi::Uprac()
{
delete b;
}

TCosi c;
c.Inic();
...
c.Uprac();

Vidíme, že pri inicialozovaní objektu sa bitmapa vytvorí a prečíta, pri skončení práce s objektom sa bitmapa uvoľní.

Takéto programovanie je náchylné na chyby:

* nesmieme zabúdať objekt inicializovať (inak by neexistovala bitmapa),
* pri skončení práce s objektom musíme volať metódu Uprac() (inak by sme zaplnili pamäť obrázkami, ktoré už nikdy nebudeme potrebovať).

Pomocou dvoch špeciálnych metód - konštruktora a deštruktora, je inicializovanie a upratovanie objektu elegantnejšie:

* konštruktor objektu je metóda, ktorá sa automaticky vykoná ihneď po tom, ako objekt v pamäti vznikol - napríklad, v konštruktore vytvoríme bitmapu a prečítame ju zo súboru,
* deštruktor objektu je metóda, ktorá sa automaticky vykoná pred tým, ako sa objekt zruší - napríklad, v deštruktore zrušíme bitmapu.

TCosi s využitím konštruktora a deštruktora:
class TCosi {
private:
Graphics::TBitmap *b;
public:
TCosi(); // konštruktor
~TCosi(); // deštruktor
...
};

TCosi::TCosi()
{
b=new Graphics::TBitmap;
b->LoadFromFile("obrazok.bmp&q uot;);
}

TCosi::~TCosi()
{
delete b;
}

Pri deklarácii:

TCosi c; ... sa automaticky zavolá konštruktor objektu c

Ak je c globálna premenná, deštruktor sa zavolá automaticky pri skončení programu. Ak je c lokálna premenná, deštruktor sa zavolá automaticky pri skončení volania funkcie.

Všimnime si, že:

* konštruktor objektu je metóda, ktorá má špeciálne meno - je zhodné s menom objektovej triedy: TCosi(),
* meno deštruktora objektu začína ~ (vlnovkou), za ktorým nasleduje meno objektovej triedy: ~TCosi(),
* konštruktor nemôžeme a deštruktor nesmieme zavolať priamo z programu - teda nesmieme: c.~TCosi();
* konštruktor môže mať aj parametre, deštruktor ich nesmie mať,
* konštruktor ani deštruktor nevracajú žiadne hodnoty.


Konštruktor s parametrami

Objektová trieda môže mať viacej konštruktorov, musia sa však líšiť v parametroch (deštruktor je vždy iba jeden a bez parametrov). Pridáme ešte jeden konštruktor s parametrom:
class TCosi {
private:
Graphics::TBitmap *b;
public:
TCosi(); // základný konštruktor
TCosi(AnsiString Meno); // konštruktor s parametrom
~TCosi(); // deštruktor
...
};

TCosi::TCosi()
{
b=new Graphics::TBitmap;
b->LoadFromFile("obrazok.bmp&q uot;);
}

TCosi::TCosi(AnsiString Meno)
{
b=new Graphics::TBitmap;
b->LoadFromFile(Meno);
}

TCosi::~TCosi()
{
delete b;
}

Potom:

TCosi c; ... volá sa základný konštruktor
TCosi d("Mesiac.bmp"); ... volá sa konštruktor s parametrom
TCosi *p;
p=new TCosi("Mesiac.bmp"); ... volá sa konštruktor s parametrom

Každá trieda by mala mať základný konštruktor (tzv. default constructor). Aj keď sme do dnešného dňa konštruktory nepoznali, kompilátor za nás vždy definoval základný konštruktor.

Pozor: Ak by sme v triede TCosi definovali iba konštruktor s parametrom (ale nedefinovali by sme základný konštruktor), mohli by sme mať problémy s deklaráciou:

TCosi c; ... chyba, kompilátor nepozná základný konštruktor
TCosi c("obrazok.bmp"); ... ok, kompilátor zavolá konštruktor s parametrom


Pohyblivé objekty

Pohyb jednoduchých útvarov po obrazovke sme programovali podľa nasledujúcej schémy:

* vykonali sme zmenu (počítadla, polohy, stavu útvaru),
* zmazali obrázok,
* do čistého obrázka sme nakreslili útvary, ktoré tvoria scénu.

Aj teraz budeme postupovať rovnako, ale s tým, že využijeme objekty. Skúsime vytvoriť jednoduchý program - loptu, ktorá sa odráža od okrajov hracej plochy:

* plocha má rozmery 400x300 obrazových bodov
* potrebné obrázky sú uložené v súboroch: pozadie.bmp, lopta.bmp.

class TLopta {
private:
Graphics::TBitmap *b;
int X, Y, dX, dY;
public:
TLopta(); // inicializuje objekt (vytvorí a nahrá bitmapu, ...)
~TLopta(); // uvoľní bitmapu
void Pohni(); // vykoná pohyb objektu (1 krok animácie)
void Kresli(); // nakreslí objekt (bitmapu)
};

TLopta::TLopta() {
b=new Graphics::TBitmap;
b->LoadFromFile("Lopta.bmp&quo t;);
b->TransparentColor=clRed; // červená bude priesvitná farba
b->Transparent=true; // povolíme priesvitnosť
X=200;
Y=100;
dX=1+random(10);
dY=1+random(10);
}

TLopta::~TLopta() {
delete b;
}

void TLopta::Pohni() {
X=X+dX;
Y=Y+dY;
if (X<b->Width/2) {
X=b->Width/2;
dX=-dX;
}
if (Y<b->Height/2) {
Y=b->Height/2;
dY=-dY;
}
if (X>300-b->Width/2) {
X=300-b->Width/2;
dX=-dX;
}
if (Y>300-b->Height/2) {
Y=300-b->Height/2;
dY=-dY;
}
}

void TLopta::Kresli() {
Form1->Image->Canvas->Draw(X-b- >Width/2, Y-b->Height/2, b);
}

//-------------------------------------- -----------------------------------

TLopta l;
Graphics::TBitmap *poz; // smerník na bitmapu s pozadím

void __fastcall TForm1::FormCreate(TObject *Sender) {
poz=new Graphics::TBitmap; // vytvoríme a nahráme bitmapu pozadia:
poz->LoadFromFile("pozadie.bmp ");
}

void __fastcall TForm1::FormDestroy(TObject *Sender) {
delete poz; // uvoľníme pozadie
}

void __fastcall TForm1::Timer1Timer(TObject *Sender) {
l.Pohni(); // vykonáme pohyb
Image->Canvas->Draw(0, 0, poz); // nakreslíme pozadie
l.Kresli(); // nakreslíme loptu
}

Program môžeme triviálne upraviť tak, aby sa na obrazovke hýbalo veľa lôpt:
TLopta l[10];

void __fastcall TForm1::Timer1Timer(TObject *Sender) {
int i;
for(i=0; i<10; i++) l[i].Pohni();
Image->Canvas->Draw(0, 0, poz);
for(i=0; i<10; i++) l[i].Kresli();
}

Kompletný program aj s obrázkami

O automatické inicializovanie a upratanie objektov z poľa l sa stará konštruktor a deštruktor každého objektu.


Zhrnutie

Pri práci s bitmapami:

*

najskôr deklarujeme smerník na bitmapu a vytvoríme ju ako dynamickú premennú:
Graphics::TBitmap *b
b=new Graphics::TBitmap
*

potom môžeme:
o nastaviť rozmery bitmapy:
b->Width=šírka;
b->Height=výška;
o prečítať obrázok zo súboru: b->LoadFromFile(meno_súboru);
o nakresliť bitmapu do grafickej plochy: Image1->Canvas->Draw(x, y, b);
o kresliť do maliarskeho plátna bitmapy: b->Canvas-> ...
*

po skončení práce treba bitmapu uvoľniť: delete b;

Konštruktor je špeciálna metóda, ktorá:

* sa automaticky zavolá ihneď potom, ako objekt v pamäti vznikne,
* slúži na inicializovanie objektu (nastavenie hodnôt, vytvorenie dynamických premenných, ktoré sa v objekte používajú a pod.),
* má špeciálne meno, ktoré sa zhoduje s menom triedy, do ktorej konštrukor patrí,
* môže mať parametre (je však výhodné, ak v triede existuje aj konštruktor bez parametrov - tzv. základný konštruktor),
* nevracia hodnotu.

Deštruktor je špeciálna metóda, ktorá:

* sa automaticky zavolá tesne pred tým, ako objekt zaniká,
* slúži na upratanie objektu (zrušenie dynamických premenných, ktoré objekt vytvoril a pod.),
* má špeciálne meno, ktoré začína ~ a zvyšok mena sa zhoduje s menom triedy, do ktorej deštruktor patrí,
* nemá parametre,
* nevracia hodnotu.