22.10.2002
|
Lexikálna analýza - skener (scanner)
- algoritmus, ktorý slúži na predspracovanie
textového vstupu - rozloží text na
logické časti, tzv. lexikálne jednotky
- lexémy
- najlepšie sa využije pri spracovaní zdrojových
textov programov, najčastejšie v pascale:
- lexikálnymi jednotkami by teraz mohli
byť, napr. identifikátor (vrátane
rezervované slová), číselná
konštanta, konštanta znakový reťazec,
špeciálne symboly, napr. zátvorky,
bodkočiarka, čiarka, relačné a aritmetické
operátory a pod.
- pomocou takéhoto algoritmu môžeme
veľmi jednoducho spracovať identifikátory
premenných alebo procedúr,
rôznym spôsobom vypisovať pascalovský
program, farebne vyznačovať niektoré
časti a pod.
- princíp práce je tento: procedúra
skener, vždy keď je zavolaná,
zanalyzuje ďalšiu časť textu a v globálnych
premenných vráti
nasledujúcu lexému - typ lexémy
+ doplnková informácia lexémy
(hodnota pre číslo alebo reťazec, identifikátor,
...)
- po spracovaní každej lexémy skener
v textovom súbore skončí nastavený
na znaku tesne za lexémou - je jasné,
že vždy, keď je zavolaný, už musí
počítať s tým, že je možno nastavený
na prvom znaku nejakej lexémy (alebo
napr. na medzere pred ňou)
- niektoré reťazce sú pri spracovávaní
textu nedôležité a preto ich môže
skener filtrovať, napr. medzery a konce riadkov
a tiež všetky typy komentárov {...},
//..., (*...*) - často slúžia len ako
oddeľovače lexém
- zhrnieme pravidlá pre vytvorenie skenera:
- určíme, aké rôzne typy lexém
budeme zo súboru zisťovať - zadefinujeme globálnu
premennú vymenovaného typu:
lex: (koniec,ident,cislo,retazec,symbol,...);
- vytvoríme pomocnú procedúru znak, ktorá
číta súbor a do globálnej znakovej
premennej
z priradí načítaný znak:
- #0, keď je koniec súboru (mohol by sa
použiť aj iný znak, napr. #26)
- #1, na konci riadka, pričom spracuje tento koniec
riadka (aj tu by mohol byť iný znak, napr. #13)
- inak prečíta znak zo súboru
- z:char aj t:TextFile sú globálne premenné:
procedúra znak:
|
var
z:char;
t:TextFile;
procedure znak; // procedúra na čítanie zo súboru
begin
if eof(t) then z:=#0
else if eoln(t) then begin z:=#1; readln(t); end
else read(t,z);
end;
|
- vytvoríme procedúru skener, ktorá
spracováva znaky a stará sa o tie postupnosti
znakov, ktoré tvoria nejaké lexémy (každé volanie zistí
nasledujúcu lexému)
procedúra skener:
|
procedure skener;
var
ok:boolean;
begin
ok:=false;
repeat
case z of
#0: // už je koniec súboru
begin
lex:=koniec; ok:=true;
end;
'{': // začína komentár
begin
// nájdeme k nemu druhú zátvorku alebo prídeme na koniec súboru
end;
'''': // začína reťazec
'A'..'Z','a'..'z': // začína identifikátor
...
else ...
end; // case
until ok;
end;
|
- ok - znamená, že sme našli lexému -
môžeme ukončiť toto volanie => v lex je typ
lexémy v iných premenných je ďalšia
informácia (napr. reťazec identifikátora
premennej)
v tele samotného programu sa nachádza
otvorenie súboru, inicializácia a hlavný
cyklus:
|
znak; skener;
while lex<>koniec do begin
// spracovanie tých lexém, ktoré potrebujeme
skener;
end;
|
Príklad
Máme daný pascalovský unit "unit1.pas"
- zistíme, či ku každému begin
prislúcha jeden end - nakoľko v programoch
môžu byť aj iné konštrukcie, ktorých
súčasťou je end, musíme počítať
aj s nimi: record, case a class
- taktiež na konci unitu je jeden end navyše.
Použijeme takýto algoritmus: pri každom begin,
record ... pripočítame 1 a pri end
odpočítame 1 - ak momentálny súčet
bude menší ako 0, znamená, že práve
bolo viac end ako "otvorených" begin - po skončení
analýzy musí byť súčet -1, lebo
na záver je jedno end s bodkou.
- skener nepotrebuje vrátiť všetky
typy lexém - stačí nám len
ident - identifikátor
- budeme filtrovať komentáre {...} a //...
a tiež konštanty znakové reťazce, lebo by
mohli obsahovať napr. slovo end a takéto
by sme nemali započítať
zjednodušený skener:
|
var
lex:(koniec,ident);
hodn:string;
procedure skener;
var
ok:boolean;
begin
ok:=false;
repeat
case z of
#0:
begin
lex:=koniec; ok:=true;
end;
'{':
begin
repeat znak until z in ['}',#0];
znak;
end;
'/':
begin
znak;
if z='/' then begin
repeat znak until z in [#1,#0];
end;
end;
'''':
begin
znak;
while not (z in ['''',#0,#1]) do znak;
if z='''' then znak;
end;
'A'..'Z','a'..'z','_':
begin
hodn:=''; lex:=ident; ok:=true;
while z in ['A'..'Z','a'..'z','0'..'9','_'] do begin
hodn:=hodn+z; znak;
end;
end;
else
znak;
end;
until ok;
end;
|
- program, ktorý spracováva text,
dostáva od skenera len 2 typy lexém:
koniec a ident, preto v tele cyklu
je jasné, že lexéma je ident
spracovanie textu:
|
procedure TForm1.Button1Click(Sender: TObject);
var
poc:integer;
begin
// Memo1.Lines.LoadFromFile('unit1.pas');
// Memo1.Lines.Add('===================================================');
AssignFile(t,'unit1.pas'); Reset(t);
znak; skener; poc:=0;
while lex<>koniec do begin
hodn:=LowerCase(hodn);
if (hodn='begin') or (hodn='case') or
(hodn='record') or (hodn='class') then begin
if poc<0 then Memo1.Lines.Add('zle je');
inc(poc);
// Memo1.Lines.Add('... '+hodn+' ... '+IntToStr(poc));
end
else if hodn='end' then begin
dec(poc);
// Memo1.Lines.Add('... '+hodn+' ... '+IntToStr(poc));
end;
skener;
end;
CloseFile(t);
if poc<>-1 then
Memo1.Lines.Add('zle je pocet = '+IntToStr(poc))
else
Memo1.Lines.Add('ok');
end;
|
- pouvažujte nad tým, čo by bolo treba
zmeniť, aby sme vedeli hlásiť chybu pri ľubovoľnom
texte za záverečným end, za
ktorým je bodka
Príklad
Textový súbor unit2.pas obsahuje
pascalovský program - treba v ňom nahradiť všetky
výskyty identifikátora r1 reťazcom
r2 - samozrejme, že nahrádzať sa nebude
ani v reťazcoch ani v komentároch.
- zadefinujeme pomocnú procedúru pis,
ktorá vypíše znak z do výstupného
súboru t1
procedúra pis:
|
var
t1:TextFile;
procedure pis;
begin
if z<>#0 then
if z=#1 then writeln(t1)
else write(t1,z);
end;
|
- všimnite si procedúru skener -
komentáre aj reťazce sa len kopírujú
zo vstupu na výstup (znak => pis),
podobne sa kopírujú aj všetky symboly
(aj čísla), ktoré nie sú súčasťou
identifikátorov - skener rozpozná
len identifikátory a samozrejme aj koniec
vstupu
procedúra skener:
|
var
lex:(koniec,ident);
hodn:string;
procedure skener;
var
ok:boolean;
begin
ok:=false;
repeat
case z of
#0:
begin
lex:=koniec; ok:=true;
end;
'{':
begin
repeat pis; znak; until z in ['}',#0];
pis; znak;
end;
'/':
begin
pis; znak;
if z='/' then begin
repeat pis; znak; until z in [#1,#0];
end;
end;
'''':
begin
repeat pis; znak; until z in ['''',#0,#1];
pis; znak;
end;
'A'..'Z','a'..'z','_':
begin
hodn:=''; lex:=ident; ok:=true;
while z in ['A'..'Z','a'..'z','0'..'9','_'] do begin
hodn:=hodn+z; znak;
end;
end;
else
pis; znak;
end;
until ok;
end;
|
- samotný program, načíta z dvoch
komponentov editovací riadok dva reťazce
- identifikátor, ktorý nahrádzame
a reťazec, ktorým nahrádzame (Edit1
a Edit2); v textovej ploche Memo1 sa vypíše
obsah súboru pred aj po nahrádzaní
program nahradí identifikátor
iným reťazcom:
|
procedure TForm1.FormCreate(Sender: TObject);
begin
Memo1.Lines.LoadFromFile('unit2.pas');
end;
procedure TForm1.Button1Click(Sender: TObject);
var
r1,r2:string; // všetky výskyty r1 zmení na r2
begin
r1:=LowerCase(Trim(Edit1.Text));
r2:=Trim(Edit2.Text);
if (r1='') or (r2='') then begin
ShowMessage('Najprv zadaj oba reťazce');
exit;
end;
AssignFile(t,'unit2.pas'); Reset(t);
AssignFile(t1,'unit2a.pas'); Rewrite(t1);
znak; skener;
while lex<>koniec do begin
if LowerCase(hodn)=r1 then write(t1,r2)
else write(t1,hodn);
skener;
end;
CloseFile(t); CloseFile(t1);
DeleteFile('unit2.pas');
RenameFile('unit2a.pas','unit2.pas');
Memo1.Lines.LoadFromFile('unit2.pas');
end;
|
- použili sme dve systémové procedúry
DeleteFile a RenameFile - aby sme
mohli prepísať pôvodný obsah
súboru unit2.pas novým opraveným
obsahom
Príklad
Program bude čítať
textový súbor unit3.pas, v ktorom sa
nachádza nejaký pascalovský program
a vytvorí súbor unit3a.pas, ktorý
bude maximálne zhustený na nejakú
zadanú
šírku riadka (napr. 80 znakov). V každom riadku bude maximálny
počet znakov, ktoré sa sem zmestia a v súbore
bude minimálny počet medzier tak, aby program
ešte ostal funkčný. Komentáre z programu
vynecháme.
- aby bol program rovnako funkčný ako originál,
musíme komentáre, ktoré začínajú
znakom $, tiež preniesť do výstupu
- bude to pre nás špeciálny symbol,
napr. {$R
*.dfm}
- niektoré pascalovské viacznakové
lexémy by mohli robiť problémy,
keby sa rozdelili do dvoch riadkov, napr. znak
priradenia :=,
relačné operátory <=,
dve bodky pre interval ..
a pod. - musíme z nich vyrobiť jedinú
lexému typu symbol
- všimnite si aj spracovanie apostrofu v znakových
reťazcoch
najprv dosť vylepšený skener:
|
var
lex: (koniec,ident,cislo,retazec,symbol);
hodn: string;
procedure skener;
var
ok:boolean;
begin
repeat
ok:=true;
case z of
#0: lex:=koniec;
' ',#1,#9: // #9 znamená znak tabulátor
begin
ok:=false; znak;
end;
'{':
begin
hodn:=z; znak; lex:=symbol;
ok:=z='$'; // napr. pre {$R *.dfm}
while not (z in ['}',#0]) do begin
hodn:=hodn+z; znak;
end;
hodn:=hodn+z; znak;
end;
'''':
begin
znak; hodn:=''; lex:=retazec;
repeat
while not (z in ['''',#0,#1]) do begin
hodn:=hodn+z; znak;
end;
if z='''' then znak;
if z='''' then hodn:=hodn+'''';
until z<>'''';
end;
'A'..'Z','a'..'z','_':
begin
hodn:=''; lex:=ident;
while z in ['A'..'Z','a'..'z','0'..'9','_'] do begin
hodn:=hodn+z; znak;
end;
end;
'#','0'..'9':
begin
hodn:=z; znak; lex:=cislo;
while z in ['0'..'9'] do begin
hodn:=hodn+z; znak;
end;
end;
'/':
begin
znak;
if z='/' then begin // komentár
repeat znak until z in [#1,#0];
ok:=false;
end
else begin
hodn:='/'; lex:=symbol;
end;
end;
':':
begin
hodn:=z; znak; lex:=symbol;
if z='=' then
begin hodn:=hodn+z; znak; end;
end;
'.':
begin
hodn:=z; znak; lex:=symbol;
if z='.' then
begin hodn:=hodn+z; znak; end;
end;
'<','>':
begin
hodn:=z; znak; lex:=symbol;
if (z='=') or (hodn='<') and (z='>') then
begin hodn:=hodn+z; znak; end;
end;
else
lex:=symbol; hodn:=z; znak;
end;
until ok;
end;
|
- samotný algoritmus
zhusťovania textu - medzi niektoré
dvojice za sebou idúcich lexém
musíme pridávať medzeru
vytváranie zhusteného
pascalovského programu:
|
procedure TForm1.Button1Click(Sender: TObject);
const
maxsir = 80;
var
t1:TextFile;
i,sir:integer; // sir - doterajšia šírka riadka
b0,b1:boolean; // či vkladať medzery medzi lexémy
s:string;
begin
AssignFile(t,'unit3.pas'); Reset(t);
AssignFile(t1,'unit3a.pas'); Rewrite(t1);
sir:=0; b0:=false;
znak; skener;
while lex<>koniec do begin
b1:=lex in [cislo,ident]; s:=hodn;
if lex=retazec then begin
s:='';
for i:=1 to length(s) do
if hodn[i]='''' then s:=s+''''''
else s:=s+hodn[i];
s:=''''+hodn+'''';
end;
if b0 and b1 then s:=' '+s;
b0:=b1;
if (sir>0) and (sir+length(s)>maxsir) then begin
writeln(t1); sir:=0;
end;
if (sir=0) and (s[1]=' ') then delete(s,1,1);
write(t1,s); inc(sir,length(s));
skener;
end;
CloseFile(t); CloseFile(t1);
end;
|
NDÚ:
- dorobte do skenera aj (*...*) komentáre
- dorobte skener aj pre reálne čísla,
šestnástkové konštanty '$' a znakové
konštanty s '#'
- zameňte všetky identifikátory premenných
(nie rezervovaných slov pascalu) identifikátormi,
ktoré obsahujú len znaky O, 0 a Q (písmeno
O, nula a Q), napr. OOOO, OOO0, OO0O, OO00, O0OO a pod.
- aby sa program stal maximálne nečitateľný,
ale aby ostal funkčný
Bitmapy
v tejto časti môžete použiť súbory z
predchádzajúcich prednášok obrazky.zip ale
aj nové súbory s obrázkami obrazky2.zip
- zatiaľ sme poznali jeden spôsob, ako vložiť
obrázok (súbor .BMP) do grafickej
plochy: pomocou LoadFromFile
prečítanie obrázka
zo súboru do grafickej plochy
Image:
|
procedure TForm1.Button1Click(Sender: TObject);
begin
Image1.Picture.LoadFromFile('tiger.bmp');
end;
|
- pomocou tohto spôsobu sa niekedy zmení
grafická plocha a tiež bude nepoužiteľný,
keď potrebujeme bitmapu umiestniť nie do ľavého
horného rohu, ale na nejaké konkrétne
súradnice
- budeme pracovať s novým objektom - bitmapa
- môžeme si všimnúť istú podobnosť
s objektom korytnačka:
- deklarovaním premennej bmp:TBitmap
objekt (inštancia) ešte nevzniká - nový
objekt treba ešte vytvoriť
- bmp:=TBitmap.Create - vytvorí
sa nová bitmapa (obrázok), ktorý
je zatiaľ prázdny - má rozmery
0x0
- do existujúceho objektu bitmapa
môžeme prečítať obrázok
zo súboru pomocou LoadFromFile
(ak už bitmapa mala nejaký obsah, tento
sa stráca a nahradí sa prečítaným
obrázkom - zmení sa jej veľkosť)
- teraz môžeme "opečiatkovať"
náš nový objekt bitmapa do grafickej
plochy Image1 pomocou metódy Draw
- a mali by sme si na záver zvyknúť
na jedno dôležité pravidlo: vždy,
keď skončíme pracovať s objektom bitmapa,
tak ju musíme uvoľniť
zo systému pomocou metódy Free
- bitmapa je totiž taký špeciálny
objekt, ktorý odčerpáva zdroje
z Windows a ak takýchto zdrojov Windows
minieme priveľa, systém sa môže zrútiť...
bitmapa do pozadia grafickej plochy:
|
procedure TForm1.Button1Click(Sender: TObject);
var
bmp:TBitmap;
begin
bmp:=TBitmap.Create;
bmp.LoadFromFile('tiger.bmp');
Image1.Canvas.Draw(0,0,bmp);
bmp.Free;
end;
|
- metóda Draw opečiatkuje bitmapu
do Canvasu na súradnice zadané dvoma
prvými parametrami - tieto určujú
ľavý horný roh kladeného obrázka
- pritom môže nejaká časť obrázka
z plochy vypadnúť; niekedy sa môžu
hodiť aj záporné súradnice
obrázka ...
- existuje ešte špeciálny prípad
metódy Draw - položenie obrázka do
plochy so zmenou veľkosti StretchDraw - obrázok
môžeme ľubovoľne zmenšiť alebo zväčšiť,
môžeme ho napríklad natiahnuť na celú
grafickú plochu
- StretchDraw má dva parametre:
prvým je obdĺžnik (rectangle), do ktorého
treba umiestniť obrázok a druhým je
samotná bitmapa; na definovanie obdĺžnika
často použijeme konštrukciu Rect(x1,y1,x2,y2),
v ktorej zadáme súradnice ľavého
horného a pravého dolného rohu
oblasti, napr.
natiahnutie obrázka na celú
plochu:
|
procedure TForm1.Button2Click(Sender: TObject);
var
bmp:TBitmap;
begin
bmp:=TBitmap.Create;
bmp.LoadFromFile('tiger.bmp');
Image1.Canvas.StretchDraw(Rect(0,0,Image1.Width,Image1.Height),bmp);
bmp.Free;
end;
|
- 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
okachličkovanie plochy:
|
procedure TForm1.Button3Click(Sender: TObject);
var
bmp:TBitmap;
x,y,w,h:integer;
begin
bmp:=TBitmap.Create;
bmp.LoadFromFile('pozadie.bmp');
w:=Image1.Width; h:=Image1.Height; y:=0;
while y<h do begin
x:=0;
while x<w do begin
Image1.Canvas.Draw(x,y,bmp);
inc(x,bmp.Width);
end;
inc(y,bmp.Height);
end;
bmp.Free;
end;
|
NDÚ
- vycentrujete bitmapu do grafickej plochy
- vykachličkujte plochu dvojnásobne zväčšeným
(zmenšeným) obrázkom
- natiahnite obrázok tak, aby pokryl celú
plochu, ale pritom, aby ostal pomer strán
obrázka zachovaný - pravdepodobne
obrázok v jednom rozmere môže vypadnúť
z plochy
- predpokladajme, že máme pripravených
8 súborov .BMP - bitmáp malých
obrázkov bmp1.bmp, bmp2.bmp, ...; po zatlačení
tlačidla sa na náhodnom mieste plochy položí
náhodne vybraný jeden z obrázkov
náhodný obrázok
na náhodnú pozíciu
plochy:
|
procedure TForm1.Button4Click(Sender: TObject);
var
bmp:TBitmap;
begin
bmp:=TBitmap.Create;
bmp.LoadFromFile('bmp'+IntToStr(random(8)+1)+'.bmp');
Image1.Canvas.Draw(random(Image1.Width-bmp.Width),
random(Image1.Height-bmp.Height),bmp);
bmp.Free;
end;
|
- ak použije obrázky zo súboru
obrazky2.zip,
môžete si 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 zadefinovať dve stavové premenné
(vlastnosti) TransparentColor a Transparent
- prvou definujeme farbu, ktorá sa stane
priesvitnou a druhou zapíname (môžeme
aj vypnúť) režim priesvitnej farby, napr.
obrázky s priesvitnými
časťami:
|
procedure TForm1.Button5Click(Sender: TObject);
var
bmp:TBitmap;
begin
bmp:=TBitmap.Create;
bmp.LoadFromFile('bmp'+IntToStr(random(8)+1)+'.bmp');
bmp.TransparentColor:=clWhite;
bmp.Transparent:=true;
Image1.Canvas.Draw(random(Image1.Width-bmp.Width),
random(Image1.Height-bmp.Height),bmp);
bmp.Free;
end;
|
- môžeme pracovať 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ť
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 tiež Canvas najprv vyfarbiť nejakou farbou (napr.
pomocou FillRect)
programom vytvorený obrázok:
|
procedure TForm1.Button6Click(Sender: TObject);
var
bmp:TBitmap;
i:integer;
begin
bmp:=TBitmap.Create;
bmp.Width:=100; bmp.Height:=100;
bmp.Canvas.Brush.Color:=clWhite;
bmp.Canvas.FillRect(Rect(0,0,bmp.Width,bmp.Height));
for i:=10 downto 1 do
with bmp.Canvas do begin
if odd(i) then Brush.Color:=clYellow
else Brush.Color:=clRed;
Ellipse(50-5*i,50-5*i,50+5*i,50+5*i);
end;
bmp.TransparentColor:=clWhite;
bmp.Transparent:=true;
Image1.Canvas.Draw(random(Image1.Width-100),random(Image1.Height-100),bmp);
bmp.Free;
end;
|
- do bitmapy môžeme opečiatkovať aj iné
bitmapy - vytvoríme bitmapu so žltým
kruhom a do nej opečiatkujeme náhodne vybranú
bitmapu, napr.
použitie dvoch bitmáp:
|
procedure TForm1.Button7Click(Sender: TObject);
var
bmp,bmp1:TBitmap;
begin
bmp:=TBitmap.Create;
bmp.Width:=100; bmp.Height:=100;
bmp.Canvas.Brush.Color:=clWhite;
bmp.Canvas.FillRect(Rect(0,0,bmp.Width,bmp.Height));
with bmp.Canvas do begin
Brush.Color:=clYellow;
Ellipse(0,0,100,100);
end;
bmp.TransparentColor:=clWhite;
bmp.Transparent:=true;
bmp1:=TBitmap.Create;
bmp1.LoadFromFile('bmp'+IntToStr(random(5)+1)+'.bmp');
bmp1.TransparentColor:=clWhite;
bmp1.Transparent:=true;
bmp.Canvas.Draw((100-bmp1.Width) div 2,(100-bmp1.Height) div 2,bmp1);
bmp1.Free;
Image1.Canvas.Draw(random(Image1.Width-100),random(Image1.Height-100),bmp);
bmp.Free;
end;
|
- je ešte veľa iných možností, ako
pracovať s bitmapami, napr.
- namiesto bmp.LoadFromFile('bmp.bmp');
môžeme pomocou bmp.Assign(Image1.Picture);
načítať-prekopírovať obsah celej
grafickej plochy do bitmapy
- z grafickej plochy môžeme do bitmapy
prekopírovať len časť, napr. bmp.Canvas.Draw(-200,-100,Image1.Picture.Graphic);
opečiatkuje celú grafickú plochu do Canvasu bitmapy - tá
časť, ktorá sa nezmestí, bude
odrezaná - zrejme bitmapa už musí
mať pred týmto volaním nastavenú
veľkosť, napr. bmp.Width:=100; bmp.Height:=100;
- podobne by sme mohli použiť aj StretchDraw
- metóda CopyRect je podobná
StretchDraw, len môžeme zadať nielen
obdĺžnik kam sa kopíruje, ale aj obdĺžnik
(výrez), ktorý sa bude kopírovať;
CopyRect má tri parametre:
- obdĺžnik kam sa kopíruje (rovnako
ako pre StretchDraw)
- Canvas obrázka/plochy, ktorý
sa kopíruje (napr. bmp.Canvas,
Image1.Canvas, ...)
- obdĺžnik výrezu obrázka,
ktorý sa bude kopírovať, napr.
Rect(100,100,200,150);
- použitie CopyRect na jednoduchom príklade:
z bitmapy tigra "vystrihneme" len hlavu
opečiatkovanie len časti obrázka:
|
procedure TForm1.Button8Click(Sender: TObject);
var
bmp:TBitmap;
x1,y1,x2,y2,w,h:integer;
begin
bmp:=TBitmap.Create;
bmp.LoadFromFile('tiger.bmp');
x1:=random(Image1.Width); y1:=random(Image1.Height); // kam
x2:=64; y2:=51; // odkiaľ
w:=130; h:=152; // veľkosť toho, čo vystrihnem
Image1.Canvas.CopyRect(Rect(x1,y1,x1+w,y1+h),bmp.Canvas,Rect(x2,y2,x2+w,y2+h));
bmp.Free;
end;
|
- ak vám pri práci s grafickou plochou
(nielen pri práci s bitmapami) bliká
celá plocha, môžeme formuláru
nastaviť takúto stavovú premennú
(môže kvôli tomu sa mierne spomaliť
zobrazovanie v Image)
aby grafická plocha neblikala:
|
procedure TForm1.FormCreate(Sender: TObject);
begin
DoubleBuffered:=true;
end;
|
Bitmapa ako globálna premenná
- často sa nám môže hodiť, aby sme
nemuseli často používanú bitmapu (ale
aj viac bitmáp - možno aj pole bitmáp)
tesne pred použitím vytvoriť a prečítať
zo súboru a po použití (napr. po Draw)
ju pomocou Free uvoľniť
- najjednoduchším spôsobom je
vytvorenie globálnych premenných pri štarte
formulára - FormCreate
- potom je dobre nezabudnúť uvoľniť
takéto bitmapy pri konci aplikácie - FormDestroy
bitmapa ako globálna premenná:
|
var
bmp:TBitmap;
procedure TForm1.FormCreate(Sender: TObject);
begin
bmp:=TBitmap.Create;
bmp.LoadFromFile('tiger.bmp');
DoubleBuffered:=true;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
x,y,w,h:integer;
begin
w:=50+random(200); h:=50+random(200);
x:=random(Image1.Width-w); y:=random(Image1.Height-h);
Image1.Canvas.StretchDraw(Rect(x,y,x+w,y+h),bmp);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
bmp.Free;
end;
|
Kópia bitmapy
- ak potrebujeme prekopírovať obsah jednej
bitmapy do druhej, nikdy nesmieme použiť "obyčajné"
priradenie, napr.
!!! chybné riešenie !!!
|
var
b1,b2:TBitmap;
begin
b1:=TBitmap.Create; b2:=TBitmap.Create;
b1.LoadFromFile('miri.bmp');
b2:=b1; // tá istá bitmapa dostala druhé meno - b2
// ... požívanie bitmapy b2 ...
b1.Free; b2.Free;
|
- táto časť programu spadne, lebo po b1.Free
už neexistuje ani b2 a teda sa nedá
urobiť b2.Free ...
- prvé správne riešenie
kopírovanie jednej bitmapy
do Canvasu druhej bitmapy:
|
var
b1,b2:TBitmap;
begin
b1:=TBitmap.Create; b2:=TBitmap.Create;
b1.LoadFromFile('miri.bmp');
b2.Width:=b1.Width; b2.Height:=b1.Height;
b2.Canvas.Draw(0,0,b1);
// ... požívanie bitmapy b2 ...
b1.Free; b2.Free;
|
použitie metódy Assign:
|
var
b1,b2:TBitmap;
begin
b1:=TBitmap.Create; b2:=TBitmap.Create;
b1.LoadFromFile('miri.bmp');
b2.Assign(b1);
// ... požívanie bitmapy b2 ...
b1.Free; b2.Free;
|
Zhrnutie triedy TBitmap
TBitmap je
preddefinovaná trieda, ktorá slúži na manipuláciu s obrázkami - môžeme si ju
predstaviť ako obsah súboru s príponou .BMP (dá sa s ním pracovať napr. v
programe Paint/Skicár). Táto trieda má niekoľko užitočných stavových premenných
(vlastnosti - property) a metód.
niektoré stavové premenné:
- 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)
- 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é metódy:
- 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
- SaveToFile - uloží obrázok do súboru vo formáte .BMP
- Assign - urobí kópiu obrázka z inej bitmapy
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)
- Canvas.FillRect
- Canvas.Pixels[riadok,stĺpec]
- 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
|