8.Prednaska

Z Pascal
Prejsť na: navigácia, hľadanie
8. Prednáška

úlohy | cvičenie


Bitmapa

Keď sme doteraz pracovali s grafickou plochou (komponent Image), väčšinou sme ju ešte pred prvým použitím vyčistili nabielo:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Image1.Canvas.FillRect(Image1.ClientRect);
end;

Lenže do grafickej plochy môžeme vložiť aj nejaký obrázok zo súboru (load from file), resp. momentálny obsah grafickej plochy môžeme do súboru uložiť (save to file). Ukážme to na príklade. Predpokladajme, že máme pripravených niekoľko súborov s obrázkami (zatiaľ by bolo najlepšie, keby to boli súbory typu .BMP). Aby sa nám s nimi čo najlepšie pracovalo, presuňme ich do toho istého priečinka, kde máme lazarusovský projekt (kde je jeho .EXE). Môžete použiť, napr. aj tieto súbory obrazky.zip. Do formulára vložíme grafickú plochu a niekoľko tlačidiel. Vložiť rastrový obrázok (bitmapu) do grafickej plochy robíme pomocou LoadFromFile, napr.

procedure TForm1.Button1Click(Sender: TObject);
begin
  Image1.Picture.LoadFromFile('tiger.bmp');
end;

Takto sa do pozadia grafickej plochy prečíta zadaný súbor a súčasne s tým grafická plocha preberá rozmer obrázka (zmení sa tým Image1.Width a Image1.Height). Týmto príkazom sa dá do grafickej plochy prečítať nielen súbor formátu .BMP, ale napr. aj .PNG a .JPG.


Najčastejšie sa používa úplne iný spôsob ukladania obrázkov do grafickej plochy: obsah nejakého obrázkového súboru najprv prečítame do špeciálnej premennej a potom túto premennú môžeme ľubovoľný počet krát opečiatkovať do grafickej plochy - na ľubovoľné pozície a dokonca pritom môže byť aj zadaný obrázok zmenšený alebo zväčšený. S takýmito premennými na uchovávanie obrázkov (uvidíme neskôr, že sú to objektové premenné) musíme manipulovať špeciálnym spôsobom:

  1. zadeklarujeme ju s typom TBitmap - keďže je to objektová premenná, bude ju treba ešte vytvoriť,
    • deklaráciu zapíšeme napr. Bitmapa: TBitmap; alebo Obr: TBitmap;
  2. obrázkovú premennú vytvoríme tak, že do premennej priradíme zápis TBitmap.Create
    • napr. Bitmapa := TBitmap.Create; alebo Obr := TBitmap.Create;
    • obrázok v takejto premennej má zatiaľ nulové rozmery (jeho šírka aj výška je zatiaľ 0), ak by sme ho teraz chceli niekde zobraziť, neuvidíme vôbec nič
  3. do obrázkovej premennej teraz môžeme prečítať nejaký obrázok zo súboru (s príponou .BMP) pomocou metódy LoadFromFile
    • týmto príkazom sa nastaví nová veľkosť obrázka
    • ak už bitmapa mala nejaký obsah, tento sa stráca a nahradí sa prečítaným novým obrázkom
    • táto operácia je pre počítač dosť časovo náročná - treba s tým v našich programoch počítať
  4. obsah obrázkovej premennej môžeme opečiatkovať napr. do grafickej plochy pomocou príkazov Draw a StretchDraw - toto sú procedúry Canvasu, t.j. voláme ich podobne ako iné príkazy, napr. Rectangle, TextOut, ...
    • zapíšeme napr. Image1.Canvas.Draw(0, 0, Bitmapa);
    • alebo Image1.Canvas.StrethDraw(Image1.ClientRect, Obr);
  5. na záver by sme vždy mali uvoľniť obrázkovú premennú pomocou príkazu Free
    • takto sa uvoľní pamäť aplikácie, ktorá slúži na uchovávanie systémových zdrojov, napr. bitmáp - táto pamäť je obmedzená a aplikácia, ktorá by iba vytvárala nové obrázkové premenné a neuvoľňovala by ich, by mohla spadnúť na "prečerpaní systémových zdrojov" (napr. Windows resources)
    • premenné uvoľníme napr. takto Bitmapa.Free; alebo Obr.Free;

