//---------------------------------------------------------------------
// Ukazkovy priklad k serii clanku OpenGL Imaging Subset
//
// Autor:          Pavel Tisnovsky
// Cislo clanku:   6
// Cislo prikladu: 2
//
// Tento ukazkovy program nacte z externiho souboru "lena.tga" obrazek,
// ktery posleze zobrazi pomoci funkce glDrawPixels() do okna aplikace.
// Pri prenosu pixelu se provadi filtrace obrazku pomoci predem nasta-
// veneho konvolucniho filtru.
// Zde pouzity filtr provadi Gaussovo prumerovani hodnot na okoli 3x3
// pixely.
// Pomoci klavesy E se povoluje ci zakazuje aplikace konvolucniho filtru.
// Klavesou ESC je mozne program ukoncit.
//---------------------------------------------------------------------



#ifdef __BORLANDC__
#include <windows.h>                                // oprava chyby v nekterych Borlandskych prekladacich
#endif

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/glut.h>                                // hlavickovy soubor funkci GLUTu a OpenGL

#ifdef __BORLANDC__
#pragma hdrstop                                     // konec predkompilovanych hlavicek pro Borlandske prekladace
#endif

#define WINDOW_WIDTH    450                         // velikost okna
#define WINDOW_HEIGHT   450
#define WINDOW_LEFT      30                         // pozice leveho horniho rohu okna na desktopu
#define WINDOW_TOP       30
#define WINDOW_TITLE    "OpenGL Imaging Subset, priklad cislo 6.2" // titulek okna

#define PIXMAP_NAME     "lena.tga"                 // jmeno souboru, ve kterem je ulozena pixmapa

typedef enum {                                      // typ pixelu ulozenych v pixmape
    IndexedPixmap,                                  // indexy do palety
    TrueColorPixmap,                                // format RGB
    TrueColorAlphaPixmap                            // format RGBA
} PixmapType;

typedef struct {                                    // novy datovy typ zapouzdrujici pixmapu
    PixmapType      type;                           // typ ulozenych pixelu
    unsigned int    width;                          // sirka pixmapy v pixelech
    unsigned int    height;                         // vyska pixmapy v pixelech
    unsigned char  *pixels;                         // pole pixelu
} Pixmap;

typedef struct {                                    // novy datovy typ zapouzdrujici velikost okna
    unsigned int    width;
    unsigned int    height;
} Window;

GLboolean filter=GL_TRUE;                           // priznak, zda se ma pouzivat filtrace
Pixmap pixmap;
Window window;



//---------------------------------------------------------------------
// Nacteni pixmapy ze souboru typu TGA
//---------------------------------------------------------------------
int pixmapLoadFromTGA(const char *filename, Pixmap *pixmap)
{
    FILE               *fin;
    unsigned short int width=0, height=0;           // sirka a vyska obrazku
    unsigned short int palettelength;               // delta palety
    unsigned char      bpp=0;                       // pocet bitu na pixel
    int                size;
    unsigned char      tgaHeader[18];               // hlavicka formatu TGA

    if (!filename) return -1;                       // jmeno neni zadane
    fin=fopen(filename, "rb");
    if (!fin) return -1;                            // otevreni souboru se nezdarilo
    if (fread(tgaHeader, 18, 1, fin)!=1) return -1; // nacist hlavicku BMP souboru
    memcpy(&width, tgaHeader+12, 2);                // nacist sirku obrazku v pixelech
    memcpy(&height, tgaHeader+14, 2);               // nacist vysku obrazku v pixelech
    memcpy(&bpp, tgaHeader+16, 1);                  // nacist pocet bitu na pixel
    memcpy(&palettelength, tgaHeader+5, 2);         // nacist delku palety
    pixmap->width=width;
    pixmap->height=height;
    switch (bpp) {                                  // rozeskok podle typu pixmapy
        case 8:                                     // indexy do palety
            size=width*height;
            pixmap->pixels=(unsigned char *)malloc(size*sizeof(unsigned char));
            pixmap->type=IndexedPixmap;
            if (fread(pixmap->pixels, palettelength, 1, fin)!=1) return -1;
            if (fread(pixmap->pixels, size, 1, fin)!=1) return -1;
            fclose(fin);
            break;
        case 24:                                    // format RGB
            size=width*height*3;
            pixmap->pixels=(unsigned char *)malloc(size*sizeof(unsigned char));
            pixmap->type=TrueColorPixmap;
            if (fread(pixmap->pixels, size, 1, fin)!=1) return -1;
            fclose(fin);
            break;
        case 32:                                    // format RGBA
            size=width*height*4;
            pixmap->pixels=(unsigned char *)malloc(size*sizeof(unsigned char));
            pixmap->type=TrueColorAlphaPixmap;
            if (fread(pixmap->pixels, size, 1, fin)!=1) return -1;
            fclose(fin);
            break;
        default:
            break;
    }
    return 0;
}



