Prezentowane materiały są przeznaczone dla uczniów szkół ponadgimnazjalnych. Autor artykułu: mgr Jerzy Wałaszek, wersja1.0 |
©2010 mgr
Jerzy Wałaszek |
Zanim przystąpisz do tej lekcji, zapoznaj się z poprzednimi lekcjami:
Wstęp do grafiki - biblioteki SDL i newgfx
Opis
biblioteki newgfx
Grafika - wielokąty
Tworzenie wykresów funkcji
Obcinanie (ang. clipping) ma na celu ograniczanie rysowania grafiki do wybranego obszaru na powierzchni graficznej. Fragmenty rysowanych figur, które znajdą się poza obszarem obcinania (ang. clip region), nie są rysowane.
Bez obcinania |
Z obcinaniem |
Obszar obcinania jest określony za pomocą struktury SDL_Rect:
typedef struct { Sint16 x, y; Uint16 w, h; } SDL_Rect;
Prostokąt obcinający zawarty jest w strukturze SDL_Surface. Standardowo ma on wymiary ekranu. Większość funkcji reagujących na ten prostokąt ma w swojej nazwie słowo Clip. Rozwiązanie takie przyjęto z powodu zmniejszenia szybkości działania tych funkcji w stosunku do wersji bez obcinania. Obcinanie gwarantuje, iż grafika nie wyjdzie poza zdefiniowany obszar.
Prostokąt obcinania ustawiamy za pomocą funkcji:
void SDL_SetClipRect(SDL_Surface * surface, SDL_Rect * clip);
Jeśli drugi parametr ma wartość NULL, to jako prostokąt obcinający zostanie ustawiony cały ekran. Poniższy program rysuje serię przypadkowych linii, które zostają obcięte do zdefiniowanego wcześniej prostokąta:
// Obcinanie // (C)2011 Koło Informatyczne // I LO w Tarnowie //------------------------------- #include "newgfx.h" #include <cstdlib> #include <time.h> int main(int argc, char *argv[]) { if(!SDL_Init(SDL_INIT_VIDEO)) { atexit(SDL_Quit); SDL_Surface * screen = SDL_SetVideoMode(320, 240, 32, SDL_HWSURFACE); SDL_Rect * r = new SDL_Rect; // Inicjujemy generator liczb pseudolosowych srand(time(NULL)); // Ustawiamy prostokat obcinania r->x = r->y = 20; r->w = screen->w - 40; r->h = screen->h - 40; SDL_SetClipRect(screen,r); // Zwiększamy prostokąt o 1 piksel w każdym kierunku r->x--; r->y--; r->w += 2; r->h += 2; if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); // Rysujemy ramkę wokół prostokata obcinania gfxRect(screen,r,0xffff00); // w prostokącie obcinania rysujemy 100 przypadkowych linii for(int i = 0; i < 100; i++) { Sint32 xp = rand() % screen->w; Sint32 xk = rand() % screen->w; Sint32 yp = rand() % screen->h; Sint32 yk = rand() % screen->h; gfxClipLine(screen,xp,yp,xk,yk,0xffffff); } if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); SDL_UpdateRect(screen, 0, 0, 0, 0); // oczekujemy klawisza ESC int waiting = 0; do { SDL_Event event; while (SDL_PollEvent(&event)) if ((event.type == SDL_QUIT) || ((event.type == SDL_KEYDOWN) && (event.key.keysym.sym == SDLK_ESCAPE))) waiting = 1; } while(!waiting); } return 0; } |
Zmieniając nieco pętlę for, możemy rysować różne figury w prostokącie obcinania:
Okręgi:
... for(int i = 0; i < 100; i++) { Sint32 x = rand() % screen->w; Sint32 y = rand() % screen->h; Uint32 p = 20 + rand() % 100; gfxClipCircle(screen,x,y,p,0xffffff); } ... |
Koła (uwaga: funkcje wypełniające figury automatycznie reagują na prostokąt obcinania i dlatego nie mają w swojej nazwie słowa Clip):
... for(int i = 0; i < 100; i++) { Sint32 x = rand() % screen->w; Sint32 y = rand() % screen->h; Uint32 p = 5 + rand() % 30; Uint32 c = ((rand() % 256) << 16) | ((rand() % 256) << 8) | (rand() % 256); gfxFillCircle(screen,x,y,p,c); } ... |
Elipsy:
... for(int i = 0; i < 100; i++) { Sint32 x = rand() % screen->w; Sint32 y = rand() % screen->h; Uint32 px = 5 + rand() % 30; Uint32 py = 5 + rand() % 30; gfxClipEllipse(screen,x,y,px,py,0xffffff); } ... |
Owale:
... for(int i = 0; i < 100; i++) { Sint32 x = rand() % screen->w; Sint32 y = rand() % screen->h; Uint32 px = 5 + rand() % 30; Uint32 py = 5 + rand() % 30; Uint32 c = ((rand() % 256) << 16) | ((rand() % 256) << 8) | (rand() % 256); gfxFillEllipse(screen,x,y,px,py,c); } ... |
Prawie każda funkcja graficzna biblioteki newgfx potrafi również rysować z efektem przezroczystości. W tym celu w wywołaniu funkcji dodajemy jeszcze jeden parametr o zakresie od 0 (obiekt zupełnie przezroczysty) do 256 (obiekt zupełnie nieprzezroczysty). Ostatni program z efektem przezroczystości wygląda następująco:
Owale przezroczyste:
... for(int i = 0; i < 100; i++) { Sint32 x = rand() % screen->w; Sint32 y = rand() % screen->h; Uint32 px = 5 + rand() % 30; Uint32 py = 5 + rand() % 30; Uint32 c = ((rand() % 256) << 16) | ((rand() % 256) << 8) | (rand() % 256); gfxFillEllipse(screen,x,y,px,py,c,196); } ... |
Biblioteka newgfx zawiera wygodne funkcje do rysowania dowolnych łamanych:
void gfxClipPoly(SDL_Surface * s, Sint32 * f, Uint32 color); void gfxClipPoly(SDL_Surface * s, Sint32 * f, Uint32 color, Uint32 alpha); void gfxPoly(SDL_Surface * s, Sint32 * f, Uint32 color); void gfxPoly(SDL_Surface * s, Sint32 * f, Uint32 color, Uint32 alpha); void gfxClipWuPoly(SDL_Surface * s, Sint32 * f, Uint32 color); // łamana z wygładzonymi liniami void gfxWuPoly(SDL_Surface * s, Sint32 * f, Uint32 color);
oraz łamanych wypełnionych (powinny być zamknięte):
void gfxFillPoly(SDL_Surface * s, Sint32 * f, Uint32 color); void gfxFillPoly(SDL_Surface * s, Sint32 * f, Uint32 color, Uint32 alpha);
s - powierzchnia graficzna
f - definicja figury
color - kolor linii lub wypełnienia
alpha - przezroczystość
Kształt łamanej zdefiniowany jest w tablicy f, której wskaźnik przekazujemy do funkcji. Każda łamana rozpoczyna się od liczby określającej ilość kolejnych wierzchołków. Jeśli liczba ta ma wartość 0, to oznacza to koniec listy łamanych. Za liczbą wierzchołków następują pary współrzędnych x,y określające położenie wierzchołków na płaszczyźnie rysunkowej.
Przykład:
3 12 1 14 5 1 1 2 5 3 4 7 0
Ta lista definiuje dwie łamane:
3 12 1 14 5 1 1 2 5 3 4 7 0
Pierwsza łamana posiada trzy wierzchołki: (12,1), (14,5) i (1,1).
Druga łamana posiada tylko dwa wierzchołki: (5,3) i (4,7).
Ostatnie zero
oznacza koniec listy łamanych.
Listę łamanych można utworzyć w tablicy o odpowiedniej wielkości. Na przykład tak:
Sint32 f[] = {3, 12, 1, 14, 5, 1, 1, 2, 5, 3, 4, 7, 0};
Dla przykładu zdefiniujmy następującą figurę:
Figura składa się z dwóch łamanych zamkniętych - wymaga to powtórzenie na początku i na końcu pierwszego wierzchołka. Zatem łamana zewnętrzna posiada 5 wierzchołków, a łamana wewnętrzna ma ich 4. Pełna definicja wygląda następująco:
5 20 10 110 50 20 90 10 50 20 10
4 30 30 80 50 30 70 30 30
0
// Łamane // (C)2011 Koło Informatyczne // I LO w Tarnowie //------------------------------- #include "newgfx.h" int main(int argc, char *argv[]) { if(!SDL_Init(SDL_INIT_VIDEO)) { atexit(SDL_Quit); SDL_Surface * screen = SDL_SetVideoMode(320, 240, 32, SDL_HWSURFACE); // Tworzymy tablicę z listą łamanych Sint32 f[] = {5,20,10,110,50,20,90,10,50,20,10, 4,30,30,80,50,30,70,30,30, 0}; if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); // Rysujemy wypełnienie łamanej w kolorze czerwonym gfxFillPoly(screen,f,0xff0000); // Na wypełnieniu rysujemy obrys łamanej w kolorze białym gfxPoly(screen,f,0xffffff); if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); SDL_UpdateRect(screen, 0, 0, 0, 0); // oczekujemy klawisza ESC int waiting = 0; do { SDL_Event event; while (SDL_PollEvent(&event)) if ((event.type == SDL_QUIT) || ((event.type == SDL_KEYDOWN) && (event.key.keysym.sym == SDLK_ESCAPE))) waiting = 1; } while(!waiting); } return 0; } |
Biblioteka newgfx posiada dwie funkcje do wypełniania dowolnych obszarów:
void gfxBoundaryFill(SDL_Surface * screen, Sint32 x, Sint32 y, Uint32 fcolor, Uint32 bcolor);
void gfxFloodFill(SDL_Surface * screen, Sint32 x, Sint32 y, Uint32 fcolor);
W obu przypadkach podajemy współrzędne punktu (x,y) wewnątrz obszaru, który ma zostać wypełniony kolorem fcolor.
Pierwsza funkcja wykonuje tzw. wypełnianie konturowe (ang boundary-fill) - wszystkie piksele zawarte wewnątrz konturu (zamkniętej linii zbudowanej z pikseli o tym samym kolorze) zostają pokolorowane na ten sam kolor wypełnienia. Zawartość objętego konturem obszaru nie jest istotna (patrz poniżej). Nie może on jedynie zawierać pikseli o kolorze wypełnienia. Kolor konturu określa parametr bcolor.
Wypełnianie powodziowe (ang. floodfill) - wszystkie piksele spójne (tzn. przyległe w 4 lub 8 kierunkach) do piksela startowego i posiadające ten sam co on kolor zostaną pokolorowane na nowy kolor wypełnienia. Wynika z tego, iż wypełniany obszar musi posiadać przed operacją jednolity kolor. Natomiast obszary przyległe mogą posiadać kolory dowolne, ale różne od koloru wypełnianego obszaru.
Piksel, od którego rozpoczynamy wypełnianie obszaru nazywamy ziarnem wypełnienia (ang. fill seed). Musi on znajdować się wewnątrz wypełnianego obszaru. To właśnie jego położenie określają współrzędne x,y.
Obie procedury reagują na prostokąt obcinania.
Poniższy program najpierw rysuje zielone okręgi, a na nich czerwone prostokąty. Następnie wypełnia tło kolorem niebieskim wykorzystując wypełnienie konturowe. Za kontur przyjmujemy kolor czerwony - czyli kolor prostokątów. Zwróć uwagę, że okręgi poza prostokątami zostaną zamalowane - zniknie wszystko aż do napotkania konturu czerwonego.
// Wypełnienie konturowe // (C)2011 Koło Informatyczne // I LO w Tarnowie //------------------------------- #include "newgfx.h" #include <cstdlib> #include <time.h> int main(int argc, char *argv[]) { if(!SDL_Init(SDL_INIT_VIDEO)) { atexit(SDL_Quit); SDL_Surface * screen = SDL_SetVideoMode(320, 240, 32, SDL_HWSURFACE); srand(time(NULL)); if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); // rysujemy okręgi w kolorze zielonym for(int i = 0; i < 10; i++) gfxClipCircle(screen, rand() % screen->w, rand() % screen->h, 10 + rand() % 50, 0x00ff00); // rysujemy ramki w kolorze czerwonym SDL_Rect * r = new SDL_Rect; for(int i = 0; i < 10; i++) { r->x = rand() % screen->w; r->y = rand() % screen->h; r->w = 20 + rand() % 50; r->h = 20 + rand() % 50; gfxClipRect(screen,r,0xff0000); } // wypełniamy od środka ekranu kolorem niebieskim do konturu czerwonego gfxBoundaryFill(screen, screen->w >> 1, screen->h >> 1, 0x0000ff, 0xff0000); if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); SDL_UpdateRect(screen, 0, 0, 0, 0); // oczekujemy klawisza ESC int waiting = 0; do { SDL_Event event; while (SDL_PollEvent(&event)) if ((event.type == SDL_QUIT) || ((event.type == SDL_KEYDOWN) && (event.key.keysym.sym == SDLK_ESCAPE))) waiting = 1; } while(!waiting); } return 0; } |
Wymień teraz w programie funkcję gfxBondaryFill() na:
gfxFloodFill(screen, screen->w >> 1, screen->h >> 1, 0x0000ff);
Teraz efekt wypełnienia będzie inny - funkcja gfxFloodFill() reaguje na każdy kolor, który różni się od koloru ziarna:
Poniższy program pozwala śledzić wypełnianie obszarów za pomocą funkcji gfxFloodFill(). W programie zrealizowany jest algorytm wypełniania. Wykorzystuje on stos do przechowywania pikseli wypełnianego obszaru:
// Wizualizacja algorytmu wypełniania powodziowego // (C)2011 Koło Informatyczne // I LO w Tarnowie //------------------------------- #include <windows.h> #include "newgfx.h" #include <cmath> // funkcja sqrt #include <cstdlib> #include <time.h> // do inicjalizacji generatora pseudolosowego #include <stack> // używamy STL do implementacji stosu using namespace std; // potrzebne dla STL! const int SCRX = 640; // stałe określające szerokość i wysokość const int SCRY = 480; // ekranu w pikselach int main(int argc, char *argv[]) { if(SDL_Init(SDL_INIT_VIDEO)) exit(-1); atexit(SDL_Quit); SDL_Surface * screen; screen = SDL_SetVideoMode(SCRX, SCRY, 32, SDL_HWSURFACE); // inicjujemy generator liczb pseudolosowych srand((unsigned)time(NULL)); // wyznaczamy środek ekranu Sint32 x = screen->w >> 1; Sint32 y = screen->h >> 1; if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); // rysujemy 20 losowych ramek w różnych kolorach, tak aby nie przykrywały środka ekranu SDL_Rect * r = new SDL_Rect; Uint32 color; for(int i = 0; i < 20; i++) { do { r->x = rand() % screen->w; r->y = rand() % screen->h; r->w = rand() % (screen->w >> 3) + 4; r->h = rand() % (screen->h >> 3) + 4; } while((r->x <= x) && (x < r->x + r->w) && (r->y <= y) && (y < r->y + r->h)); color = ((rand() % 256) << 16) | ((rand() % 256) << 8) | (rand() % 256); gfxClipRect(screen, r, color); } // rysujemy 20 losowych okręgów w różnych kolorach, tak aby nie przykrywały środka Sint32 xs, ys, rs; for(int i = 0; i < 20; i++) { do { xs = rand() % screen->w; ys = rand() % screen->h; rs = rand() % (screen->w >> 3) + 4; } while(sqrt((x - xs) * (x - xs) + (y - ys) * (y - ys)) <= rs); color = ((rand() % 256) << 16) | ((rand() % 256) << 8) | (rand() % 256); gfxClipCircle(screen,xs,ys,rs,color); } if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); SDL_UpdateRect(screen, 0, 0, 0, 0); // tutaj umieszczamy algorytm wypełniania Uint32 scolor; // kolor ziarna Uint32 fcolor = 0x0000ff; // kolor wypełnienia Uint32 pcolor; // kolor piksela stack<Sint32> q; // stos if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); scolor = gfxGetPixel(screen, x, y); if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); q.push(x); q.push(y); while(!q.empty()) { y = q.top(); q.pop(); // pobieramy ze stosu x i y x = q.top(); q.pop(); if((x < screen->w) && (x >= 0) && (y < screen->h) && (y >= 0)) { if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); pcolor = gfxGetPixel(screen, x, y); if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); if(pcolor != scolor) continue; if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); gfxPlot(screen, x, y, fcolor); if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); SDL_UpdateRect(screen, x, y, 1, 1); q.push(x); q.push(y - 1); q.push(x + 1); q.push(y); q.push(x); q.push(y + 1); q.push(x - 1); q.push(y); } } // oczekujemy klawisza ESC int waiting = 0; do { SDL_Event event; while (SDL_PollEvent(&event)) if ((event.type == SDL_QUIT) || ((event.type == SDL_KEYDOWN) && (event.key.keysym.sym == SDLK_ESCAPE))) waiting = 1; } while(!waiting); return 0; } |
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