Vyskúšajme pečiatkovanie bitmapy aplikáciou, v ktorej okrem tlačidla bude vo formulári aj grafická plocha Image1. Grafickú plochu môžeme maximálne natiahnuť a nastaviť jej vlastnosť Align na hodnotu alClient, alebo ju môžeme ukotviť na všetky štyri steny (vlastnosť Anchors).

procedure TForm1.Button1Click(Sender: TObject);
var
  Obrazok: TBitmap;
begin
  Obrazok := TBitmap.Create;
  Obrazok.LoadFromFile('tiger.bmp');
  Image1.Canvas.Draw(30, 70, Obrazok);
  Obrazok.Free;
end;

Metóda Draw opečiatkuje bitmapu do Canvasu grafickej plochy na súradnice zadané dvoma prvými parametrami - tieto určujú ľavý horný roh pečiatkovaného obrázka. V našom príklade sa obrázok tigra umiestni tak, že jeho ľavý horný roh je na súradniciach (30, 70). Niekedy sa môžu hodiť hoci aj záporné súradnice - vtedy nejaká časť obrázka z plochy vypadne.

Ďalší variant metódy Draw položí obrázok do plochy aj so zmenou jeho veľkosti: metóda StretchDraw.

  • má dva parametre: obdĺžniková oblasť, do ktorej sa natlačí/roztiahne obrázok, ktorý je zadaný ako druhý parameter
  • obdĺžniková oblasť je buď
    • Image1.ClientRect, teda maximálne veľká oblasť na celú grafickú plochu
    • definovanie obdĺžnikovej oblasti pomocou Rect so štyrmi parametrami, teda Rect(x1, y1, x2, y2): označuje oblasť, ktorá má ľavý horný roh (x1, y1) a pravý dolný roh (x2, y2)
  • všimnite si, že oba nasledujúce príkazy robia presne to isté:
Image1.Canvas.Draw(X, Y, Obr);
Image1.Canvas.StretchDraw(Rect(X, Y, X + Obr.Width, Y + Obr.Height), Obr);

Nasledujúca ukážka ilustruje ešte jednu vlastnosť StretchDraw

procedure TForm1.Button1Click(Sender: TObject);
var
  Obrazok: TBitmap;
begin
  Obrazok := TBitmap.Create;
  Obrazok.LoadFromFile('tiger.bmp');
  Image1.Canvas.StretchDraw(Rect(Random(400), Random(300), Random(400), Random(300)), Obrazok);
  Obrazok.Free;
end;

Keď si teraz pozorne všimnete pečiatkované obrázky tigra, niektoré z nich sú preklopené vo vodorovnej rovine a niektoré sú preklopené zvisle. Totiž pre Rect(x1, y1, x2, y2), ak x1 > x2, tak oblasť označuje preklopenie vo vodorovnom smere a tiež, ak y1 > y2, tak oblasť označuje preklopenie vo zvislom smere. Keďže tu sa generujú súradnice oblasti náhodne, raz začas vzniká preklopenie.

P8.x.png P8.x.png P8.x.png

Obrázok natiahneme na celú grafickú plochu Image1:

procedure TForm1.Button1Click(Sender: TObject);
var
  Obrazok: TBitmap;
begin
  Obrazok := TBitmap.Create;
  Obrazok.LoadFromFile('tiger.bmp');
  Image1.Canvas.StretchDraw(Image1.ClientRect, Obrazok);
  Obrazok.Free;
end;

Volanie Image1.ClientRect je to isté, ako zápis Rect(0, 0, Image1.Width, Image1.Height). Vedeli by ste, čo sa stane, ak namiesto Client.Rect dáme:

Image1.Canvas.StretchDraw(Rect(Image1.Width, 0, 0, Image1.Height), Obrazok);

Pozrime sa na maximalizovaný obrázok v grafickej ploche. Predpokladáme, že ste pre Image1 nastavili vlastnosť Align na hodnotu alClient. Toto nastavenie spôsobí, že po spustení aplikácie môžeme ťahaním zväčšovať/zmenšovať rozmery formulára a tým sa bude naťahovať aj celá grafická plocha. Keď sa po zmene veľkosti formulára, zmení aj veľkosť grafickej plochy Image1, znovu zatlačme tlačidlo Button1, dostávame

P8.x.png P8.x.png

Vidíme, že opečiatkovaný obrázok sa naozaj naťahuje, ale grafická plocha sa vykresľuje len v pôvodných rozmeroch. Vysvetlime si, prečo je to tak a ako to môžeme opraviť:

  • komponent Image1 má svoj skrytý obrázok (Picture), do ktorého sa naozaj všetko kreslí, keď pracujeme s Image1.Canvas a zobrazí sa to len vtedy, keď je to hotové a je požiadavka od operačného systému na zobrazenie komponentu
  • toto je vymyslené preto, aby sme mohli hoci zminimalizovať aplikáciu, alebo ju prekryť a odokryť iným oknom a stále vidíme grafickú plochu nepoškodenú - program si predsa pamätá obsah plochy
  • ďalšou výhodou tohoto je aj to, že ani pri rýchlom kreslení a pečiatkovaní do plochy, táto plocha nebliká - vďaka tomu môžeme veľmi jenoducho robiť aj animáciu
  • problém s Picture je ten, že jeho veľkosť sa vytvorí vtedy, keď prvý krát začneme pracovať s Image1.Canvas a ďalšie zmeny veľkosti Image1 ho už nemenia
  • takže to, čo sme videli niekedy urezanú časť grafickej plochy, iba znamená, že Picture je stále menší ako Image1
  • opraviť to vieme veľmi jednoducho - pred vykreslením maximalizovaného obrázka, maximalizujeme aj rozmery Picture:
procedure TForm1.Button1Click(Sender: TObject);
var
  Obrazok: TBitmap;
begin
  Obrazok := TBitmap.Create;
  Obrazok.LoadFromFile('tiger.bmp');
  Image1.Picture.Bitmap.Width := Image1.Width;
  Image1.Picture.Bitmap.Height := Image1.Height;
  Image1.Canvas.StretchDraw(Image1.ClientRect, Obrazok);
  Obrazok.Free;
end;

Takéto naťahovanie Picture podľa momentálneho rozmeru grafickej plochy Image1 môžeme dať do jednej udalosti, ktorá vzniká vždy vtedy, keď zmeníme veľkosť formulára. Je to udalosť onResize a najlepšie ju bude nastaviť pre formulár. V inšpektore objektov prejdeme do udalostí pre Form1 a zvolíme onResize. Vytvorí sa prázdna procedúra FormResize a do nej môžeme dopísať:

procedure TForm1.FormResize(Sender: TObject);
begin
  Image1.Picture.Bitmap.Width := Image1.Width;
  Image1.Picture.Bitmap.Height := Image1.Height;
  Image1.Canvas.Brush.Color := clWhite;
  Image1.Canvas.FillRect(Image1.ClientRect);
end;

Toto spôsobí to, že zmena veľkosti okna aplikácie automaticky nastaví nové rozmery grafickej ploche a zmaže ju. Hoci toto sa nám nemusí vždy hodiť ...


Vykachličkovanie grafickej plochy

Ukážeme typickú prácu s bitmapou - opečiatkujeme obrázok viackrát vedľa seba a pod seba tak, aby presne vyplnil celé pozadie grafickej plochy (môžete použiť napr. bitmapy v súbore ...). Tomuto sa niekedy hovorí vykachličkovanie plochy nejakým obrázkom:

procedure Kachlickuj(Image: TImage; Bmp: TBitmap);
var
  X, Y: Integer;
begin
  Y := 0;
  while Y < Image.Height do
  begin
    X := 0;
    while X < Image.Width do
    begin
      Image.Canvas.Draw(X, Y, Bmp);
      X := X + Bmp.Width;
    end;
    Y := Y + Bmp.Height;
  end;
end;
 
procedure TForm1.Button3Click(Sender: TObject);
var
  Obrazok: TBitmap;
begin
  Obrazok := TBitmap.Create;
  Obrazok.LoadFromFile('pozadie.bmp');
  Kachlickuj(Image1, Obrazok);
  Obrazok.Free;
end;

Všimnite si, že pomocou Bmp.Width a Bmp.Height vieme zistiť aktuálnu veľkosť bitmapy. O tieto dve hodnoty potom zväčšujeme X-ovú aj Y-ovú súradnicu. Procedúra Kachlickuj je globálna a preto ju môžeme používať na viacerých miestach. Táto procedúra má prvý parameter Image grafickú plochu, ktorú bude treba vykachličkovať. Druhým parametrom je samotná bitmapa. Túto kachličkovaciu procedúru by sme mohli zapísať aj pomocou for-cyklov (hoci je tu priveľa násobenia a delenia), napr. takto:

