Bufor akumulacyjny umożliwia łączenie kilku obrazów w celu uzyskania określonego efektu końcowego. W swojej budowie bufor akumulacyjny przypomina bufor koloru, przechowuje bowiem informacje o składowych RGBA kolorów. Bufor akumulacyjny nie jest dostępny, gdy biblioteka OpenGL pracuje w trybie indeksu kolorów.
Bufor akumulacyjny używany jest do uzyskiwania wielu efektów specjalnych. Najpopularniejsze z nich to rozmycie w ruchu (ang. motion blur), głębia ostrości (ang. depth of field), antyaliasing pełnoekranowy (ang. FSAA - Full Scene Anti Aliasing) oraz miękkie cienie (ang. soft shadows). Dwie pierwsze z wymienionych technik przedstawimy w niniejszym odcinku kursu. Pozostałe zostaną zaprezentowane wraz z innymi technikami antyaliasingu i cieni.
Niestety użycie bufora akumulacyjnego do uzyskania efektów specjalnych wiąże się najczęściej z bardzo dużą ilością dodatkowych obliczeń. Jednak największym problemem jest brak sprzętowej obsługi bufora akumulacyjnego przez popularne procesory graficzne.
Sterowanie buforem akumulacyjnym
Działaniem bufora akumulacyjnego steruje funkcja:
void glAccum( GLenum op, GLfloat value )
której parametr op określa rodzaj operacji wykonywanej na buforze:
Jak Czytelnik zauważył, w powyższych równaniach (Rc, Gc, Bc, Ac) oznaczają składowe bufora kolorów, a (Ra , Ga , Ba , Aa ) wartości bufora szablonowego
Czyszczenie bufora akumulacyjnego
W razie potrzeby bufor akumulacyjny może zostać wypełniony określonymi wartościami składowych RGBA. Służy to tego dobrze znana funkcja glClear wraz z parametrem
GL_ACCUM_BUFFER_BIT. Domyślnie bufor akumulacyjny wypełniany jest wartościami (0, 0, 0, 0), ale funkcja:
void glClearAccum( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha )
umożliwia zmianę tego stanu. Wartości parametrów red, green, blue i alpha w razie potrzeby obcinane są do przedziału [−1, 1].
Programy przykładowe
Zgodnie z wcześniejszą zapowiedzią pierwszy program przykładowy (plik rozmycie_w_ruchu.cpp) przedstawia efekt rozmycia w ruchu. Efekt uzyskiwany jest poprzez odpowiednie operacje na buforze akumulacyjnym. Z wielu możliwości uzyskania efektu rozmycia program przedstawia trzy metody.
Pierwsze dwie dają bardzo podobny efekt (patrz rysunki 1 i 2) i sprowadzają się jedynie do wykonywania odpowiednich operacji na buforze akumulacyjnym, przy czym bufor ten czyszczony jest tylko przy rysowaniu pierwszej ramki obrazu.
Trzeci zastosowany algorytm jest bardziej klasyczny i sprowadza się wielokrotnego rysowania sceny za każdym razem przesuniętej o odpowiedni kąt. Kolejne rysowane ramki są po skalowaniu sumowane w buforze akumulacyjnym. Efekt uzyskany przy 16 rysowanych ramkach przedstawia rysunek 3. Metoda ta daje pełną kontrolę nad jakością uzyskanego efektu rozmycia w ruchu, ale zwielokrotnienie rysowania sceny bezpośrednio wpływa na spadek szybkości renderingu.
Aby udostępnić bufor akumulacyjny trzeba go dodać przy inicjalizacji bufora ramki. W przypadku biblioteki GLUT sprowadza się to do dodania stałej GLUT ACCUM przy wywołaniu funkcji glutInitDisplayMode.
Plik rozmycie_w_ruchu.cpp
#include <GL/glut.h>
#include <stdlib.h>
#include "colors.h"
enum
{
MOTION_BLUR_1,
MOTION_BLUR_2,
MOTION_BLUR_3,
FULL_WINDOW,
ASPECT_1_1,
EXIT
};
int aspect = FULL_WINDOW;
const GLdouble left = - 2.0;
const GLdouble right = 2.0;
const GLdouble bottom = - 2.0;
const GLdouble top = 2.0;
const GLdouble near = 3.0;
const GLdouble far = 7.0;
GLfloat rotatex = 20.0;
GLfloat rotatey = 20.0;
GLfloat rotatez = 20.0;
GLint WORLD_LIST;
int motion_blur = MOTION_BLUR_1;
void DisplayScene()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glEnable( GL_DEPTH_TEST );
glEnable( GL_LIGHTING );
glEnable( GL_LIGHT0 );
glEnable( GL_NORMALIZE );
glEnable( GL_COLOR_MATERIAL );
glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );
glTranslatef( 0, 0, -( near + far ) / 2 );
glRotatef( rotatex, 1.0, 0.0, 0.0 );
glRotatef( rotatey, 0.0, 1.0, 0.0 );
glRotatef( rotatez, 0.0, 0.0, - 1.0 );
glCallList( WORLD_LIST );
switch( motion_blur )
{
case MOTION_BLUR_1:
glAccum( GL_MULT, 0.9 );
glAccum( GL_ACCUM, 0.1 );
glAccum( GL_RETURN, 1.0 );
break;
case MOTION_BLUR_2:
glAccum( GL_ACCUM, 0.1 );
glAccum( GL_RETURN, 1.0 );
glAccum( GL_LOAD, 0.9 );
break;
case MOTION_BLUR_3:
glAccum( GL_LOAD, 0.5 );
int count = 15;
for( int i = 1; i <= count; i++ )
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glRotatef( - 1.0, 1.0, 0.0, 0.0 );
glRotatef( 1.0, 0.0, 1.0, 0.0 );
glRotatef( - 1.0, 0.0, 0.0, - 1.0 );
glCallList( WORLD_LIST );
glAccum( GL_ACCUM, 0.5 /( float ) count );
}
glAccum( GL_RETURN, 1.0 );
break;
}
glFlush();
glutSwapBuffers();
}
void Timer( int value )
{
rotatex++;
rotatey--;
rotatez++;
DisplayScene();
glutTimerFunc( 20, Timer, 0 );
}
void Reshape( int width, int height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
if( aspect == ASPECT_1_1 )
{
if( width < height && width > 0 )
glFrustum( left, right, bottom * height / width, top * height / width, near, far );
else
if( width >= height && height > 0 )
glFrustum( left * width / height, right * width / height, bottom, top, near, far );
}
else
glFrustum( left, right, bottom, top, near, far );
glClearAccum( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_ACCUM_BUFFER_BIT );
DisplayScene();
}
void Menu( int value )
{
switch( value )
{
case FULL_WINDOW:
aspect = FULL_WINDOW;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case ASPECT_1_1:
aspect = ASPECT_1_1;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case EXIT:
exit( 0 );
}
}
void Keyboard( unsigned char key, int x, int y )
{
switch( key )
{
case '1':
glClear( GL_ACCUM_BUFFER_BIT );
motion_blur = MOTION_BLUR_1;
break;
case '2':
glClear( GL_ACCUM_BUFFER_BIT );
motion_blur = MOTION_BLUR_2;
break;
case '3':
glClear( GL_ACCUM_BUFFER_BIT );
motion_blur = MOTION_BLUR_3;
break;
}
DisplayScene();
}
void GenerateDisplayList()
{
WORLD_LIST = glGenLists( 1 );
glNewList( WORLD_LIST, GL_COMPILE );
glColor4fv( Blue );
glutSolidTorus( 0.5, 1.5, 50, 40 );
glEndList();
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_ACCUM );
glutInitWindowSize( 500, 500 );
glutCreateWindow( "Rozmycie w ruchu" );
glutDisplayFunc( DisplayScene );
glutReshapeFunc( Reshape );
glutKeyboardFunc( Keyboard );
int MenuAspect = glutCreateMenu( Menu );
#ifdef WIN32
glutAddMenuEntry( "Aspekt obrazu - całe okno", FULL_WINDOW );
#else
glutAddMenuEntry( "Aspekt obrazu - cale okno", FULL_WINDOW );
#endif
glutAddMenuEntry( "Aspekt obrazu 1:1", ASPECT_1_1 );
glutCreateMenu( Menu );
#ifdef WIN32
glutAddSubMenu( "Aspekt obrazu", MenuAspect );
glutAddMenuEntry( "Wyjście", EXIT );
#else
glutAddSubMenu( "Aspekt obrazu", MenuAspect );
glutAddMenuEntry( "Wyjscie", EXIT );
#endif
glutAttachMenu( GLUT_RIGHT_BUTTON );
GenerateDisplayList();
glutTimerFunc( 20, Timer, 0 );
glutMainLoop();
return 0;
}
Drugi program przykładowy (plik glebia_ostrosci.cpp) przedstawia jedną z możliwych realizacji efektu głębi ostrości (ang. depth of field). Idea pomysłu jest bardzo prosta i sprowadza się do wielokrotnego rysowania obiektów sceny, za każdym razem nieco przesuniętych w stosunku do podstawowego obrazu. Odpowiedni dobór przesunięcia powoduje, że obiekty położone bliżej (rysunek 4) są bardziej „ostre” od obiektów położonych dalej (rysunek 4). Program umożliwia wybór jakości uzyskanego efektu, przy czym lepsza jakość związana jest niestety z większą ilością wykonywanych obliczeń.
Podobna technika może zostać wykorzystania do uzyskania efektu antyaliasingu całej sceny, ale to przedstawimy w następnym odcinku kursu.
Plik glebia_ostrosci.cpp
#include <GL/glut.h>
#include <stdlib.h>
#include "colors.h"
enum
{
DOF_3,
DOF_5,
DOF_7,
FULL_WINDOW,
ASPECT_1_1,
EXIT
};
int aspect = FULL_WINDOW;
const GLdouble left = - 2.0;
const GLdouble right = 2.0;
const GLdouble bottom = - 2.0;
const GLdouble top = 2.0;
const GLdouble near = 1.0;
const GLdouble far = 11;
GLfloat z = 0;
int depth_of_field = DOF_3;
int dof_quality = 1;
void DisplayScene()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glEnable( GL_DEPTH_TEST );
glEnable( GL_LIGHTING );
glEnable( GL_LIGHT0 );
glEnable( GL_COLOR_MATERIAL );
glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );
glEnable( GL_NORMALIZE );
int width = glutGet( GLUT_WINDOW_WIDTH );
int height = glutGet( GLUT_WINDOW_HEIGHT );
int min = - dof_quality;
int max = - min + 1;
GLfloat count =( max - min ) *( max - min );
glClear( GL_ACCUM_BUFFER_BIT );
for( int y = min; y < max; y++ )
for( int x = min; x < max; x++ )
{
GLfloat dx = x * 0.02;
GLfloat dy = y * 0.02;
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
if( aspect == ASPECT_1_1 )
{
if( width < height && width > 0 )
glFrustum( left + dx, right + dx,
bottom * height / width + dy, top * height / width + dy,
near, far );
else
if( width >= height && height > 0 )
glFrustum( left * width / height + dx,
right * width / height + dy, bottom + dy, top + dy,
near, far );
}
else
glFrustum( left + dx, right + dx, bottom + dy, top + dy, near, far );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0, 0, -( near + far ) / 2 );
glTranslatef( x * 0.02, y * 0.02, 0.0 );
glColor4fv( Green );
glTranslatef( 0.0, 0.0, z );
glutSolidTorus( 1.0, 2.5, 50, 40 );
glAccum( GL_ACCUM, 1.0 / count );
}
glAccum( GL_RETURN, 1.0 );
glFlush();
glutSwapBuffers();
}
void Reshape( int width, int height )
{
glViewport( 0, 0, width, height );
DisplayScene();
}
void Keyboard( unsigned char key, int x, int y )
{
if( key == '+' )
z += 0.1;
else
if( key == '-' )
z -= 0.1;
DisplayScene();
}
void Menu( int value )
{
switch( value )
{
case DOF_3:
depth_of_field = DOF_3;
dof_quality = 1;
DisplayScene();
break;
case DOF_5:
depth_of_field = DOF_5;
dof_quality = 2;
DisplayScene();
break;
case DOF_7:
depth_of_field = DOF_7;
dof_quality = 3;
DisplayScene();
break;
case FULL_WINDOW:
aspect = FULL_WINDOW;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case ASPECT_1_1:
aspect = ASPECT_1_1;
Reshape( glutGet( GLUT_WINDOW_WIDTH ), glutGet( GLUT_WINDOW_HEIGHT ) );
break;
case EXIT:
exit( 0 );
}
}
int main( int argc, char * argv[] )
{
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_ACCUM );
glutInitWindowSize( 500, 500 );
#ifdef WIN32
glutCreateWindow( "Głębia ostrości" );
#else
glutCreateWindow( "Glebia ostrosci" );
#endif
glutDisplayFunc( DisplayScene );
glutReshapeFunc( Reshape );
glutKeyboardFunc( Keyboard );
int MenuDOF = glutCreateMenu( Menu );
glutAddMenuEntry( "Bufor akumulacyjny 3x3", DOF_3 );
glutAddMenuEntry( "Bufor akumulacyjny 5x5", DOF_5 );
glutAddMenuEntry( "Bufor akumulacyjny 7x7", DOF_7 );
int MenuAspect = glutCreateMenu( Menu );
#ifdef WIN32
glutAddMenuEntry( "Aspekt obrazu - całe okno", FULL_WINDOW );
#else
glutAddMenuEntry( "Aspekt obrazu - cale okno", FULL_WINDOW );
#endif
glutAddMenuEntry( "Aspekt obrazu 1:1", ASPECT_1_1 );
glutCreateMenu( Menu );
#ifdef WIN32
glutAddSubMenu( "Głębia ostrości", MenuDOF );
glutAddSubMenu( "Aspekt obrazu", MenuAspect );
glutAddMenuEntry( "Wyjście", EXIT );
#else
glutAddSubMenu( "Glebia ostrosci", MenuDOF );
glutAddSubMenu( "Aspekt obrazu", MenuAspect );
glutAddMenuEntry( "Wyjscie", EXIT );
#endif
glutAttachMenu( GLUT_RIGHT_BUTTON );
glutIdleFunc( DisplayScene );
glutMainLoop();
return 0;
}
Trzeci i ostatni program przykładowy (plik filtracja_obrazow.cpp) przedstawia sposób wykorzystania bufora akumulacyjngo do filtracji obrazów przy użyciu filtrów splotowych. Zbiór filtrów jest taki sam jak używany w programie prezentującym opcjonalny podzbiór przetwarzania obrazów (plik filters.h).
Efekt działania filtra splotowego uzyskiwany jest poprzez wielokrotne rysowanie filtrowanego obrazu przy czym za każdym razem zmieniane jest położenie rysowanego obrazu i odpowiednio modyfikowany współczynnik skalowania składowych RGB obrazu. Z uwagi na specyfikę działania bufora akumulacyjnego maski filtra muszą być tak przeskalowane, aby ich wartość bezwzględna nie była większa o jednego. Drugim ważnym elementem, jest kolejność nakładania poszczególnych obrazów, bowiem w przypadku przekroczenia przez składową bufora akumulacyjnego przedziału wartości [−1, 1], nastąpią przekłamania w końcowym wyniku filtracji. Obie powyższe operacje wykonywane są każdorazowo przy wyborze filtra (funkcja Menu).
Efekty działania programu nie różną się w żaden sposób od działania wspomnianego wyżej programu wykorzystującego opcjonalny podzbiór funkcji przetwarzania obrazów. Efekt działania wybranych filtrów przedstawiono na rysunkach 6 - 10.
Plik filtracja_obrazow.cpp
#include <GL/glut.h>
#include <GL/glext.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "targa.h"
#include "filters.h"
enum
{
NONE,
AVERAGE,
LP1,
LP2,
LP3,
GAUSS,
MEAN_REMOVAL,
HP1,
HP2,
HP3,
HORIZONTAL,
VERTICAL,
HORIZONTAL_VERTICAL,
GRADIENT_EAST,
GRADIENT_SOUTH_EAST,
GRADIENT_SOUTH,
GRADIENT_SOUTH_WEST,
GRADIENT_WEST,
GRADIENT_NORTH_WEST,
GRADIENT_NORTH,
GRADIENT_NORTH_EAST,
EMBOSS_EAST,
EMBOSS_SOUTH_EAST,
EMBOSS_SOUTH,
EMBOSS_SOUTH_WEST,
EMBOSS_WEST,
EMBOSS_NORTH_WEST,
EMBOSS_NORTH,
EMBOSS_NORTH_EAST,
LAPLACIAN_LAPL1,
LAPLACIAN_LAPL2,
LAPLACIAN_LAPL3,
LAPLACIAN_DIAGONAL,
LAPLACIAN_HORIZONTAL,
LAPLACIAN_VERTICAL,
SOBEL_HORIZONTAL,
SOBEL_VERTICAL,
PREWITT_HORIZONTAL,
PREWITT_VERTICAL,
SAVE_FILE,
EXIT
};
GLsizei width;
GLsizei height;
GLenum format;
GLenum type;
GLvoid * pixels;
GLfloat neutral[ 9 ] =
{
0, 0, 0,
0, 1, 0,
0, 0, 0
};
GLfloat filter[ 9 ] =
{
0, 0, 0,
0, 1, 0,
0, 0, 0
};
GLfloat scale = 1.0;
int dir_h[ 9 ] =
{
2, 0, 0, 2, 1, 0, 1, 2, 1
};
int dir_w[ 9 ] =
{
0, 0, 2, 2, 0, 1, 2, 1, 1
};
void LoadTARGA( int argc, char * argv[] )
{
if( argc < 2 )
{
printf( "Brak nazwy pliku TARGA\n" );
exit( 1 );
}
if( !load_targa( argv[ 1 ], width, height, format, type, pixels ) )
{
#ifdef WIN32
printf( "Błąd odczytu lub błędny format pliku: %s\n", argv[ 1 ] );
#else
printf( "Blad odczytu lub bledny format pliku: %s\n", argv[ 1 ] );
#endif
exit( 1 );
}
}
void CheckImageFormat()
{
if( format == GL_BGR || format == GL_BGRA )
{
const char * version =( char * ) glGetString( GL_VERSION );
int major = 0, minor = 0;
if( sscanf( version, "%d.%d", & major, & minor ) != 2 )
{
#ifdef WIN32
printf( "Błędny format wersji OpenGL\n" );
#else
printf( "Bledny format wersji OpenGL\n" );
#endif
exit( 1 );
}
if( major <= 1 && minor < 2 )
{
if( !glutExtensionSupported( "GL_EXT_bgra" ) )
{
printf( "Brak rozszerzenia GL_EXT_bgra\n" );
exit( 1 );
}
}
}
}
void SaveTARGA( GLenum format )
{
glPixelStorei( GL_PACK_ALIGNMENT, 1 );
GLvoid * pixels;
GLsizei width = glutGet( GLUT_WINDOW_WIDTH );
GLsizei height = glutGet( GLUT_WINDOW_HEIGHT );
if( format == GL_BGRA )
pixels = new unsigned char[ width * height * 4 ];
else
if( format == GL_BGR )
pixels = new unsigned char[ width * height * 3 ];
else
if( format == GL_LUMINANCE )
pixels = new unsigned char[ width * height ];
else
return;
if( format == GL_LUMINANCE )
{
glPixelTransferf( GL_RED_SCALE, 0.229 );
glPixelTransferf( GL_GREEN_SCALE, 0.587 );
glPixelTransferf( GL_BLUE_SCALE, 0.114 );
}
glReadPixels( 0, 0, width, height, format, GL_UNSIGNED_BYTE, pixels );
if( format == GL_LUMINANCE )
{
glPixelTransferf( GL_RED_SCALE, 1.0 );
glPixelTransferf( GL_GREEN_SCALE, 1.0 );
glPixelTransferf( GL_BLUE_SCALE, 1.0 );
}
if( format == GL_BGRA )
save_targa( "test_BGRA.tga", width, height, format, type, pixels );
else
if( format == GL_BGR )
save_targa( "test_BGR.tga", width, height, format, type, pixels );
else
if( format == GL_LUMINANCE )
save_targa( "test_LUMINANCE.tga", width, height, format, type, pixels );
delete[]( unsigned char * ) pixels;
}
void Display()
{
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClearAccum( 0.0, 0.0, 0.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT | GL_ACCUM_BUFFER_BIT );
int win_width = glutGet( GLUT_WINDOW_WIDTH );
int win_height = glutGet( GLUT_WINDOW_HEIGHT );
const int whmax = 3;
for( int i = 0; i < whmax * whmax; i++ )
{
glViewport( - dir_w[ i ], - dir_h[ i ], win_width - dir_w[ i ], win_height - dir_h[ i ] );
glRasterPos2i( 0, 0 );
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
glPixelZoom(( float ) win_width /( float ) width,( float ) win_height /( float ) height );
glDrawPixels( width, height, format, type, pixels );
glAccum( GL_ACCUM, filter[ dir_w[ i ] + dir_h[ i ] * whmax ] );
}
glViewport( 0, 0, win_width, win_height );
glAccum( GL_RETURN, scale );
glFinish();
glutSwapBuffers();
}
void Reshape( int Width, int Height )
{
glViewport( 0, 0, Width, Height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluOrtho2D( 0.0, Width, 0.0, Height );
Display();
}
void Menu( int value )
{
GLfloat * filters[ 39 ] =
{
neutral, average, lp1, lp2, lp3, gauss, mean_removal, hp1, hp2,
hp3, horizontal, vertical, horizontal_vertical, gradient_east,
gradient_south_east, gradient_south, gradient_south_west,
gradient_west, gradient_north_west, gradient_north,
gradient_north_east, emboss_east, emboss_south_east, emboss_south,
emboss_south_west, emboss_west, emboss_north_west, emboss_north,
emboss_north_east, laplacian_lapl1, laplacian_lapl2,
laplacian_lapl3, laplacian_diagonal, laplacian_horizontal,
laplacian_vertical, sobel_horizontal, sobel_vertical,
prewitt_horizontal, prewitt_vertical
};
enum
{
HW, HW_REV, CROSS, CROSS_REV
};
const int hw_h[ 9 ] =
{
0, 0, 0, 1, 1, 1, 2, 2, 2
};
const int hw_w[ 9 ] =
{
0, 1, 2, 0, 1, 2, 0, 1, 2
};
const int hw_rev_h[ 9 ] =
{
2, 2, 2, 1, 1, 1, 0, 0, 0
};
const int hw_rev_w[ 9 ] =
{
2, 1, 0, 2, 1, 0, 2, 1, 0
};
const int cross_rev_h[ 9 ] =
{
2, 0, 0, 2, 1, 0, 1, 2, 1
};
const int cross_rev_w[ 9 ] =
{
0, 0, 2, 2, 0, 1, 2, 1, 1
};
const int cross_h[ 9 ] =
{
0, 2, 2, 0, 1, 2, 1, 0, 1
};
const int cross_w[ 9 ] =
{
0, 0, 2, 2, 0, 1, 2, 1, 1
};
int directions[ 39 ] =
{
CROSS, CROSS, CROSS,
CROSS, CROSS, CROSS,
CROSS, CROSS, CROSS,
CROSS, CROSS, CROSS,
CROSS, CROSS, CROSS,
CROSS, CROSS, CROSS,
CROSS, CROSS, CROSS,
HW, CROSS, CROSS,
CROSS_REV, HW_REV, CROSS,
CROSS, CROSS_REV, CROSS,
CROSS, CROSS, CROSS,
CROSS, CROSS, CROSS,
CROSS, CROSS, CROSS
};
switch( value )
{
case SAVE_FILE:
SaveTARGA( format );
break;
case NONE:
case AVERAGE:
case LP1:
case LP2:
case LP3:
case GAUSS:
case MEAN_REMOVAL:
case HP1:
case HP2:
case HP3:
case HORIZONTAL:
case VERTICAL:
case HORIZONTAL_VERTICAL:
case GRADIENT_EAST:
case GRADIENT_SOUTH_EAST:
case GRADIENT_SOUTH:
case GRADIENT_SOUTH_WEST:
case GRADIENT_WEST:
case GRADIENT_NORTH_WEST:
case GRADIENT_NORTH:
case GRADIENT_NORTH_EAST:
case EMBOSS_EAST:
case EMBOSS_SOUTH_EAST:
case EMBOSS_SOUTH:
case EMBOSS_SOUTH_WEST:
case EMBOSS_WEST:
case EMBOSS_NORTH_WEST:
case EMBOSS_NORTH:
case EMBOSS_NORTH_EAST:
case LAPLACIAN_LAPL1:
case LAPLACIAN_LAPL2:
case LAPLACIAN_LAPL3:
case LAPLACIAN_DIAGONAL:
case LAPLACIAN_HORIZONTAL:
case LAPLACIAN_VERTICAL:
case SOBEL_HORIZONTAL:
case SOBEL_VERTICAL:
case PREWITT_HORIZONTAL:
case PREWITT_VERTICAL:
switch( directions[ value ] )
{
case CROSS:
memcpy( dir_h, cross_h, 9 * sizeof( int ) );
memcpy( dir_w, cross_w, 9 * sizeof( int ) );
break;
case CROSS_REV:
memcpy( dir_h, cross_rev_h, 9 * sizeof( int ) );
memcpy( dir_w, cross_rev_w, 9 * sizeof( int ) );
break;
case HW:
memcpy( dir_h, hw_h, 9 * sizeof( int ) );
memcpy( dir_w, hw_w, 9 * sizeof( int ) );
break;
case HW_REV:
memcpy( dir_h, hw_rev_h, 9 * sizeof( int ) );
memcpy( dir_w, hw_rev_w, 9 * sizeof( int ) );
break;
}
memcpy( filter, filters[ value ], 9 * sizeof( GLfloat ) );
GLfloat absmax = fabs( filter[ 0 ] );
for( int i = 1; i < 9; i++ )
if( fabs( filter[ i ] ) > absmax )
absmax = fabs( filter[ i ] );
if( absmax > 1.0 )
for( int i = 0; i < 9; i++ )
filter[ i ] /= absmax;
if( absmax > 1.0 )
scale = absmax;
else
scale = 1.0;
Display();
break;
case EXIT:
exit( 0 );
break;
}
}
int main( int argc, char * argv[] )
{
LoadTARGA( argc, argv );
glutInit( & argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_ACCUM );
glutInitWindowSize( width, height );
glutCreateWindow( argv[ 1 ] );
glutDisplayFunc( Display );
glutReshapeFunc( Reshape );
CheckImageFormat();
int MenuFilters = glutCreateMenu( Menu );
#ifdef WIN32
glutAddMenuEntry( "Filtr uśredniający", AVERAGE );
glutAddMenuEntry( "Filtr LP1", LP1 );
glutAddMenuEntry( "Filtr LP2", LP2 );
glutAddMenuEntry( "Filtr LP3", LP3 );
glutAddMenuEntry( "Filtr Gaussa", GAUSS );
glutAddMenuEntry( "Filtr usuwający średnią", MEAN_REMOVAL );
glutAddMenuEntry( "Filtr HP1", HP1 );
glutAddMenuEntry( "Filtr HP2", HP2 );
glutAddMenuEntry( "Filtr HP3", HP3 );
glutAddMenuEntry( "Filtr poziomy", HORIZONTAL );
glutAddMenuEntry( "Filtr pionowy", VERTICAL );
glutAddMenuEntry( "Filtr poziomy/pionowy", HORIZONTAL_VERTICAL );
glutAddMenuEntry( "Filtr gradientowy wschód", GRADIENT_EAST );
glutAddMenuEntry( "Filtr gradientowy południowy wschód", GRADIENT_SOUTH_EAST );
glutAddMenuEntry( "Filtr gradientowy południe", GRADIENT_SOUTH );
glutAddMenuEntry( "Filtr gradientowy południowy zachód", GRADIENT_SOUTH_WEST );
glutAddMenuEntry( "Filtr gradientowy zachód", GRADIENT_WEST );
glutAddMenuEntry( "Filtr gradientowy północny zachód", GRADIENT_NORTH_WEST );
glutAddMenuEntry( "Filtr gradientowy północ", GRADIENT_NORTH );
glutAddMenuEntry( "Filtr gradientowy północny wschód", GRADIENT_NORTH_EAST );
glutAddMenuEntry( "Filtr uwypuklający wschód", EMBOSS_EAST );
glutAddMenuEntry( "Filtr uwypuklający południowy wschód", EMBOSS_SOUTH_EAST );
glutAddMenuEntry( "Filtr uwypuklający południe", EMBOSS_SOUTH );
glutAddMenuEntry( "Filtr uwypuklający południowy zachód", EMBOSS_SOUTH_WEST );
glutAddMenuEntry( "Filtr uwypuklający zachód", EMBOSS_WEST );
glutAddMenuEntry( "Filtr uwypuklający północny zachód", EMBOSS_NORTH_WEST );
glutAddMenuEntry( "Filtr uwypuklający północ", EMBOSS_NORTH );
glutAddMenuEntry( "Filtr uwypuklający północny wschód", EMBOSS_NORTH_EAST );
glutAddMenuEntry( "Filtr Laplace'a LAPL1", LAPLACIAN_LAPL1 );
glutAddMenuEntry( "Filtr Laplace'a LAPL2", LAPLACIAN_LAPL2 );
glutAddMenuEntry( "Filtr Laplace'a LAPL3", LAPLACIAN_LAPL3 );
glutAddMenuEntry( "Filtr Laplace'a skośny", LAPLACIAN_DIAGONAL );
#else
glutAddMenuEntry( "Filtr uśredniający", AVERAGE );
glutAddMenuEntry( "Filtr LP1", LP1 );
glutAddMenuEntry( "Filtr LP2", LP2 );
glutAddMenuEntry( "Filtr LP3", LP3 );
glutAddMenuEntry( "Filtr Gaussa", GAUSS );
glutAddMenuEntry( "Filtr usuwajacy srednia", MEAN_REMOVAL );
glutAddMenuEntry( "Filtr HP1", HP1 );
glutAddMenuEntry( "Filtr HP2", HP2 );
glutAddMenuEntry( "Filtr HP3", HP3 );
glutAddMenuEntry( "Filtr poziomy", HORIZONTAL );
glutAddMenuEntry( "Filtr pionowy", VERTICAL );
glutAddMenuEntry( "Filtr poziomy/pionowy", HORIZONTAL_VERTICAL );
glutAddMenuEntry( "Filtr gradientowy wschod", GRADIENT_EAST );
glutAddMenuEntry( "Filtr gradientowy poludniowy wschod", GRADIENT_SOUTH_EAST );
glutAddMenuEntry( "Filtr gradientowy poludnie", GRADIENT_SOUTH );
glutAddMenuEntry( "Filtr gradientowy poludniowy zachod", GRADIENT_SOUTH_WEST );
glutAddMenuEntry( "Filtr gradientowy zachod", GRADIENT_WEST );
glutAddMenuEntry( "Filtr gradientowy polnocny zachod", GRADIENT_NORTH_WEST );
glutAddMenuEntry( "Filtr gradientowy polnoc", GRADIENT_NORTH );
glutAddMenuEntry( "Filtr gradientowy polnocny wschod", GRADIENT_NORTH_EAST );
glutAddMenuEntry( "Filtr uwypuklajacy wschod", EMBOSS_EAST );
glutAddMenuEntry( "Filtr uwypuklajacy poludniowy wschod", EMBOSS_SOUTH_EAST );
glutAddMenuEntry( "Filtr uwypuklajacy poludnie", EMBOSS_SOUTH );
glutAddMenuEntry( "Filtr uwypuklajacy poludniowy zachod", EMBOSS_SOUTH_WEST );
glutAddMenuEntry( "Filtr uwypuklajacy zachod", EMBOSS_WEST );
glutAddMenuEntry( "Filtr uwypuklajacy polnocny zachod", EMBOSS_NORTH_WEST );
glutAddMenuEntry( "Filtr uwypuklajacy polnoc", EMBOSS_NORTH );
glutAddMenuEntry( "Filtr uwypuklajacy polnocny wschod", EMBOSS_NORTH_EAST );
glutAddMenuEntry( "Filtr Laplace'a LAPL1", LAPLACIAN_LAPL1 );
glutAddMenuEntry( "Filtr Laplace'a LAPL2", LAPLACIAN_LAPL2 );
glutAddMenuEntry( "Filtr Laplace'a LAPL3", LAPLACIAN_LAPL3 );
glutAddMenuEntry( "Filtr Laplace'a skosny", LAPLACIAN_DIAGONAL );
#endif
glutAddMenuEntry( "Filtr Laplace'a poziomy", LAPLACIAN_HORIZONTAL );
glutAddMenuEntry( "Filtr Laplace'a pionowy", LAPLACIAN_VERTICAL );
glutAddMenuEntry( "Filtr poziomy Sobela", SOBEL_HORIZONTAL );
glutAddMenuEntry( "Filtr pionowy Sobela", SOBEL_VERTICAL );
glutAddMenuEntry( "Filtr poziomy Prewitta", PREWITT_HORIZONTAL );
glutAddMenuEntry( "Filtr pionowy Prewitta", PREWITT_VERTICAL );
glutAddMenuEntry( "Brak", NONE );
glutCreateMenu( Menu );
glutAddSubMenu( "Filtry", MenuFilters );
glutAddMenuEntry( "Zapisz", SAVE_FILE );
#ifdef WIN32
glutAddMenuEntry( "Wyjście", EXIT );
#else
glutAddMenuEntry( "Wyjscie", EXIT );
#endif
glutAttachMenu( GLUT_RIGHT_BUTTON );
glutMainLoop();
return 0;
}