//-----------------------------------------------------------------------------
// Demonstracni priklad k serialu "Graficke formaty"
// http://www.root.cz/
//
// Autor: Pavel Tisnovsky
//
// Tento program vytvori obrazek s prubehem bazovych funkci pouzitych
// v dvourozmerne diskretni kosinove transformaci (2D DCT).
//-----------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>

// stupen DCT (smysluplne hodnoty jsou: 8, 16, 32)
#define N 8

// jmeno souboru, se kterym se pracuje
#define FILE_NAME   "2d_dct.tga"

// datova struktura popisujici pixmapu
typedef struct {
    unsigned int width;
    unsigned int height;
    unsigned char *pixels;
} pixmap;

// pracovni pixmapa
pixmap *pix;



//-----------------------------------------------------------------------------
// vytvoreni pixmapy ve formatu truecolor
//-----------------------------------------------------------------------------
pixmap * createPixmap(unsigned int width, unsigned int height)
{
    pixmap *px;
    unsigned int size;

    assert(width>0);
    assert(height>0);
    // alokace pameti pro hlavicku pixmapy
    px=(pixmap*)malloc(sizeof(pixmap));
    // kontrola alokace pameti
    assert(px);
    px->width=width;
    px->height=height;
    size=3*width*height;
    // alokace pameti pro pixely
    px->pixels=(unsigned char *)malloc(sizeof(unsigned char)*size);
    // kontrola alokace pameti
    assert(px->pixels);
    return px;
}



//-----------------------------------------------------------------------------
// ulozeni pixmapy do externiho souboru
//-----------------------------------------------------------------------------
void savePixmap(const pixmap *p, const char *filename)
{
    FILE *fout;
    unsigned int i, j;
    unsigned char tgaHeader[18]={               // hlavicka formatu typu TGA
                        0x00,                   // typ hlavicky TGA
                        0x00,                   // nepouzivame paletu
                        0x02,                   // typ obrazku je grayscale
                        0x00, 0x00,             // delka palety je nulova
                        0x00, 0x00, 0x00,       // pozice v palete nas nezajima
                        0x00, 0x00, 0x00, 0x00, // obrazek je umisteny na pozici [0, 0]
                        0x00, 0x00, 0x00, 0x00, // sirka a vyska obrazku (dva byty na polozku)
                        0x18,                   // format je 8 bitu na pixel
                        0x20                    // orientace bitmapy v obrazku
    };
    assert(p);                                  // je pixmapa v poradku?
    assert(p->pixels);
    printf("pixmap info:\n\tname:\t%s\n\twidth:\t%d\n\theight:\t%d\n\tbpp:\t%d\n", filename, p->width, p->height, 24);
    memcpy(tgaHeader+12, &(p->width), 2);       // do hlavicky TGA zapsat velikost obrazku
    memcpy(tgaHeader+14, &(p->height), 2);
    fout=fopen(filename, "wb");
    assert(fout);
    fwrite(tgaHeader, 18, 1, fout);             // zapsat hlavicku TGA do souboru
    for (i=0; i<p->height; i++) {               // postupny zapis vsech radku
        unsigned int yoff=3*i*p->width;         // y-ovy offset v poli
        unsigned char *p1=p->pixels+yoff;
        for (j=0; j<p->width; j++) {            // prohozeni BGR na RGB a ulozeni radku do souboru
            int result;
            result=fputc(*(p1+2), fout); assert(result!=EOF);
            result=fputc(*(p1+1), fout); assert(result!=EOF);
            result=fputc(*(p1), fout);   assert(result!=EOF);
            p1+=3;
        }
    }
    fclose(fout);
    puts("pixmap saved");
}



//-----------------------------------------------------------------------------
// ulozeni barvy pixelu na souradnice x, y
//-----------------------------------------------------------------------------
void putpixel(const pixmap *pix, int x, int y, int r, int g, int b)
{
    unsigned char *p;
    assert(pix);
    assert(pix->pixels);
    assert(x>=0 && x<pix->width);
    assert(y>=0);
    assert(y<pix->height);
    p=pix->pixels+3*(x+y*pix->width);
    *p++=r;
    *p++=g;
    *p=b;
}



//-----------------------------------------------------------------------------
// vykresleni horizontalni usecky
//-----------------------------------------------------------------------------
void hline(const pixmap *pix, int x1, int x2, int y, int r, int g, int b)
{
    int i;
    for (i=x1; i<=x2; i++)
        putpixel(pix, i, y, r, g, b);
}



//-----------------------------------------------------------------------------
// vykresleni vertikalni usecky (povoleno prohozeni souradnic y1 <=> y2)
//-----------------------------------------------------------------------------
void vline(const pixmap *pix, int x, int y1, int y2, int r, int g, int b)
{
    int i;
    if (y1<y2)
        for (i=y1; i<=y2; i++)
            putpixel(pix, x, i, r, g, b);
    else
        for (i=y2; i<=y1; i++)
            putpixel(pix, x, i, r, g, b);
}



//-----------------------------------------------------------------------------
// vykresleni osove orientovaneho obdelnika
//-----------------------------------------------------------------------------
void box(const pixmap *pix, int x1, int y1, int x2, int y2, int r, int g, int b)
{
    int i, j;
    for (j=y1; j<=y2; j++) {
        for (i=x1; i<=x2; i++) {
            putpixel(pix, i, j, r, g, b);
        }
    }
}



//-----------------------------------------------------------------------------
// vykresleni bazovych funkci dvourozmerne DCT
//-----------------------------------------------------------------------------
void draw2d_dct(const pixmap *pix, int n)
{
#define PI 3.1415927
    int u, v;

    // zmena koeficientu "v" ve vertikalnim smeru
    for (v=0; v<n; v++) {
        // zmena koeficientu "u" v horizontalnim smeru
        for (u=0; u<n; u++) {
            // vypocet pocatecnich souradnic ctverce
            int x0=u*100;
            int y0=v*100;
            int x, y;
            // vypocet hodnot 2D DCT pro dane "u" a "v"
            for (y=0; y<n; y++) {
                for (x=0; x<n; x++) {
                    float alfa=PI*(2.0*x+1.0)*u/(2.0*n);
                    float beta=PI*(2.0*y+1.0)*v/(2.0*n);
                    // prepocet na stupne sedi
                    float value=127.5+127.5*cos(alfa)*cos(beta);
                    // vykresleni jedne hodnoty 2D DCT
                    box(pix,
                        x0+64*x/n,      // x1
                        y0+64*y/n,      // y1
                        x0+64*(x+1)/n,  // x2
                        y0+64*(y+1)/n,  // y2
                        value,          // cervena barvova slozka
                        value,          // zelena barvova
                        value);         // modra barvova slozka
                }
            }
        }
    }
}



//-----------------------------------------------------------------------------
// hlavni funkce konzolove aplikace
//-----------------------------------------------------------------------------
int main(int argc, char **argv)
{
    char *full_name;

    printf("\nprocessing...\n");
    pix=createPixmap(N*100, N*100);
    draw2d_dct(pix, N);
    savePixmap(pix, FILE_NAME);
    printf("\ndone!\n");
    return 0;
}



//-----------------------------------------------------------------------------
// finito
//-----------------------------------------------------------------------------