procedure Kachlickuj(Image: TImage; Bmp: TBitmap);
var
  X, Y: Integer;
begin
  for Y := 0 to Image.Height div Bmp.Height do
    for X := 0 to Image.Width div Bmp.Width do
      Image.Canvas.Draw(X * Bmp.Width, Y * Bmp.Height, Bmp);
end;

Predpokladajme, že máme k dispozícii viac rôznych bitmáp (aj rôznych rozmerov), ktoré môžeme použiť na vykachličkovanie grafickej plochy. Vytvorme ďalšiu verziu tejto procedúry: plochu vydláždime dvojicou bitmáp - tieto sa budú striedať tak ako na šachovnici.

Prvá verzia bude dobre fungovať, len ak majú obe bitmapy rovnakú veľkosť:

procedure Kachlickuj(Image: TImage; Bmp1, Bmp2: TBitmap);
var
  X, Y: Integer;
  B1, B2: Boolean;
begin
  Y := 0;
  B1 := True;
  while Y < Image.Height do
  begin
    X := 0;
    B2 := B1;
    while X < Image.Width do
    begin
      if B2 then
        Image.Canvas.Draw(X, Y, Bmp1)
      else
        Image.Canvas.Draw(X, Y, Bmp2);
      X := X + Bmp1.Width;
      B2 := not B2;
    end;
    Y := Y + Bmp1.Height;
    B1 := not B1;
  end;
end;
 
procedure TForm1.Button4Click(Sender: TObject);
var
  Obr1, Obr2: TBitmap;
begin
  Obr1 := TBitmap.Create;
  Obr1.LoadFromFile(Format('pozadie%d.bmp', [Random(16) + 1]));
  Obr2 := TBitmap.Create;
  Obr2.LoadFromFile(Format('pozadie%d.bmp', [Random(16) + 1]));
  Kachlickuj(Image1, Obr1, Obr2);
  Obr1.Free;
  Obr2.Free;
end;

Po otestovaní vidíme, že ak druhá bitmapa je výrazne menšia, tak vo vykachličkovanej ploche sú "diery". Procedúru Kachlickuj opravíme napr. takto

procedure Kachlickuj(Image: TImage; Bmp1, Bmp2: TGraphic);
var
  X, Y: Integer;
  B1, B2: Boolean;
begin
  Y := 0;
  B1 := True;
  while Y < Image.Height do
  begin
    X := 0;
    B2 := B1;
    while X < Image.Width do
    begin
      if B2 then
        Image.Canvas.Draw(X, Y, Bmp1)
      else
        Image.Canvas.StretchDraw(Rect(X, Y, X + Bmp1.Width, Y + Bmp1.Height), Bmp2);
      X := X + Bmp1.Width;
      B2 := not B2;
    end;
    Y := Y + Bmp1.Height;
    B1 := not B1;
  end;
end;

Vďaka tomu, že sme v deklarácii tejto procedúry parametrom Bmp1 a Bmp2 určili typ TGraphic, ako hodnoty sem môžeme poslať nielen objekty typu TBitmap, ale aj typu TJpegImage, alebo TPortableNetworkGraphic. Rozumeiť tomuto budeme až neskôr.

Všimnite si, že procedúru Kachlickuj máme teraz definovanú dvakrát: raz s dvoma parametrami a raz s troma:

  • procedure Kachlickuj(Image1: TImage; Bmp: TBitmap);
  • procedure Kachlickuj(Image1: TImage; Bmp1, Bmp2: TGraphic);

Pripomíname, že FreePascal umožňuje definovať procedúru (aj funkciu) viackrát, len pritom kompilátor musí pri volaní vedieť rozpoznať, ktorú z definícií máme na mysli. Buď sa tieto definície líšia počtom parametrov, alebo sa musia líšiť aspoň typmi parametrov. Dokonca môžeme z jednej z nich volať druhú a kompilátor to správne rozpozná:

procedure Kachlickuj(Image: TImage; Bmp: TGraphic);
begin
  Kachlickuj(Image, Bmp, Bmp);
end;

Napr. bude fungovať

procedure TForm1.Button3Click(Sender: TObject);
var
  Obrazok: TJpegImage;
