Computação Gráfica
Manipulação de Imagens
em OpenGL e C++
Prof. Márcio Sarroglia Pinho 

O conteúdo desta página está baseado no material criado
pela professora Isabel Harb Manssour.


Antes de iniciar esta aula, certifique-se que o Visual C está configurado corretamente para utilizar a OpenGL.
Caso não esteja, siga as instruções desta página.



Introdução


Faça o download do arquivo VisualCImagens.zip.

Este arquivo contém os fontes que devem ser adicionados no projeto e algumas imagens que serão usadas para teste da aplicação.
Atenção: as imagens deverão estar na pasta do projeto, junto com as DLLs
.



Biblioteca de Acesso a Imagens

A bilioteca de carga de Imagens é composta de dois fontes: ImageClassNEW2.cpp e BmpLib0.cpp. Ambos devem ser adicionados ao projeto utilizano na configuração do OpenGL no Visual C.

O arquivo
BmpLib0.cpp faz a leitura e a gravação de  imagens no formato BMP de 24bits. Estas funções são usadas pela classe ImageClass, cujos métodos são descritos a seguir.

O programa que utiliza esta classe para exibir imagens em OpenGL está descrito mais abaixo.


Métodos da Biblioteca ImageClass

Método Descrição
ImageClass(void) Construtora da classe. Não define um tamanho para a imagem. Posteriormente o programa deve carregar um arquivo de imagem.
ImageClass(int sizeX, int sizeY) Construtora da classe. Cria uma imagem que terá um tamanho pré-definido.
int Load(char *); Carrega uma imagem JPG de disco.
void Display(void); Exibe a imagem na tela. Deve ser usado somente dentro do método que trata o evento de redesenho da tela, em OpenGL.  
void Delete(void); Libera a área de memoria criada para armazenar a imagem. Não remove o objetoda memória. A partir da execução deste método pode-se executar novamente o método Load para carregar um arquivo de imagem.
void DrawPixel(int x, int y, unsigned char r, unsigned char g, unsigned char b); Coloca um pixel de cor (r,g,b) na coordenada (x,y) da imagem. O pixel só aparece na tela quando da próxima execução do  método Display.
void DrawLineH(int y, int x1, int x2, unsigned char r, unsigned char g, unsigned char b); Traça uma linha horizontal entre os pontos (x1,y) e (x2,y), com a cor (r,g,b). A linha só aparece na tela quando da próxima execução do  método Display.
void DrawLineV(int x, int y1, int y2,unsigned char r, unsigned char g, unsigned char b ); Traça uma linha vertical entre os pontos (x,y1) e (x,y2), com a cor (r,g,b). A linha só aparece na tela quando da próxima execução do  método Display.
void ReadPixel(GLint x, GLint y, unsigned char &r, unsigned char &g, unsigned char &b); Lê da imagem a cor (r,g,b) do pixel que está na coordenada (x,y) da imagem. 
int GetPointIntensity(int x, int y); Retorna o nível de intensidade da cor do pixel (x,y). O valor retornado pode ser considerado como o nível de cinza do ponto (x,y).
int SizeX(); Informa a largura da imagem em pixels.
int SizeY(); Informa a altura da imagem em pixels.
void Clear(); Limpa o conteúdo da imagem, pintado o fundo de branco.  O resultado deste método só aparece na tela quando da próxima execução do  método Display.
void SetPos(int X, int Y); Posiciona o canto superior direito da imagem na coordenada (x,y) da janela.
void SetZoomH(float H);
void SetZoomV(float V);
Métodos que definem o nível de zoom para a exibição da imagem. A imagem só é atualizada na tela quando da próxima execução do  método Display.
float GetZoomH();
float GetZoomV(); 
Métodos que informam o nível de zoom que stá sendo usado para a exibição da imagem.


Exemplo de utilização da classe ImageClass

Para testar a classe ImageClass observe o fonte ImageTestNEW.cpp.  

No inicio deste fonte são definidos dois objetos da classe ImageClass:

#include <iostream>

#include <windows.h>
#include "gl\glut.h"
#include "ImageClassNew2.h"


ImageClass *Image, *NovaImagem;

Na função
main são definidos os tratadores de eventos do programa. Veja os comentários no trecho de programa abaixo.

int  main ( int argc, char** argv )  
{
    glutInit            ( &argc, argv );
    glutInitDisplayMode (GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB );
    glutInitWindowPosition (0,0);

    // Define o tamanho da janela grafica do programa
    glutInitWindowSize  ( 650, 500);


    // Cria a janela na tela, definindo o nome da
    // que aparecera na barra de título da janela.
    glutCreateWindow    ( "Manipula imagens" );
       
    // executa algumas inicializações, entre elas a
    // carga da imagem. 
    init ();

   
    // Define que o tratador de evento para
    // o redesenho da tela. A funcao "display"
    // será chamada automaticamente quando
    // for necessário redesenhar a janela   
    glutDisplayFunc ( display ); 


    // Define que o tratador de evento para
    // o redimensionamento da janela. A funcao "
reshape"
    // será chamada automaticamente quando
    // o usuário alterar o tamanho da janela
    glutReshapeFunc ( reshape );


    // Define que o tratador de evento para
    // as teclas. A funcao "
keyboard"
    // será chamada automaticamente sempre 
    // o usuário pressionar uma tecla comum

    glutKeyboardFunc ( keyboard );

    // Define que o tratador de evento para
    // as teclas especiais(F1, F2,... ALT-A,
    // ALT-B, Teclas de Seta, ...).
    // A funcao "
arrow_keys" será chamada
    // automaticamente sempre o usuário
    // pressionar uma tecla especial

    glutSpecialFunc ( arrow_keys );

    // inicia o tratamento dos eventos

    glutMainLoop ( );      
  

    return 0;
}

A rotina init presente no código acima, realiza a carga de uma imagem e faz algumas outras inicializações.

void init(void)
{

    Image = new ImageClass();

    // carrega um imagem

    int r;

    r = Image->Load("Aguia.bmp");

    if (!r) // não foi possivel carregar a imagem
        exit(1);

   
// Limpa a tela com a cor azul
    glClearColor(0.0f, 0.0f, 1.0f, 1.0f); 

    // Instancia o objeto que ira exibir a nova imagem
    // Caso precise alterar o tamanho da nova imagem, mude os parametros
    // do construtor, na chamada abaixo
    NovaImagem = new ImageClass(Image->SizeX(), Image->SizeY());
   
    // Posiciona a imagem nova ao lado da antiga
    NovaImagem->SetPos(Image->SizeX()+10, 0);

    // Ajusta a 
largura da tela para a
    // acomodar as duas imagens lado a lado
   
glutReshapeWindow ( Image->SizeX()*2+20, 500 );
}


A seguir, são comentados os tratadores alguns dos eventos.

Tratador de evento de teclado

Este tratador de evento é chamado toda vez que uma tecla for pressionada. O parâmetro "key"  informa qual foi a tecla pressionada e os parâmetros x,y informa a posção do mouse sobre a janela no momento em que este evento ocorreu.

Importante: caso haja alguma modificação na imagem dentro deste tratador de evento, é obrigatória a chamada da função "
glutPostRedisplay()". Esta função irá forçar o sistema de gerência de janela a chamar o tratador de eventos responsável pela exibição da iamgem na tela. Caso isto não seja feito, a tela não será atualizada.
 
void keyboard ( unsigned char key, int x, int y ) 
{
    // Pode chamar aqui os metodos que fazem o processamento da imagem
   
    switch ( key )
    {
        case 27:        // Termina o programa qdo
            exit ( 0 );   // a tecla ESC for pressionada
            break;
        case '2':
            NovaImagem->Clear(); // limpa a imagem que fica à direita da tela
            ConvertBlackAndWhite();
            glutPostRedisplay();  // obrigatorio para redesenhar a tela
            break;  
        default:       
            break;
    }
}


De forma similar a a função "keyboard", existe o tratador de teclas especiais. Veja a função "
arrow_keys" para maiores detalhes.

Tratador de evento de redesenho

Sempre que for necessário redesenhar a janela, o tratador de eventos "display" será ativado. Este evento é chamado automaticamente pelo sistema operacional quando, por exemplo, a janela está 'embaixo' de outra e por alguma razão passa 'para cima'.
Além disto, o programa deve forçar a ativação deste tratator sempre que fizer alguma alteração nas imagens que aparecem na janela. Esta ativação entretanto, deve ser feita a partir da chamada da função
"glutPostRedisplay()". Note que o programa nunca deve chamar a rotina "display()" de forma direta.



Atividades

Compile e execute o programa. Pressio a tecla 2. Além disso, a única opção implementada é a tecla ESC para sair do programa. Ao ser pressionada qualquer tecla, o programa exibe uma nova imagem ao lado da original (que por enquanto é igual a ela). 

Observe atentamente o código para entender o seu funcionamento. Depois realize as tarefas 1 à 7 especificadas ao longo do código. O único arquivo que deve ser alterado é o ImageTestNew.cpp, e a imagem a ser alterada está no atributo nova. Para facilitar, estas tarefas foram listadas a seguir.

Observações:
- para todas as tarefas experimente alterar as imagens pressionando as teclas F6 (avança) e F7 (volta);
- analise o método keyPressed e verifique as teclas que podem ser usadas no programa para testar cada tarefa que for implementada.