//-----------------------------------------------------------------------------
// Demonstracni priklad k serialu "Graficke formaty"
// http://www.root.cz/
//
// Autor: Pavel Tisnovsky
//
// Tento program vytvori obrazek s prubehem bazovych funkci pouzitych
// v jednorozmerne diskretni kosinove transformaci (1D 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   "1d_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 bazovych funkci jednorozmerne DCT
//-----------------------------------------------------------------------------
void draw1d_dct(const pixmap *pix, int n)
{
#define PI 3.1415927
    int u;
    // pozicovani prubehu bazovych funkci v pixmape
    int xoffset=25*8/N, yoffset=50;
    int xdelta=50*8/N,  ydelta=100;
    float amplitude=40.0;

    // vykreslit vsech "n" bazovych funkci
    for (u=0; u<n; u++) {
        int x;
        // pozice horizontalni osy pro danou bazovou funkci
        int y0=yoffset+ydelta*u;
        float y;
        // horizontalni osa
        hline(pix, 0, pix->width-1, y0, 255, 128, 0);
        // vypocet vsech bodu bazove funkce
        for (x=0; x<n; x++) {
            float alfa=PI*(2.0*x+1.0)*u/(2.0*n);
            // hodnota bazove funkce pro dane "x"
            y=amplitude*cos(alfa);
            //printf("%d %d -> %f\n", u, x, alfa);
            // vykresleni vertikalni hodnoty
            vline(pix, xoffset+xdelta*x, y0, y0-y, 0, 255, 128);
            // zvyrazneni koncoveho bodu pomoci horizontalni carky
            hline(pix, xoffset+xdelta*x-3, xoffset+xdelta*x+3, y0-y, 0, 255, 128);
        }
    }
}



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

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



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