//---------------------------------------------------------------------
// Tato funkce vykresli retezec zadanym bitmapovym fontem
//---------------------------------------------------------------------
void printGlutBitmapFont(char *string, void *font, int x, int y, float r, float g, float b)
{
    glColor3f(r, g, b);                             // nastaveni barvy vykreslovanych bitmap
    glRasterPos2i(x, y);                            // nastaveni pozice pocatku bitmapy
    while (*string)                                 // projit celym retezcem
        glutBitmapCharacter(font, *string++);       // vykresleni jednoho znaku
}



//---------------------------------------------------------------------
// Tato funkce pripravi dvojrozmerny konvolucni filtr
//---------------------------------------------------------------------
void prepareConvolutionFilter2D(void)
{
    GLfloat filterKernel[3][3] = {                  // jadro konvolucniho filtru
        { (float)1/16,  (float)2/16,  (float)1/16 },
        { (float)2/16,  (float)4/16,  (float)2/16 },// Gaussuv filtr
        { (float)1/16,  (float)2/16,  (float)1/16 }
    };
    glConvolutionFilter2D(GL_CONVOLUTION_2D,        // typ konvolucniho filtru
                          GL_LUMINANCE,             // typ polozek jadra
                          3, 3,                     // rozmery jadra 3x3 polozky
                          GL_LUMINANCE,             // typ polozek jadra
                          GL_FLOAT,                 // datovy typ polozek jadra
                          filterKernel);            // ukazatel na data
}



//---------------------------------------------------------------------
// Funkce pro inicializaci vykreslovani
//---------------------------------------------------------------------
void onInit(void)
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);           // barva pozadi obrazku
    glClearDepth(1.0f);                             // implicitni hloubka ulozena v pameti hloubky
    glShadeModel(GL_FLAT);                          // nastaveni stinovaciho rezimu
    glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE);    // nastavit interni format pixmapy
    glPixelStorei(GL_PACK_ALIGNMENT, 1);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    pixmapLoadFromTGA(PIXMAP_NAME, &pixmap);        // nacist pixmapu z externiho souboru
    prepareConvolutionFilter2D();                   // priprava dvojrozmerneho konvolucniho filtru
}



//---------------------------------------------------------------------
// Nastaveni souradneho systemu v zavislosti na velikosti okna
//---------------------------------------------------------------------
void onResize(int w, int h)                         // argumenty w a h reprezentuji novou velikost okna
{
    glViewport(0, 0, w, h);                         // viditelna oblast pres cele okno
    glMatrixMode(GL_PROJECTION);                    // zacatek modifikace projekcni matice
    glLoadIdentity();                               // vymazani projekcni matice (=identita)
    glOrtho(0, w, 0, h, -1, 1);                     // mapovani abstraktnich souradnic do souradnic okna
    glScalef(1, -1, 1);                             // inverze y-ove osy, aby se y zvetsovalo smerem dolu
    glTranslatef(0, -h, 0);                         // posun pocatku do leveho horniho rohu
    window.width=w;                                 // zapamatovat si velikost okna
    window.height=h;
}