begin
  Obrazok := TJpegImage.Create;
  Obrazok.LoadFromFile('obrazok.jpg');
  Kachlickuj(Image1, Obrazok);
  Obrazok.Free;
end;

alebo

procedure TForm1.Button3Click(Sender: TObject);
var
  Obrazok: TPortableNetworkGraphic;
begin
  Obrazok := TPortableNetworkGraphic.Create;
  Obrazok.LoadFromFile('obrazok.png');
  Kachlickuj(Image1, Obrazok);
  Obrazok.Free;
end;


Priesvitnosť bitmáp

Teraz budeme pracovať s 8 pripravenými súbormi s obrázkami - 'bmp1.bmp', 'bmp2.bmp', ... (sú v súbore ...). Po zatlačení tlačidla sa na náhodnom mieste plochy položí náhodne vybraný jeden z týchto obrázkov:

procedure TForm1.Button5Click(Sender: TObject);
var
  Bmp: TBitmap;
begin
  Bmp := TBitmap.Create;
  Bmp.LoadFromFile(Format('bmp%d.bmp', [Random(8) + 1]));
  Image1.Canvas.Draw(
      Random(Image1.Width - Bmp.Width),
      Random(Image1.Height - Bmp.Height),
      Bmp);
  Bmp.Free;
end;

Náhodnú pozíciu obrázka sme zvolili tak, aby obrázok nevypadol z plochy. Ďalej si môžete všimnúť, že niektoré časti obrázkov sú biele, ale bolo by krajšie, keby boli priesvitné. Bitmapám môžeme jednu farbu určiť ako priesvitnú a potom kladenie obrázka do plochy pomocou Draw alebo StretchDraw ponechá priesvitné časti nezmenené. Bitmape musíme nastaviť stavovú premennú Transparent na True:

procedure TForm1.Button5Click(Sender: TObject);
var
  Bmp: TBitmap;
begin
  Bmp := TBitmap.Create;
  Bmp.LoadFromFile(Format('bmp%d.bmp', [Random(8) + 1]));
  Bmp.Transparent := True;
  Image1.Canvas.Draw(
      Random(Image1.Width - Bmp.Width),
      Random(Image1.Height - Bmp.Height),
      Bmp);
  Bmp.Free;
end;

Týmto sme rozhodli, že v bitmape budú všetky farebné body (pixel), ktoré sú rovnaké ako bod v ľavom dolnom rohu, priesvitné. Ak potrebujeme vyhlásiť za priesvitnú inú farbu, použijeme stavovú premennú TransparentColor:

procedure TForm1.Button5Click(Sender: TObject);
var
  Bmp: TBitmap;
begin
  Bmp := TBitmap.Create;
  Bmp.LoadFromFile(Format('bmp%d.bmp', [Random(8) + 1]));
  Bmp.TransparentColor := clFuchsia;
  Bmp.Transparent := True;
  Image1.Canvas.Draw(
      Random(Image1.Width - bmp.Width),
      Random(Image1.Height - bmp.Height),
      Bmp);
  Bmp.Free;
end;


Vytvárame vlastné bitmapy

Pracovať môžeme nielen s bitmapami, ktoré sme prečítali zo súboru, ale bitmapu si môžeme nakresliť aj sami. Bitmapa má rovnaký Canvas ako grafická plocha Image1 a teda do nej môžeme kresliť úplne rovnakým spôsobom. Ak nebudeme do práve vytvorenej bitmapy (TBitmap.Create) čítať súbor pomocou LoadFromFile, ale chystáme sa kresliť do jej Canvasu, musíme jej najprv určiť veľkosť Bmp.Width a Bmp.Height a farebný formát Bmp.PixelFormat:

procedure TForm1.Button6Click(Sender: TObject);
var
  Bmp: TBitmap;
begin
  Bmp := TBitmap.Create;
  Bmp.Width := 100;
  Bmp.Height := 100;
  Bmp.PixelFormat := pf24bit;
  Image1.Canvas.Draw(
      Random(Image1.Width - Bmp.Width),
      Random(Image1.Height - Bmp.Height),
      Bmp);
  Bmp.Free;
end;

Takto sme vytvorili bitmapu - čierny štvorec veľkosti 100x100. Nakreslime do tejto novej bitmapy červený kruh a okraje jej spravme priesvitné:

procedure TForm1.Button6Click(Sender: TObject);
var
  Bmp: TBitmap;
begin
  Bmp := TBitmap.Create;
  Bmp.Width := 100;
  Bmp.Height := 100;
  Bmp.PixelFormat := pf24bit;
  Bmp.Canvas.Brush.Color := clWhite;
  Bmp.Canvas.FillRect(0, 0, 100, 100);
  Bmp.Canvas.Brush.Color := clRed;
  Bmp.Canvas.Ellipse(0, 0, 100, 100);
  Bmp.Transparent := True;
  Image1.Canvas.Draw(
      Random(Image1.Width - Bmp.Width),
      Random(Image1.Height - Bmp.Height),
      Bmp);
  Bmp.Free;
end;

Do takejto bitmapy môžeme opečiatkovať aj inú bitmapu, napr.

procedure TForm1.Button6Click(Sender: TObject);
var
  Bmp, Bmp1: TBitmap;
begin
  Bmp := TBitmap.Create;
  Bmp.Width := 100;
  Bmp.Height := 100;
  Bmp.PixelFormat := pf24bit;
  Bmp.Canvas.Brush.Color := clWhite;
  Bmp.Canvas.FillRect(0, 0, 100, 100);
  Bmp.Canvas.Brush.Color := clYellow;
  Bmp.Canvas.Ellipse(0, 0, 100, 100);
  Bmp.Transparent := True;
 
    Bmp1 := TBitmap.Create;
    Bmp1.LoadFromFile(Format('bmp%d.bmp', [Random(8) + 1]));
    Bmp1.Transparent := True;
    Bmp.Canvas.Draw(20, 20, Bmp1);
    Bmp1.Free;
 
  Image1.Canvas.Draw(
      Random(Image1.Width - Bmp.Width),
      Random(Image1.Height - Bmp.Height),
      Bmp);
  Bmp.Free;
end;

Niekedy potrebujeme z väčšej bitmapy vystrihnúť menšiu obdĺžnikovú časť a ďalej pracovať len s týmto kúskom. Môžeme to urobiť napr. takto

procedure TForm1.Button7Click(Sender: TObject);
var
  Bmp, Bmp1: TBitmap;
begin
  Bmp := TBitmap.Create;
  Bmp.Width := 130;
  Bmp.Height := 152;
  Bmp.PixelFormat := pf24bit;
    Bmp1 := TBitmap.Create;
    Bmp1.LoadFromFile('tiger.bmp');
    Bmp.Canvas.Draw(-64, -51, Bmp1);
    Bmp1.Free;
  Image1.Canvas.Draw(
      Random(Image1.Width - Bmp.Width),
      Random(Image1.Height - Bmp.Height),
      Bmp);
  Bmp.Free;
end;

Je to rovnaký princíp pečiatkovania jednej bitmapy do druhej ako v predchádzajúcom príklade len obrázok, ktorý pečiatkujeme je výrazne väčší ako bitmapa, do ktorej pečiatkujeme. Všimnite si, že do príkazu Draw sme zadali záporné súradnice ľavého horného rohu tigra. Niekedy sa namiesto Draw môže hodiť aj príkaz CopyRect - pozrite si ho v helpe.

Častou začiatočníckou chybou býva vzájomné priraďovanie bitmáp, napr.

procedure TForm1.Button8Click(Sender: TObject);
var
  Bmp, Bmp1: TBitmap;
begin
  Bmp := TBitmap.Create;
  Bmp.LoadFromFile('tiger.bmp');
 
    Bmp1 := TBitmap.Create;
    Bmp1 := Bmp;                   // toto je chyba !
    Image1.Canvas.Draw(0, 0, Bmp1);
    Bmp1.Free;
 
  Bmp.Free;
end;

Neskôr, keď sa naučíme, ako fungujú objekty a smerníky, pochopíme, akú veľkú chybu sme spravili. Zapamätajte si, že ak chcete do jednej bitmapy priradiť obsah inej, buď to urobíme opečiatkovaním, alebo použijeme špeciálnu metódu Assign:

procedure TForm1.Button8Click(Sender: TObject);
var
  Bmp, Bmp1: TBitmap;
begin
  Bmp := TBitmap.Create;
  Bmp.LoadFromFile('tiger.bmp');
 
    Bmp1 := TBitmap.Create;
    Bmp1.Assign(Bmp);           // takto je to správne
    Image1.Canvas.Draw(0, 0, Bmp1);
    Bmp1.Free;
 
  Bmp.Free;
