//---------------------------------------------------------------------
// Ukazkovy priklad k serii clanku OpenGL Imaging Subset
//
// Autor:          Pavel Tisnovsky
// Cislo clanku:   3
// Cislo prikladu: 2
//
// Tento ukazkovy program nacte z externiho souboru "lena.tga" obrazek,
// ktery posleze zobrazi pomoci funkce glDrawPixels() do okna aplikace.
// Pomoci zmeny polohy jednoho ze tri posuvniku lze menit kontrast
// jednotlivych barvovych slozek (zmena parametru scale).
// 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 <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 3.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;

typedef struct {                                    // novy datovy typ pro ulozeni konfigurace slideru
    unsigned int    red;
    unsigned int    green;
    unsigned int    blue;
} Sliders;

Pixmap pixmap;
Window window;
Sliders sliders={100, 100, 100};



//---------------------------------------------------------------------
// 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 vykresli retezec zadanym bitmapovym fontem
//---------------------------------------------------------------------
void drawSliders(Sliders *sliders)
{
    char str[100];

    sprintf(str, "Red scale:   %d\%", sliders->red);
    printGlutBitmapFont(str, GLUT_BITMAP_8_BY_13, 20, 60, 0.8, 0.4, 0.4);
    sprintf(str, "Green scale: %d\%", sliders->green);
    printGlutBitmapFont(str, GLUT_BITMAP_8_BY_13, 20, 72, 0.4, 0.8, 0.4);
    sprintf(str, "Blue scale:  %d\%", sliders->blue);
    printGlutBitmapFont(str, GLUT_BITMAP_8_BY_13, 20, 84, 0.4, 0.4, 0.8);
    glColor3f(1.0, 0.4, 0.4);                       // vykreslit cerveny slider
    glBegin(GL_POINTS);
        glVertex2i(200+sliders->red, 55);           // cerveny slider
    glEnd();
    glColor3f(0.8, 0.4, 0.4);
    glBegin(GL_LINE_LOOP);                          // mez slideru
        glVertex2i(197, 52);
        glVertex2i(403, 52);
        glVertex2i(403, 58);
        glVertex2i(197, 58);
    glEnd();
    glColor3f(0.4, 1.0, 0.4);                       // vykreslit zeleny slider
    glBegin(GL_POINTS);
        glVertex2i(200+sliders->green, 67);         // zeleny slider
    glEnd();
    glColor3f(0.4, 0.8, 0.4);
    glBegin(GL_LINE_LOOP);                          // mez slideru
        glVertex2i(197, 64);
        glVertex2i(403, 64);
        glVertex2i(403, 70);
        glVertex2i(197, 70);
    glEnd();
    glColor3f(0.4, 0.4, 1.0);                       // vykreslit modry slider
    glBegin(GL_POINTS);
        glVertex2i(200+sliders->blue, 79);          // modry slider
    glEnd();
    glColor3f(0.4, 0.4, 0.8);
    glBegin(GL_LINE_LOOP);                          // mez slideru
        glVertex2i(197, 76);
        glVertex2i(403, 76);
        glVertex2i(403, 82);
        glVertex2i(197, 82);
    glEnd();
}



//---------------------------------------------------------------------
// 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
    glEnable(GL_BLEND);                             // nastaveni zpusobu vykreslovani bodu
    glEnable(GL_POINT_SMOOTH);
    glPointSize(6.0f);
    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
}



//---------------------------------------------------------------------
// 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.6, 0.6, 0.6);
    sprintf(str, "Pixmap size: %d x %d", pixmap.width, pixmap.height);
    printGlutBitmapFont(str, GLUT_BITMAP_8_BY_13, 20, 32, 0.6, 0.6, 0.6);

    drawSliders(&sliders);                          // vykreslit slidery
    glPixelTransferf(GL_RED_SCALE, sliders.red/100.0f);     // nastavit nasobitel barvovych slozek
    glPixelTransferf(GL_GREEN_SCALE, sliders.green/100.0f);
    glPixelTransferf(GL_BLUE_SCALE, sliders.blue/100.0f);

    glRasterPos2i((window.width-pixmap.width)>>1,   // vykreslit vycentrovanou 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;
        default:                        break;
    }
}
#ifdef __BORLANDC__
#pragma option -w+par
#endif



//---------------------------------------------------------------------
// Tato funkce je volana pri stisku ci pusteni tlacitka mysi
//---------------------------------------------------------------------
#ifdef __BORLANDC__
#pragma option -w-par                               // aby Borlandi prekladace nehlasily
#endif                                              // warningy ze argumenty nejsou pouzity
void onMouse(int button, int state, int x, int y)
{
    if (state==GLUT_DOWN) {
        if (x>=200 && x<=400 && y>=52 && y<=58) {   // mys je v oblasti cerveneho slideru
            sliders.red=x-200;
            glutPostRedisplay();
        }
        if (x>=200 && x<=400 && y>=64 && y<=70) {   // mys je v oblasti zeleneho slideru
            sliders.green=x-200;
            glutPostRedisplay();
        }
        if (x>=200 && x<=400 && y>=76 && y<=82) {   // mys je v oblasti modreho slideru
            sliders.blue=x-200;
            glutPostRedisplay();
        }
    }
}
#ifdef __BORLANDC__
#pragma option -w+par
#endif



//---------------------------------------------------------------------
// Tato funkce je volana pri pohybu mysi
//---------------------------------------------------------------------
void onMouseMotion(int x, int y)
{
    if (x>=200 && x<=400 && y>=52 && y<=58) {       // mys je v oblasti cerveneho slideru
        sliders.red=x-200;
        glutPostRedisplay();
    }
    if (x>=200 && x<=400 && y>=64 && y<=70) {       // mys je v oblasti zeleneho slideru
        sliders.green=x-200;
        glutPostRedisplay();
    }
    if (x>=200 && x<=400 && y>=76 && y<=82) {       // mys je v oblasti modreho slideru
        sliders.blue=x-200;
        glutPostRedisplay();
    }
}



//---------------------------------------------------------------------
// 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
    glutMouseFunc(onMouse);                         // registrace funkce volane pri stlaceni ci pusteni tlacitka mysi
    glutMotionFunc(onMouseMotion);                  // registrace funkce volane pri pohybu mysi
    onInit();                                       // inicializace vykreslovani
    glutMainLoop();                                 // nekonecna smycka, kde se volaji zaregistrovane funkce
    return 0;                                       // navratova hodnota vracena operacnimu systemu
}



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