//--------------------------------------------------------------------
// Tato funkce je volana pri kazdem prekresleni okna
//--------------------------------------------------------------------
void onDisplay(void)
{
    char str[100];

    glClear(GL_COLOR_BUFFER_BIT);                   // smazani barvoveho bufferu

    sprintf(str, "Pixmap name: %s", PIXMAP_NAME);
    printGlutBitmapFont(str, GLUT_BITMAP_8_BY_13, 20, 20, 0.6f, 0.8f, 0.2f);
    sprintf(str, "Pixmap size: %d x %d", pixmap.width, pixmap.height);
    printGlutBitmapFont(str, GLUT_BITMAP_8_BY_13, 20, 36, 0.6f, 0.8f, 0.2f);
    sprintf(str, "Filter type: %s", filter ? "Gaussian 3x3" : "none");
    printGlutBitmapFont(str, GLUT_BITMAP_8_BY_13, 20, 54, 0.6f, 0.8f, 0.2f);
    printGlutBitmapFont("Press <E> to enable/disable convolution filter", GLUT_BITMAP_9_BY_15, 20, 72, 0.8f, 0.3f, 0.3f);

    if (filter) glEnable(GL_CONVOLUTION_2D);        // podle uzivatelova pozadavku
    else        glDisable(GL_CONVOLUTION_2D);       // povolit ci zakazat filtraci

    glRasterPos2i((window.width-pixmap.width)>>1,   // vycentrovat okno a vykreslit pixmapu
                  pixmap.height+100);
    glDrawPixels(pixmap.width, pixmap.height, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixmap.pixels);
    glFlush();                                      // provedeni a vykresleni vsech zmen
    glutSwapBuffers();                              // a prohozeni predniho a zadniho bufferu
}



//---------------------------------------------------------------------
// Tato funkce je volana pri stlaceni ASCII klavesy
//---------------------------------------------------------------------
#ifdef __BORLANDC__
#pragma option -w-par                               // aby Borlandi prekladace nehlasily
#endif                                              // warningy ze argumenty nejsou pouzity
void onKeyboard(unsigned char key, int x, int y)
{
    if (key>='A' && key<='Z')                       // uprava velkych pismen na mala
        key+='a'-'A';                               // pro zjednoduseni prikazu switch

    switch (key) {
        case 27:    exit(0);            break;      // ukonceni aplikace
        case 'q':   exit(0);            break;      // ukonceni aplikace
        case 'f':   glutFullScreen();   break;      // full screen
        case 'w':   glutReshapeWindow(WINDOW_WIDTH, WINDOW_HEIGHT);// prepnuti zpet do okna
                    glutPositionWindow(WINDOW_LEFT, WINDOW_TOP);
                                        break;
        case 'e':   filter=!filter;                 // povoleni ci zakaz filtrace
                    glutPostRedisplay();break;
        default:                        break;
    }
}
#ifdef __BORLANDC__
#pragma option -w+par
#endif



//---------------------------------------------------------------------
// Hlavni funkce konzolove aplikace
//---------------------------------------------------------------------
int main(int argc, char **argv)
{
    glutInit(&argc, argv);                          // inicializace knihovny GLUT
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);    // nastaveni dvou barvovych bufferu a pameti hloubky
    glutInitWindowPosition(WINDOW_LEFT, WINDOW_TOP);// pocatecni pozice leveho horniho rohu okna
    glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);// pocatecni velikost okna
    glutCreateWindow(WINDOW_TITLE);                 // vytvoreni okna pro kresleni
    glutDisplayFunc(onDisplay);                     // registrace funkce volane pri prekreslovani okna
    glutReshapeFunc(onResize);                      // registrace funkce volane pri zmene velikosti okna
    glutKeyboardFunc(onKeyboard);                   // registrace funkce volane pri stlaceni klavesy
    onInit();                                       // inicializace vykreslovani
    glutMainLoop();                                 // nekonecna smycka, kde se volaji zaregistrovane funkce
    return 0;                                       // navratova hodnota vracena operacnimu systemu
}



//---------------------------------------------------------------------
// Konec zdrojoveho souboru
//---------------------------------------------------------------------