Koło informatyczne 2012

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

BorderIconsbiMaximize = false
Okno nie będzie można powiększać na cały ekran.

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

Interval = 25
Czas w ms pomiędzy kolejnymi zdarzeniami onTimer.

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

ClientHeight = 200

Color = clAqua

tmrAnimate

Interval = 20

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.

 

 



List do administratora Serwisu Edukacyjnego Nauczycieli I LO

Twój email: (jeśli chcesz otrzymać odpowiedź)
Temat:
Uwaga: ← tutaj wpisz wyraz  ilo , inaczej list zostanie zignorowany

Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).

Liczba znaków do wykorzystania: 2048

 

W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień szeroko opisywanych w podręcznikach.



   I Liceum Ogólnokształcące   
im. Kazimierza Brodzińskiego
w Tarnowie

©2017 mgr Jerzy Wałaszek

Dokument ten rozpowszechniany jest zgodnie z zasadami licencji
GNU Free Documentation License.