|
|
9.Coś dla
amatorów grafiki
Witam ponownie po dłuższej przerwie. Teraz kiedy przebrnęliśmy już razem przez podstawy programowania w BCB możemy zająć się już trochę cięższymi rzeczami. Może nie będą to znowu takie trudne rzeczy ale nie ukrywam, że w kolejnych lekcja ch będzie co raz to więcej informacji do przyswojenia. Wydaje mi się, że już tak straszę od kilku lekcji a tu ciągle proste rzeczy. Kiedyś się jednak wezmę w garść i napiszę coś trudnego. A jak na razie zajmiemy się grafiką. Komponent
TImage był już omówiony ale dzisiaj wykorzystamy go do czegoś innego. Napiszemy małego Photoshopa :))))))). Nauczymy się przy tym rysować po płótnie czyli po
TCanvas. Do roboty.
Jak zwykle zaczynamy od rozpisania plany naszego nowego projektu. Ekran programu podzielimy na dwie części, dwoma panelami. Używanie paneli może nam bardzo ułatwić pracę więc radzę się
do nich przyzwyczajać. Po lewej stronie umieścimy kontrolki, którymi wybierać będziemy narzędzia do rysowania a po prawej sam rysunek (obiekt
TImage o nazwie Obrazek). To co będziemy rysować będziemy mogli wybrać klikając na odpowiedni przycisk. Przyciski będą typu
TSpeedButton, który to typ posiada ciekawe właściwości. Jeżeli ustawić właściwość Group kilku przycisków typu
TSpeedButton na taką samą ale różną od zera oraz właściwość AllowAllUp na false to tylko jeden z tych przycisków będzie mógł być wciśnięty. Taki wciśnięty przycisk określać nam będzie jaką funkcję będziemy wykonywać na obrazku. Oczywiście nie będą to jakieś wymyślne funkcje ale wystarczy na początek. Jeżeli ktoś poczuje, że brakuje mu jakiejś cennej funkcji to zachęcam go do rozszerzenia programu. Jak zwykle nie zapominamy o przycisku "Exit". Tworząc program musimy zawsze pamiętać, że interfejs użytkownika jest bardzo (ale to bardzo, bardzo) ważną rzeczą.
A teraz kilka słów "na marginesie". Nie tylko obiekt ##Timage pozwala na rysowanie po nim. Możemy malować po wszystkich obiektach posiadajacych własność Canvas. dla tych obirktów, które nie posiadają takiej własności trzeba posłużyć się sztuczką.
Piszemy w pliku nagłówkowym:
TControlCanvas *MojCanvas;
Oraz w pliku głównym (najlepiej w konstruktorze obiektu lub w trakcie tworzenia formy):
MojCanvas = new TControlCanvas;
MojCanvas->Control = NazwaObiektuPoKtorymBedziemyRysowac;
Dzięki temu będziemy mogli malować po wszystkim. Tyle jeżeli chodzi o pisanie na marginesie. Możemy wracać do naszego programu. Jak już wszyscy się pewnie domyślili (jeżeli nie to teraz już będą wiedzieć na pewno) malować możemy po obiektach mających właściwość Canvas. I takim obiektem jest właśnie obiekt typu
TImage. Nasuwa się jednak pytanie: Jak malować? Odpowiedź jest prosta: Myszką :))))). Hmmm chyba nie na taką
odpowiedź wszyscy czekali... No to może na taką: Canvas posiada wiele funkcji służących do rysowania na
sobie (linie, pojedyncze piksele, koła, prostokąty...). I właśnie wykorzystując te funkcje będziemy mogli rysować na naszym obrazku. Należy zauważyć jedną rzecz. Rysować po obrazku możemy wtedy gdy jest on bitmapą. BCB nie pozwala nam rysować swobodnie po obrazkach typu JPG. To tylko taka uwaga "na marginesie" :)))). Oczywiście wszystko się da zrobić jeżeli ... , ale ... . Jak już mówiłem wcześniej do wyboru odpowiednich funkcji użyjemy przycisków. Teraz pozostaje nam tylko sprawa wyboru odpowiedniego koloru. Może nam do tego posłużyć obiekt typu TCColorGrid z zakładki Samples. Korzystać możemy (zwłaszcza) z jego dwóch właściwości ForegroundEnabled oraz ForegroundIndex. Gdy ForegroundEnabled jest ustawiona na true to możemy wybierać kolor, którym będziemy rysować. Wybrany kolor znajduje się w ForegroundIndex. BackgroundEnabled pozwala nam na wybór koloru tła. My jednak użyjemy czegoś innego do wyboru koloru. Będzie to okienko dialogowe TColorDialog. Utworzymy mały obrazek, który będziemy wypełniać wybranym kolorem. Potrzebny będzie jeszcze komponent do wyboru grubości linii. Użyjemy TCSpinEdit również z zakładki Samples. Wartość MinValue ustawimy na 1 a MaxValue na 30 (ale możemy na więcej lub mniej). Increment pozostawimy na 1. Służy ona do wyboru o jak wiele mamy zwiększać / zmniejszać obecną wartość (przechowywaną w polu Value). Mamy już chyba wszystkie komponenty na formie więc możemy zabrać się za pisanie.
Posłużymy się zdarzeniami OnMouseUp oraz OnMouseDown obiektu Obraz. Wyglądać one będą mniej więcej tak:
{
if (Linia->Down)
{
} else
if (Kolo->Down)
{
} else
if (Prostokat->Down)
{
} else
if (Zwykle->Down)
{
} else
{
// to nie powinno się NIGDY zdarzyć ale...
}
Między odpowiednie instrukcje warunkowe wstawiać będziemy odpowiednie funkcje. Możecie się zapytać dlaczego nie użyć tylko zdarzenia OnClick, czyż nie byłoby to prostsze? Tak, byłoby prostsze ale również mniej funkcjonalne. Zauważmy, że użytkownik może rysować linię naciskając jeden klawisz w miejscu gdzie ma się ona zaczynać i puszczając, w miejscu gdzie ma się ona kończyć. Dlatego właśnie rozbijamy naszą funkcję na dwa zdarzenia. Cały szkielet jest tak zrobiony aby ułatwić dodanie nowych funkcji (po to by poćwiczyć). Obiekt typu
TCanvas posiada właściwość Pen. Posłuży ona do wyboru koloru i grubości linii. Ustawiamy je następująco:
//grubość linii
Obraz->Canvas->Pen->Width=CSpinEdit1->Value;
//kolor jakim będziemy rysować
Obraz->Canvas->Pen->Color=...;
Gdybyśmy chcieli malować wypełnione obiekty musielibyśmy posłużyć się dodatkowo własnością Brush (a więc
Obraz->Canvas->Brush->Color=(TColor);). Jak zwykle dla uproszczenia omijamy to w naszym programie (tak jak zapisywanie i odczytywanie obrazka z pliku). Nareszcie możemy przejść do rysowania linii. Wygląda to tak:
if (Linia->Down)
{
//jeżeli rysowanie nie jest w toku
//to wybieramy punkt startowy
if (!Rysowanie)
{
P1.x=X;P1.y=Y;
//ustawiamy punkt na X,Y obiektu TCanvas
Obraz->Canvas->PenPos=P1;
// a więc już zaczęliśmy rysowanie
Rysowanie=true;
}
//jeżeli już rysujemy linie to ustalamy punkt końcowy
else
{
//rysujemy linię funkcją LineTo
Obraz->Canvas->LineTo(X,Y);
//skończyliśmy rysowanie
Rysowanie=false;
}
} else
Myślę, że opis kodu jest wystarczająco jasny. Pozostaje nam jeszcze obsługa zdarzenia OnMouseUp, a wygląda ona tak:
if (Linia->Down)
{
//jeżeli rysowanie w toku oraz
//punkt startowy różny od punktu obecnego
//tak więc oznacza to, że użytkownik przesunął
//mysz między naciśnięciem i zwolnieniem klawisza
//możemy więc rysować
if ((Rysowanie)&&((P1.x!=X)||(P1.y!=Y)))
{
//rysujemy linię funkcją LineTo
Obraz->Canvas->LineTo(X,Y);
//skończyliśmy rysowanie
Rysowanie=false;
}
} else
Program napisany do tego momentu znajduje się w podkatalogu Rys1 (w pliku z kodem do dzisiejszej lekcji). Pozostałe funkcje działania programu zostaną opisane w kodzie źródłowym do dzisiejszej lekcji. Opiszę, jeszcze tylko funkcję malowania "zwykłego" gdyż używa ona niespotykanej dotąd właściwości a mianowicie Pixels. Można to zrobić tak:
Obraz->Canvas->Pixels[X][Y]=ColorDialog1->Color;
Odpowiedniemu pikselowi przyporządkowujemy odpowiedni kolor. W naszym przypadku ma to jednak pewne wady. Jakie ? Wystarczy usunąć komentarz i skompilować program. W programie użyłem więc drugiej wersji. Rysuje ona linię od punktu do punktu w miarę ruszania myszką z wciśniętym klawiszem. Użyłem do tego zdarzenia OnMouseMove. Sprawdzam tylko czy zmienna Rysowanie jest ustawiona (w tym wypadku czy przycisk jest wciśnięty) i rysuję linie:
Obraz->Canvas->LineTo(X,Y);
Jak zwykle zachęcam do dodania nowych funkcji do programu. Ciekawym rozwiązaniem byłoby zachowywanie na dysk narysowanego obrazka. A może dodać rysowanie cienia linii w trakcie rysowania ??? Proszę eksperymentować. To pomaga w nauce nowego języka.
lekcja06.rar - kod źródłowy
programu z tego rozdziału.
|
|