Prezentowane materiały są przeznaczone dla uczniów szkół ponadgimnazjalnych. Autor artykułu: mgr Jerzy Wałaszek, wersja1.0 |
©2011 mgr
Jerzy Wałaszek
|
Na dzisiejszych zajęciach zajmiemy się obsługą bitmap w Borland C++ Builder. Bitmapa jest obiektem graficznym posiadającym własne płótno Canvas, po którym program może niezależnie rysować. Zawartość bitmapy możemy również kopiować na płótno okna, uzyskując przez to ciekawe efekty animacyjne.
Utwórz nowy projekt i zapisz go na dysku w przygotowanym wcześniej katalogu. Zapamiętaj położenie tego katalogu, gdyż w dalszej części będziemy coś w nim robić. Na oknie umieść komponent Timer z zakładki System. Następnie ustaw własności okna i Timera wg poniższej specyfikacji:
Form1
BorderStyle =
bsSingle
Użytkownik nie będzie mógł zmieniać rozmiarów okna przez przeciąganie
myszką jego krawędzi.
Caption = Obrazki
Tytuł okna.
ClientHeight = 512
Wysokość obszaru graficznego okna. Taką wysokość będzie miała nasza
powierzchnia rysunkowa Canvas.
ClientWidth = 512
Szerokość obszaru graficznego okna.
Color = clBlack
Tło okna będzie czarne.
Name = frmBitmaps
Pod tą nazwą będzie dostępna w programie zmienna klasy naszego okna.
Position = poScreenCenter
Okienko aplikacji będzie się pojawiało na środku ekranu.
Timer1
Name = tmrAnimate
Na początku programu stworzymy kilka zmiennych globalnych. Wpisz następujący kod:
#include "Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TfrmBitmaps *frmBitmaps; // Zmienne globalne Graphics::TBitmap * bm; // wskaźnik bitmapy int x,y,dx,dy; //---------------------------------------------------------------------------
Zmienna bm jest wskaźnikiem bitmapy. Zmienne x i y określają położenie bitmapy na płótnie okna w trakcie animacji. Odnoszą się one do lewego górnego narożnika bitmapy. dx i dy definiują przesunięcie w osiach OX i OY.
Stwórz funkcję obsługi zdarzenie onCreate dla okna głównego i wpisz poniższy kod:
void __fastcall TfrmBitmaps::FormCreate(TObject *Sender) { srand(time(NULL)); // tworzymy bitmapę bm = new Graphics::TBitmap; bm->Width = 48; // szerokość płótna bitmapy bm->Height = 48; // wysokość płótna bitmapy // tło malujemy na czarno bm->Canvas->Brush->Color = clBlack; bm->Canvas->FillRect(Rect(0,0,bm->Width,bm->Height)); // rysujemy czerwoną piłkę z żółtym konturem bm->Canvas->Brush->Color = clRed; bm->Canvas->Pen->Color = clYellow; bm->Canvas->Pen->Width = 2; // szerokość linii bm->Canvas->Ellipse(Rect(0,0,bm->Width,bm->Height)); // wyznaczamy pozycję początkową oraz przyrosty x = rand() % (ClientWidth - bm->Width); dx = 1 + rand() % 5; y = rand() % (ClientHeight - bm->Height); dy = 1 + rand() % 5; DoubleBuffered = true; }
Stwórz funkcję obsługi zdarzenia onPaint i wpisz do niej poniższy kod:
void __fastcall TfrmBitmaps::FormPaint(TObject *Sender) { Canvas->Draw(x,y,bm); }
Funkcja Canvas->Draw() umieszcza bitmapę na płótnie Canvas na określonej przez x i y pozycji.
Na koniec stwórz funkcję obsługi zdarzenia onTimer dla komponentu tmrAnimate i wpisz do niej poniższy kod:
void __fastcall TfrmBitmaps::tmrAnimateTimer(TObject *Sender) { int t; t = x + dx; if((t < 0) || (t >= ClientWidth - bm->Width)) dx = -dx; x += dx; t = y + dy; if((t < 0) || (t >= ClientHeight - bm->Height)) dy = -dy; y += dy; Invalidate(); }
Po uruchomieniu aplikacji otrzymasz okno z odbijającą się od jego krawędzi piłeczką.
Zaletą bitmapy w porównaniu z rysowaniem jest to, że raz utworzona grafika może bardzo szybko zostać skopiowana na płótno Canvas. Ma to szczególne znaczenie, gdy rysunek jest skomplikowany i wymaga do utworzenia wielu skomplikowanych operacji graficznych.
W kolejnym kroku rozmnożymy nasze piłeczki do np. 100. Bitmapa pozostanie jedna, natomiast współrzędne poszczególnych piłeczek będziemy pamiętać w tablicy. Zmień początek programu następująco:
#include "Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TfrmBitmaps *frmBitmaps; // Zmienne globalne const int MAXB = 100; // liczba piłeczek Graphics::TBitmap * bm; int x[MAXB],y[MAXB],dx[MAXB],dy[MAXB]; //---------------------------------------------------------------------------
Zmień funkcję obsługi zdarzenia onCreate następująco:
void __fastcall TfrmBitmaps::FormCreate(TObject *Sender) { srand(time(NULL)); // tworzymy bitmapę bm = new Graphics::TBitmap; bm->Width = 48; // szerokość płótna bitmapy bm->Height = 48; // wysokość płótna bitmapy // tło malujemy na czarno bm->Canvas->Brush->Color = clBlack; bm->Canvas->FillRect(Rect(0,0,bm->Width,bm->Height)); // rysujemy czerwoną piłkę z żółtym konturem bm->Canvas->Brush->Color = clRed; bm->Canvas->Pen->Color = clYellow; bm->Canvas->Pen->Width = 2; // szerokość linii bm->Canvas->Ellipse(Rect(0,0,bm->Width,bm->Height)); // wyznaczamy pozycje początkowe oraz przyrosty for(int i = 0; i < MAXB; i++) { x[i] = rand() % (ClientWidth - bm->Width); dx[i] = 1 + rand() % 5; y[i] = rand() % (ClientHeight - bm->Height); dy[i] = 1 + rand() % 5; } DoubleBuffered = true; }
Zmień funkcję obsługi zdarzenia onPaint:
void __fastcall TfrmBitmaps::FormPaint(TObject *Sender) { for(int i = 0; i < MAXB; i++) Canvas->Draw(x[i],y[i],bm); }
Oraz funkcję obsługi onTimer:
void __fastcall TfrmBitmaps::tmrAnimateTimer(TObject *Sender) { int i,t; for(i = 0; i < MAXB; i++) { t = x[i] + dx[i]; if((t < 0) || (t >= ClientWidth - bm->Width)) dx[i] = -dx[i]; x[i] += dx[i]; t = y[i] + dy[i]; if((t < 0) || (t >= ClientHeight - bm->Height)) dy[i] = -dy[i]; y[i] += dy[i]; } Invalidate(); }
Gdy teraz uruchomisz aplikację, otrzymasz całą chmarę (100) piłeczek, które w przypadkowy sposób odbijają się od krawędzi okna:
Jednak jest pewien problem. Przyjrzyj się dokładnie piłeczkom. Na płótno Canvas jest kopiowana cała zawartość bitmapy, razem z jej tłem. Dlatego powstają czarne narożniki. Jak temu zaradzić? Otóż musimy stworzyć tzw. kolor przezroczysty na bitmapie, czyli kolor, który nie będzie kopiowany. Tym kolorem u nas jest kolor czarny. Dopisz do funkcji obsługi zdarzenia onCreate dwie linijki kodu:
void __fastcall TfrmBitmaps::FormCreate(TObject *Sender) { srand(time(NULL)); // tworzymy bitmapę bm = new Graphics::TBitmap; bm->Width = 48; // szerokość płótna bitmapy bm->Height = 48; // wysokość płótna bitmapy // tło malujemy na czarno bm->Canvas->Brush->Color = clBlack; bm->Canvas->FillRect(Rect(0,0,bm->Width,bm->Height)); // rysujemy czerwoną piłkę z żółtym konturem bm->Canvas->Brush->Color = clRed; bm->Canvas->Pen->Color = clYellow; bm->Canvas->Pen->Width = 2; // szerokość linii bm->Canvas->Ellipse(Rect(0,0,bm->Width,bm->Height)); bm->Transparent = true; // włączamy przezroczystość bm->TransparentColor = clBlack; // kolor przezroczysty // wyznaczamy pozycje początkowe oraz przyrosty for(int i = 0; i < MAXB; i++) { x[i] = rand() % (ClientWidth - bm->Width); dx[i] = 1 + rand() % 5; y[i] = rand() % (ClientHeight - bm->Height); dy[i] = 1 + rand() % 5; } DoubleBuffered = true; }
Skompiluj i uruchom ponownie aplikację, czarne narożniki znikną:
Grafikę w obiekcie bitmap można załadować z pliku dyskowego, który wcześniej przygotowaliśmy sobie w jakimś programie graficznym. Zapisz poniższy obrazek pod nazwą bm.bmp w swoim katalogu projektowym (obrazek kliknij prawym przyciskiem myszki i wybierz opcję w stylu "zapisz grafikę jako", następnie określ katalog i nazwę):
bm.bmp |
Gdy to wykonasz, zmień treść funkcji obsługi zdarzenia onCreate – zamiast rysować, załadujemy obrazek z pliku:
void __fastcall TfrmBitmaps::FormCreate(TObject *Sender) { srand(time(NULL)); // tworzymy bitmapę bm = new Graphics::TBitmap bm->LoadFromFile("bm.bmp"); // ładujemy bitmapę z pliku bm->Transparent = true; // włączamy przezroczystość bm->TransparentColor = clBlack; // kolor przezroczysty // wyznaczamy pozycje początkowe oraz przyrosty for(int i = 0; i < MAXB; i++) { x[i] = rand() % (ClientWidth - bm->Width); dx[i] = 1 + rand() % 5; y[i] = rand() % (ClientHeight - bm->Height); dy[i] = 1 + rand() % 5; } DoubleBuffered = true; }
Uruchom aplikację.
W kolejnym kroku stworzymy prostą animację biegaczy. Animacja będzie zbudowana z 6 kadrów, klatek. Skopiuj kolejno poniższe obrazki do katalogu projektowego pod podanymi nazwami:
b0.bmp | b1.bmp | b2.bmp | b3.bmp | b4.bmp | b5.bmp |
W projekcie zmień następujące własności:
frmBitmaps
Color = clAqua
tmrAnimate
Name = tmrAnimate
Zmień zmienne globalne następująco:
#pragma package(smart_init) #pragma resource "*.dfm" TfrmBitmaps *frmBitmaps; // Zmienne globalne const int MAXB = 6; // liczba kadrów animacji const int MAXA = 6; // co ile klatek zmiana kadru Graphics::TBitmap * bm[MAXB]; // bitmapy na poszczególne kadry int x,y,dx,da,cbm; //--------------------------------------------------------------------------- __fastcall TfrmBitmaps::TfrmBitmaps(TComponent* Owner)
Zmienne x,y będą określały położenie kadru na płótnie Canvas okna. Zmienna dx jest przyrostem w osi OX. Nowa zmienna da służy do zliczania poszczególnych klatek. Co MAXA klatek następuje zmiana kadru. Zmienna cbm zawiera numer bieżącego kadru.
Zmień funkcję obsługi zdarzenia onCreate:
void __fastcall TfrmBitmaps::FormCreate(TObject *Sender) { AnsiString fn[] = {"b0.bmp","b1.bmp","b2.bmp","b3.bmp","b4.bmp","b5.bmp"}; // tworzymy bitmapy for(int i = 0; i < MAXB; i++) { bm[i] = new Graphics::TBitmap; bm[i]->LoadFromFile(fn[i]); bm[i]->Transparent = true; // włączamy przezroczystość bm[i]->TransparentColor = clWhite; // kolor przezroczysty } // wyznaczamy pozycje początkowe oraz przyrosty x = -bm[0]->Width; // rozpoczynamy poza marginesem okna y = ClientHeight - bm[0]->Height - 16; dx = 3; // przesuw w osi OX da = MAXA; // zmiana kadru po MAXA klatkach cbm = 0; // bieżący kadr DoubleBuffered = true; }
Zmień funkcję obsługi zdarzenia onPaint:
void __fastcall TfrmBitmaps::FormPaint(TObject *Sender) { // rysujemy bieżnię Canvas->Brush->Color = clGreen; Canvas->FillRect(Rect(0,160,ClientWidth,ClientHeight)); // rysujemy biegacza Canvas->Draw(x,y,bm[cbm]); }
Na koniec zmień funkcję obsługi zdarzenia onTimer:
void __fastcall TfrmBitmaps::tmrAnimateTimer(TObject *Sender) { x += dx; if(x > ClientWidth) x = -bm[0]->Width; if(!--da) { da = MAXA; if(++cbm == MAXB) cbm = 0; } Invalidate(); }
Po uruchomieniu otrzymasz animację biegacza.
I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe