/*
 * Demonstracni priklad, ktery nacte a posleze vytiskne zakladni udaje
 * o souboru typu JPEG. Vytvoreno s vyuzitim upravene funkce jpeginfo()
 * Miloslava Vlcka a Zdenka Vladeky (FIT VUT v Brne).
 *
 * Autor: Pavel Tisnovsky (vse mimo funkci getJpegInfo())
 *
 * Pouziti:
 * jpeg_info image.jpg
 */

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



/*
 * Datova struktura se zakladnimi informacemi o JPEG souboru
 */
typedef struct JpegInfo {
    int width;                  // sirka obrazku
    int height;                 // vyska obrazku
    char fname[200];            // jmeno zdrojoveho souboru
    unsigned long size;         // celkova velikost souboru
    long p_data;                // pozice zacatku obrazovychdat v souboru
    long p_qtab_y;              // pozice zacatku kvantizacni tabulky pro slozku Y
    long p_qtab_c;              // pozice zacatku kvantizacni tabulky pro slozku C
    long p_htab_y_dc;           // zacatek Huffmanovy kodove tabulky DC koeficientu slozky Y
    long p_htab_y_ac;           // zacatek Huffmanovy kodove tabulky AC koeficientu slozky Y
    long p_htab_c_dc;           // zacatek Huffmanovy kodove tabulky DC koeficientu slozek CbCr
    long p_htab_c_ac;           // zacatek Huffmanovy kodove tabulky AC koeficientu slozek CbCr
    unsigned int rstinterval;   // restart interval
    unsigned char sf_y,sf_cb, sf_cr;
} JpegInfo;

JpegInfo jpegInfo;



/*
 * Funkce pro ziskani zakladnich udaju o souboru typu JFIF/JPEG
 */
void getJpegInfo(const char *fileName, FILE *fileJpeg, JpegInfo *jpegInfo)
{
    unsigned char b1,b2,so;
    long l,ll,fl;
    int ii,i;
    jpegInfo->width=0;
    jpegInfo->height=0;
    fseek(fileJpeg, 0, SEEK_END);
    ll=ftell(fileJpeg);
    jpegInfo->size=ll;
    fseek(fileJpeg, 0, SEEK_SET);
    fread(&b1, 1, 1, fileJpeg);
    fread(&b2, 1, 1, fileJpeg);
    fl=2;
    if ((b1==0xff) && (b2==0xd8)){
        strcpy(jpegInfo->fname, fileName);
        while (fl<ll){
            fseek(fileJpeg, fl, SEEK_SET);
            fread(&b1, 1, 1, fileJpeg);
            fread(&so, 1, 1, fileJpeg);
            if (b1==0xff){
                fread(&b1, 1, 1, fileJpeg);
                fread(&b2, 1, 1, fileJpeg);
                fl+=2+((long)b1<<8)+b2;
                switch (so) {
                    case 0xdd: { // define restart marker
                                   fread(&b1, 1, 1, fileJpeg);
                                   fread(&b2, 1, 1, fileJpeg);
                                   jpegInfo->rstinterval=(b1<<8)+b2;
                                   break;
                               }
                    case 0xda: { // kodovana obrazova data
                                   jpegInfo->p_data=fl;
                                   return;
                               }
                    case 0xdb: { // kvantizacni tabulky
                                   fread(&b1,1,1,fileJpeg);
                                   if (b1==0)
                                       jpegInfo->p_qtab_y=ftell(fileJpeg);
                                   else
                                           jpegInfo->p_qtab_c=ftell(fileJpeg);
                                   if (fl>ftell(fileJpeg)+65){
                                       fseek(fileJpeg, 64, SEEK_CUR);
                                       fread(&b1, 1, 1, fileJpeg);
                                       if (b1==0)
                                           jpegInfo->p_qtab_y=ftell(fileJpeg);
                                       else
                                           jpegInfo->p_qtab_c=ftell(fileJpeg);
                                   }
                                   break;
                               }
                    case 0xc4: { // Huffmanovy kodove tabulky
                                   for (ii=0; ii<4; ii++) {
                                       fread(&b1, 1, 1, fileJpeg);
                                       switch (b1){
                                           case 0:  jpegInfo->p_htab_y_dc=ftell(fileJpeg);break;
                                           case 1:  jpegInfo->p_htab_c_dc=ftell(fileJpeg);break;
                                           case 16: jpegInfo->p_htab_y_ac=ftell(fileJpeg);break;
                                           case 17: jpegInfo->p_htab_c_ac=ftell(fileJpeg);break;

                                       };
                                       l=0;
                                       for(i=0; i<16; i++) {
                                           fread(&b1, 1, 1, fileJpeg);
                                           l+=b1;
                                       };
                                       fseek(fileJpeg,l,SEEK_CUR);
                                       if (ftell(fileJpeg)>=fl) break;
                                   };
                                   break;
                               }
                    case 0xc0: { // zakladni informace o obrazku
                                   fread(&b1, 1, 1, fileJpeg);
                                   fread(&b1, 1, 1, fileJpeg);
                                   fread(&b2, 1, 1, fileJpeg);
                                   jpegInfo->height=((long)b1<<8)+b2;
                                   fread(&b1, 1, 1, fileJpeg);
                                   fread(&b2, 1, 1, fileJpeg);
                                   jpegInfo->width=((long)b1<<8)+b2;
                                   fseek(fileJpeg, 2, SEEK_CUR);
                                   fread(&jpegInfo->sf_y, 1, 1, fileJpeg);
                                   fseek(fileJpeg, 2, SEEK_CUR);
                                   fread(&jpegInfo->sf_cb, 1, 1, fileJpeg);
                                   fseek(fileJpeg, 2, SEEK_CUR);
                                   fread(&jpegInfo->sf_cr, 1, 1, fileJpeg);
                                   break;
                               }
                };
            }
        }
    }
    else {                              // nenasli jsme znacku -> vadny JPEG soubor
        return;
    }
    return;
}



/*
 * Tisk hodnoty offsetu jako dekadicke a hexadecimalni hodnoty
 */
void printOffset(const char *str, unsigned int value)
{
    printf("%s    %10u\t0x%08x\n", str, value, value);
}



/*
 * Tisk zakladnich informaci o souboru typu JFIF/JPEG
 */
void printJpegInfo(JpegInfo *jpegInfo)
{
    printf("Jmeno souboru:    %s\n",        jpegInfo->fname);
    printf("Delka souboru:    %ld bytu\n",  jpegInfo->size);
    printf("Sirka obrazku:    %d pixelu\n", jpegInfo->width);
    printf("Vyska obrazku:    %d pixelu\n", jpegInfo->height);
    printOffset("Offset zacatku obrazovych dat:            ", jpegInfo->p_data);
    printOffset("Offset zacatku kvantizacni tabulky Y:     ", jpegInfo->p_qtab_y);
    printOffset("Offset zacatku kvantizacni tabulky CbCr:  ", jpegInfo->p_qtab_c);
    printOffset("Offset zacatku Huffmanovy tabulky DC/Y:   ", jpegInfo->p_htab_y_dc);
    printOffset("Offset zacatku Huffmanovy tabulky AC/Y:   ", jpegInfo->p_htab_y_ac);
    printOffset("Offset zacatku Huffmanovy tabulky DC/CbCr:", jpegInfo->p_htab_c_dc);
    printOffset("Offset zacatku Huffmanovy tabulky AC/CbCr:", jpegInfo->p_htab_c_ac);
}



/*
 * Tisk obsahu jedne kvantizacni tabulky
 */
void printQuantizationTable(FILE *fileJpeg, unsigned int seek)
{
#define DCTSIZE         8       // velikost bloku DCT
#define NUM_QUANT_TBLS  4       // kvantizacni tabulky jsou ocislovany 0..3
    int n;                      // cislo kvantizacni tabulky
    int prec;                   // bitova sirka kvantizacnich koeficientu
    int i, j;                   // pocitadla smycek
    fseek(fileJpeg, seek, SEEK_SET);
    n=fgetc(fileJpeg);
    n&=0x0F;
    prec=n>>4;
    printf("kvantizacni tabulka cislo %d, presnost %d bitu\n", n, prec==0 ? 8:16);
    // neco je spatne, nekorektni cislo tabulky
    if (n >= NUM_QUANT_TBLS) {
        printf("nekorektni cislo kvantizacni tabulky %d!\n", n);
        return;
    }
    // nacist vsech 8x8 kvantizacnich koeficientu
    for (j=0; j<DCTSIZE; j++) {
        for (i=0; i<DCTSIZE; i++) {
            int tmp=fgetc(fileJpeg);
            if (prec) // sestnactibitove koeficienty?
                tmp=(tmp<<8)+fgetc(fileJpeg);
            printf("%d\t", tmp);
        }
        // dalsich osm hodnot na radku
        printf("\n");
    }
}



/*
 * Tisk kvantizacnich tabulek
 */
void printQuantizationTables(FILE *fileJpeg, JpegInfo *jpegInfo)
{
    // nacist kvantizacni tabulky
    printf("\nKvantizacni tabulka luminance Y (sekvence cik-cak):\n");
    printQuantizationTable(fileJpeg, jpegInfo->p_qtab_y-1);
    printf("\nKvantizacni tabulka chrominance Cb Cr (sekvence cik-cak):\n");
    printQuantizationTable(fileJpeg, jpegInfo->p_qtab_c-1);
}



/*
 * Tisk obsahu jedne Huffmanovy tabulky
 */
void printHuffmanTable(FILE *fileJpeg)
{
    int i, j;
    int count=0;
    int bits_a[16];
    printf("tabulka cislo: 0x%02x\n", fgetc(fileJpeg));
    puts("hodnoty pro bitove delky:");
    for (i=0; i<16; i++) {
        int bits=fgetc(fileJpeg);
        count+=bits;
        printf("Hodnoty pro bitove sekvence delky %02d bitu: %02x\n", i+1, bits);
        bits_a[i]=bits;
    }
    if (count>256) {
        printf("Nekorektni celkovy pocet ulozenych hodnot!");
        return;
    }
    else {
        printf("celkem je v tabulce ulozeno %d hodnot\n", count);
    }
    puts("\nHodnoty pro konstrukci binarniho stromu:");
    for (j=0; j<16; j++) {
        printf("sekvence delky %02d bitu: ", j+1);
        for (i=0; i<bits_a[j]; i++) {
            printf("%02x  ", fgetc(fileJpeg));
        }
        putchar('\n');
    }
    putchar('\n');
}



/*
 * Tisk Huffmanovych kodovych tabulek
 */
void printHuffmanTables(FILE *fileJpeg, JpegInfo *jpegInfo)
{
    printf("\nHuffmanova tabulka pro DC koeficienty luminance (Y):\n");
    fseek(fileJpeg, jpegInfo->p_htab_y_dc-1, SEEK_SET);
    printHuffmanTable(fileJpeg); // vypis stromu

    printf("\nHuffmanova tabulka pro DC koeficienty chrominance (Cb Cr):\n");
    fseek(fileJpeg, jpegInfo->p_htab_c_dc-1, SEEK_SET);
    printHuffmanTable(fileJpeg); // vypis stromu

    printf("\nHuffmanova tabulka pro AC koeficienty luminance (Y):\n");
    fseek(fileJpeg, jpegInfo->p_htab_y_ac-1, SEEK_SET);
    printHuffmanTable(fileJpeg); // vypis stromu

    printf("\nHuffmanova tabulka pro AC koeficienty chrominance (Cb Cr):\n");
    fseek(fileJpeg, jpegInfo->p_htab_c_ac-1, SEEK_SET);
    printHuffmanTable(fileJpeg); // vypis stromu
};



/*
 * Hlavni funkce konzolove aplikace
 */
int main(int argc, char *argv[])
{
    FILE *fileJpeg;

    // kontrola, jestli je z prikazoveho radku zadano jmeno souboru
    if (argc<=1) {
        printf("Pouziti jpeg_info image.jpg\n");
        return 1;
    }

    // pokus o otevreni souboru pro cteni
    fileJpeg=fopen(argv[1], "rb");
    if (!fileJpeg) {
        printf("Chyba pri otevirani souboru %s pro cteni!\n", argv[1]);
        return 1;
    }

    // ziskani informaci z JPEG souboru a jejich vypis
    getJpegInfo(argv[1], fileJpeg, &jpegInfo);
    printJpegInfo(&jpegInfo);
    printQuantizationTables(fileJpeg, &jpegInfo);
    printHuffmanTables(fileJpeg, &jpegInfo);

    if (fclose(fileJpeg)==EOF) {
        printf("Chyba pri zavirani souboru %s!\n", argv[1]);
        return 1;
    }
    return 0;
}



/*
 * finito
 */