end;

Túto ideu môžeme využiť aj na zapamätanie si celej grafickej plochy, napr.

procedure TForm1.Button8Click(Sender: TObject);
var
  Bmp: TBitmap;
begin
  Bmp := TBitmap.Create;
  Bmp.Assign(Image1.Picture);
  Image1.Canvas.Draw(Random(400), Random(400), Bmp);
  Bmp.Free;
end;

Najprv vytvorí prázdnu bitmapu (TBitmap.Create), potom do nej prekopíruje obsah celej grafickej plochy (bmp.Assign(Image1.Picture)) a túto bitmapu ďalej niekam opečiatkuje.



Bitmapa ako globálna premenná


Niekedy sa môže hodiť, keď bitmapu (alebo aj viac bitmáp, alebo pole bitmáp) vytvoríme už pri štarte aplikácie (vo FormCreate) a používať ju potom môžeme hocikedy počas behu. Samotné čítanie bitmáp zo súboru sa urobí len raz a potom už viac nezdržuje.

Ukážka jednoduchého programu s bitmapou v globálnej premennej:

var
  GlobalBmp: TBitmap;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  GlobalBmp := TBitmap.Create;
  GlobalBmp.LoadFromFile('tiger.bmp');
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var
  X, Y, Sirka, Vyska: Integer;
begin
  Sirka := 20+Random(200);
  Vyska := 20+Random(200);
  X := Random(Image1.Width - Sirka);
  Y := Random(Image1.Height - Vyska);
  Image1.Canvas.StretchDraw(Rect(X, Y, X + Sirka, Y + Vyska), GlobalBmp);
end;


Zhrnutie typu TBitmap

TBitmap je preddefinovaný typ, ktorý slúži na manipuláciu s obrázkami - môžeme si ho predstaviť ako obsah súboru s príponou .BMP (dá sa s nimi pracovať napr. v programe Paint/Skicár). Tento typ má niekoľko užitočných nastavení (vlastnosti - property) a príkazov (procedúr).

Niektoré nastavenia:

  • Width, Height - momentálna šírka a výška obrázka (môžeme ju zmeniť priradením nových hodnôt do týchto premenných)
  • PixelFormat - farebný vnútorný formát bitmapy, t.j. koľko bitov zaberá každý jeden pixel obrázka - my budeme používať konštantu pf24bit
  • Canvas - grafická plocha obrázka
  • Transparent - či má nejaké priesvitné časti (inak je to nepriesvitný obdĺžnik)
  • TransparentColor - ktorá farba v obrázku je považovaná za priesvitnú

Niektoré procedúry:

  • konštruktor Create - vytvorí zatiaľ prázdny obrázok
  • Free - uvoľní bitmapu z pamäti Windows
  • LoadFromFile - načíta obrázok zo súboru vo formáte .BMP (zmení šírku, výšku aj farebný formát)
  • SaveToFile - uloží obrázok do súboru vo formáte .BMP
  • Assign - urobí kópiu obrázka z inej bitmapy, resp. grafickej plochy

Môžeme pracovať s Canvasom bitmapy, t.j. s dvojrozmerným poľom farebných Pixelov (bodov) - do bitmapy môžeme kresliť, môžeme pracovať s jednotlivými pixelmi - úplne rovnako ako v obyčajnej grafickej ploche (TImage), napr.

  • Canvas.FillRect
  • Canvas.Pixels[riadok, stĺpec] - uvidíme neskôr
  • Canvas.Rectangle, Ellipse, TextOut, ...
  • Canvas.MoveTo, LineTo, Polygon, Polyline, FloodFill, ...

a teda môžeme týmto metódam nastaviť pero, štetec, font, ..., napr. Canvas.Pen.Color := ...

  • Canvas.Draw - opečiatkuje inú bitmapu
  • Canvas.StretchDraw - opečiatkuje inú bitmapu, pričom ju môže zväčšiť/zmenšiť
  • Canvas.CopyRect - opečiatkuje výrez iného Canvasu (bitmapy alebo grafickej plochy)
  • Canvas.BrushCopy - podobný CopyRect - jednu z farieb pri kopírovaní vie nahradiť inou


späť | ďalej