/*
 * JPEG decoding engine for DCT-baseline
 *
 *      copyrights 2003 by nikq | nikq::club.
 *
 * history::
 * 2003/04/28 | added OSASK-GUI ( by H.Kawai )
 * 2003/05/12 | optimized DCT ( 20-bits fixed point, etc...) -> line 407-464 ( by I.Tak. )
 * 2003/09/27 | PICTURE0.BIN(DLL)用に改造 ( by くーみん )
 * 2003/09/28 | 各種バグフィクス＆多少の最適化 ( by H.Kawai )
 *
 */

#include "bootpack.h"

typedef unsigned char UCHAR;

//struct DLL_STRPICENV { int work[16384]; };

typedef struct
{
    int elem; //要素数
    unsigned short code[256];
    unsigned char  size[256];
    unsigned char  value[256];
}HUFF;

typedef struct
{
    // SOF
    int width;
    int height;

    // MCU
    int mcu_width;
    int mcu_height;

    int max_h,max_v;
    int compo_count;
    int compo_id[3];
    int compo_sample[3];
    int compo_h[3];
    int compo_v[3];
    int compo_qt[3];

    // SOS
    int scan_count;
    int scan_id[3];
    int scan_ac[3];
    int scan_dc[3];
    int scan_h[3];  // サンプリング要素数
    int scan_v[3];  // サンプリング要素数
    int scan_qt[3]; // 量子化テーブルインデクス
    
    // DRI
    int interval;

    int mcu_buf[32*32*4]; //バッファ
    int *mcu_yuv[4];
    int mcu_preDC[3];
    
    // DQT
    int dqt[3][64];
    int n_dqt;
    
    // DHT
    HUFF huff[2][3];
    
    // FILE i/o
    unsigned char *fp, *fp1;
    unsigned long bit_buff;
    int bit_remain;
    int width_buf;

    int base_img[64][64]; // 基底画像 ( [横周波数uπ][縦周波数vπ][横位相(M/8)][縦位相(N/8)]

    int dummy[64];
}JPEG;

/* for 16bit */
#ifndef PIXEL16
#define PIXEL16(r, g, b)    ((r) << 11 | (g) << 5 | (b))
    /* 0 <= r <= 31, 0 <= g <= 63, 0 <= b <= 31 */
#endif

//int info_JPEG(struct DLL_STRPICENV *env, int *info, int size, UCHAR *fp);
//int decode0_JPEG(struct DLL_STRPICENV *env, int size, UCHAR *fp, int b_type, UCHAR *buf, int skip);

void jpeg_idct_init(int base_img[64][64]);
int  jpeg_init(JPEG *jpeg);
void jpeg_decode(JPEG *jpeg, unsigned char *rgb, int b_type);

/* ----------------- start main section ----------------- */

int info_JPEG(struct DLL_STRPICENV *env, int *info, int size, UCHAR *fp0)
{
    JPEG *jpeg = (JPEG *) (((int *) env) + 128);
    jpeg->fp = fp0;
    jpeg->fp1 = fp0 + size;

    if (jpeg_init(jpeg))
        return 0;

    if (jpeg->width == 0)
        return 0;
    
    info[0] = 0x0002;
    info[1] = 0x0000;
    info[2] = jpeg->width;
    info[3] = jpeg->height;

    /* OK */
    return 1;
}

int decode0_JPEG(struct DLL_STRPICENV *env, int size, UCHAR *fp0, int b_type, UCHAR *buf, int skip)
{
    JPEG *jpeg = (JPEG *) (((int *) env) + 128);
    jpeg->fp = fp0;
    jpeg->fp1 = fp0 + size;

    jpeg_idct_init(jpeg->base_img);
    jpeg_init(jpeg);

    jpeg->width_buf = skip / (b_type & 0x7f) + jpeg->width;
    jpeg_decode(jpeg, buf, b_type);

    /* OK */
    return 0;
}

// -------------------------- I/O ----------------------------

unsigned short get_bits(JPEG *jpeg, int bit)
{
    unsigned char  c;
    unsigned short ret;
    unsigned long  buff;
    int remain;

    buff   = jpeg->bit_buff;
    remain = jpeg->bit_remain;

    while (remain <= 16) {
        if (jpeg->fp >= jpeg->fp1) {
            ret = 0;
            goto fin;
        }
        c = *jpeg->fp++;
        if (c == 0xff) { // マーカエラーを防ぐため、FF -> FF 00 にエスケープされてる
            if (jpeg->fp >= jpeg->fp1) {
                ret = 0;
                goto fin;
            }
            jpeg->fp++; /* 00をskip */
        }
        buff = (buff << 8) | c;
        remain += 8;
    }
    ret = (buff >> (remain - bit)) & ((1 << bit) - 1);
    remain -= bit;

    jpeg->bit_remain = remain;
    jpeg->bit_buff   = buff;
fin:
    return ret;
}

// ------------------------ JPEG セグメント実装 -----------------

// start of frame
int jpeg_sof(JPEG *jpeg)
{
    unsigned char c;
    int i, h, v, n;

    if (jpeg->fp + 8 > jpeg->fp1)
        goto err;
    /* fp[2] は bpp */
    jpeg->height = jpeg->fp[3] << 8 | jpeg->fp[4];
    jpeg->width  = jpeg->fp[5] << 8 | jpeg->fp[6];
    n = jpeg->compo_count = jpeg->fp[7]; // Num of compo, nf
    jpeg->fp += 8;
    if (jpeg->fp + n * 3 > jpeg->fp1)
        goto err;

    for (i = 0; i < n; i++) {
        jpeg->compo_id[i] = jpeg->fp[0];

        jpeg->compo_sample[i] = c = jpeg->fp[1];
        h = jpeg->compo_h[i] = (c >> 4) & 0x0f;
        v = jpeg->compo_v[i] = c & 0x0f;

        if (jpeg->max_h < h)
            jpeg->max_h = h;
        if (jpeg->max_v < v)
            jpeg->max_v = v;

        jpeg->compo_qt[i] = jpeg->fp[2];
        jpeg->fp += 3;
    }
    return 0;
err:
    jpeg->fp = jpeg->fp1;
    return 1;
}

// define quantize table
int jpeg_dqt(JPEG *jpeg)
{
    unsigned char c;
    int i, j, size;

    if (jpeg->fp + 2 > jpeg->fp1)
        goto err;
    size = (jpeg->fp[0] << 8 | jpeg->fp[1]) - 2;
    jpeg->fp += 2;
    if (jpeg->fp + size > jpeg->fp1)
        goto err;

    while (size > 0) {
        c = *jpeg->fp++;
        size--;
        j = c & 7;
        if (j > jpeg->n_dqt)
            jpeg->n_dqt = j;

         if (c & 0xf8) {
            // 16 bit DQT
            for (i = 0; i < 64; i++) {
                jpeg->dqt[j][i] = jpeg->fp[0]; /* 下位は読み捨てている */
                jpeg->fp += 2;
            }
            size += -64 * 2;
        } else {
            //  8 bit DQT
            for (i = 0; i < 64; i++)
                jpeg->dqt[j][i] = *jpeg->fp++;
            size -= 64;
        }
    }
    return 0;
err:
    jpeg->fp = jpeg->fp1;
    return 1;
}

// define huffman table
int jpeg_dht(JPEG *jpeg)
{
    unsigned tc, th;
    unsigned code = 0;
    unsigned char val;
    int i, j, k, num, Li[17];
    int len;
    HUFF *table;

    if (jpeg->fp + 2 > jpeg->fp1)
        goto err;
    len = (jpeg->fp[0] << 8 | jpeg->fp[1]) - 2;
    jpeg->fp += 2;

    while (len > 0) {
        if (jpeg->fp + 17 > jpeg->fp1)
            goto err;
        val = jpeg->fp[0];

        tc = (val >> 4) & 0x0f; // テーブルクラス(DC/AC成分セレクタ)
        th =  val       & 0x0f; // テーブルヘッダ(何枚目のプレーンか)
        table = &(jpeg->huff[tc][th]);

        num = 0;
        k = 0;
        for (i = 1; i <= 16; i++) {
            Li[i] = jpeg->fp[i];
            num += Li[i];
            for (j = 0; j < Li[i]; j++)
                table->size[k++] = i;
        }
        table->elem = num;
        jpeg->fp += 17;

        k=0;
        code=0;
        i = table->size[0];
        while (k < num) {
            while (table->size[k] == i)
                table->code[k++] = code++;
            if (k >= num)
                break;
            do {
                code <<= 1;
                i++;
            } while (table->size[k] != i);
        }

        if (jpeg->fp + num > jpeg->fp1)
            goto err;
        for (k = 0; k < num; k++)
            table->value[k] = jpeg->fp[k];
        jpeg->fp += num;

        len -= 18 + num;
    }
    return 0;
err:
    jpeg->fp = jpeg->fp1;
    return 1;
}

int jpeg_init(JPEG *jpeg)
{
    unsigned char c;
    int r = 0, i;
    jpeg->width = 0;
    jpeg->mcu_preDC[0] = 0;
    jpeg->mcu_preDC[1] = 0;
    jpeg->mcu_preDC[2] = 0;
    jpeg->n_dqt = 0;
    jpeg->max_h = 0;
    jpeg->max_v = 0;
    jpeg->bit_remain = 0;
    jpeg->bit_buff   = 0;
    // DRIリセット無し
    jpeg->interval = 0;

    for (;;) {
        if (jpeg->fp + 2 > jpeg->fp1)
            goto err;
        if (jpeg->fp[0] != 0xff)
            goto err0;
        c = jpeg->fp[1];
        jpeg->fp += 2;
        if (c == 0xd8)
            continue; /* SOI */
        if (c == 0xd9)
            goto err; /* EOI */

        if (c == 0xc0)
            jpeg_sof(jpeg);
        else if (c == 0xc4)
            jpeg_dht(jpeg);
        else if (c == 0xdb)
            jpeg_dqt(jpeg);
        else if (c == 0xdd) {
            if (jpeg->fp + 4 > jpeg->fp1)
                goto err;
            jpeg->interval = jpeg->fp[2] << 8 | jpeg->fp[3];
            jpeg->fp += 4;
        } else if (c == 0xda) {
            if (jpeg->fp + 3 > jpeg->fp1)
                goto err;
            jpeg->scan_count = jpeg->fp[2];
            jpeg->fp += 3;
            if (jpeg->fp + jpeg->scan_count * 2 > jpeg->fp1)
                goto err;
            for (i = 0; i < jpeg->scan_count; i++) {
                jpeg->scan_id[i] = jpeg->fp[0];
                jpeg->scan_dc[i] = jpeg->fp[1] >> 4;   // DC Huffman Table
                jpeg->scan_ac[i] = jpeg->fp[1] & 0x0F; // AC Huffman Table
                jpeg->fp += 2;
            }
            jpeg->fp += 3; /* 3bytes skip */
            goto fin;
        } else {
            /* 未対応 */
            if (jpeg->fp + 2 > jpeg->fp1)
                goto err;
            jpeg->fp += jpeg->fp[0] << 8 | jpeg->fp[1];
        }
    }
err:
    jpeg->fp = jpeg->fp1;
err0:
    r++;
fin:
    return r;
}

// MCU decode

// デコード
void jpeg_decode_init(JPEG *jpeg)
{
    int i, j;

    for (i = 0; i < jpeg->scan_count; i++) {
        // i:scan
        for (j = 0; j < jpeg->compo_count; j++) {
            // j:frame
            if (jpeg->scan_id[i] == jpeg->compo_id[j]) {
                jpeg->scan_h[i]  = jpeg->compo_h[j];
                jpeg->scan_v[i]  = jpeg->compo_v[j];
                jpeg->scan_qt[i] = jpeg->compo_qt[j];
                break;
            }
        }
    }
    jpeg->mcu_width  = jpeg->max_h * 8;
    jpeg->mcu_height = jpeg->max_v * 8;
    
    for (i = 0; i < 32 * 32 * 4; i++)
        jpeg->mcu_buf[i] = 0x80;

    for (i = 0; i < jpeg->scan_count; i++)
        jpeg->mcu_yuv[i] = jpeg->mcu_buf + (i << 10);
    return;
}

// ハフマン 1シンボル復号
int jpeg_huff_decode(JPEG *jpeg, int tc, int th)
{
    HUFF *h = &(jpeg->huff[tc][th]);
    int code,size,k,v;
    code = 0;
    size = 0;
    k = 0;
    while( size < 16 ){
        size++;
        v = get_bits(jpeg,1);
        if(v < 0){
            return v;
        }
        code = (code << 1) | v;

        while(h->size[k] == size){
            if(h->code[k] == code){
                return h->value[k];
            }
            k++;
        }
    }

    return -1;
}

void jpeg_idct_init(int base_img[64][64])
{
    int u, v, m, n;
    int tmpm[8], tmpn[8];
    int cost[32];
    cost[ 0] =  32768; cost[ 1] =  32138; cost[ 2] =  30274; cost[ 3] =  27246; cost[ 4] =  23170; cost[ 5] =  18205; cost[ 6] =  12540; cost[ 7] =   6393;
    cost[ 8] =      0; cost[ 9] =  -6393; cost[10] = -12540; cost[11] = -18205; cost[12] = -23170; cost[13] = -27246; cost[14] = -30274; cost[15] = -32138;
    for (u = 0; u < 16; u++)
        cost[u + 16] = - cost[u];

    for (u = 0; u < 8; u++) {
        {
            int i=u, d=u*2;
            if (d == 0)
                i = 4;
            for (m = 0; m < 8; m++){
                tmpm[m] = cost[i]; // 横のCos波形
                i=(i+d)&31;
            }
        }
        for (v = 0; v < 8; v++) {
            {
                int i=v,d=v*2;
                if (d == 0)
                    i=4;
                for (n = 0; n < 8; n++){
                    tmpn[n] = cost[i]; // 縦のCos波形
                    i=(i+d)&31;
                }
            }
            // 掛け算して基底画像に
            for (m = 0; m < 8; m++) {
                for (n = 0; n < 8; n++) {
                    base_img[u * 8 + v][m * 8 + n] = (tmpm[m] * tmpn[n])>>15;
                }
            }
        }
    }
    return;
}

void jpeg_idct(int *block, int *dest, int base_img[64][64])
{
    int i, j ,k;

    for (i = 0; i < 64; i++)
        dest[i] = 0;

    for (i = 0; i < 64; i++) {
        k = block[i];
        if(k) { //0係数はステ
            for (j = 0; j < 64; j++) {
                dest[j] += k * base_img[i][j];
            }
        }
    }
    // 固定小数点を元に戻す+ 4で割る
    for (i = 0; i < 64; i++)
        dest[i] >>= 17;
    return;
}

// 符号化された数値を元に戻す
int jpeg_get_value(JPEG *jpeg, int size)
{
    int val = 0;
    if (size) {
        val = get_bits(jpeg,size);
        if (val < 0)
            val = 0x10000 | (0 - val);
        else if (!(val & (1<<(size-1))))
            val -= (1 << size) - 1;
    }
    return val;
}

// ---- ブロックのデコード ---
// ハフマンデコード＋逆量子化＋逆ジグザグ
int jpeg_decode_huff(JPEG *jpeg, int scan, int *block, UCHAR *zigzag_table)
{
    int size, val, run, index;
    int *pQt = (int *)(jpeg->dqt[jpeg->scan_qt[scan]]);

    // DC
    size = jpeg_huff_decode(jpeg,0,jpeg->scan_dc[scan]);
    if(size < 0)
        return 0;
    val = jpeg_get_value(jpeg,size);
    jpeg->mcu_preDC[scan] += val;
    block[0] = jpeg->mcu_preDC[scan] * pQt[0];

    //AC復号
    index = 1;
    while(index<64)
    {
        size = jpeg_huff_decode(jpeg,1,jpeg->scan_ac[scan]);
        if(size < 0)
            break;
        // EOB
        if(size == 0)
            break;
        
        // RLE
        run  = (size>>4)&0xF;
        size = size & 0x0F;
        
        val = jpeg_get_value(jpeg,size);
        if(val>=0x10000) {
            //マーカ発見
            return val;
        }

        // ZRL
        while (run-- > 0)
            block[ zigzag_table[index++] ] = 0;
        
        block[ zigzag_table[index] ] = val * pQt[index];
        index++;
    }
    while(index<64)
        block[zigzag_table[index++]]=0;
    return 0;
}

// ブロック (補間かけるには、ここで)
// リサンプリング
void jpeg_mcu_bitblt(int *src, int *dest, int width,
                     int x0, int y0, int x1, int y1)
{
    int w, h;
    int x, y, y2;
    w = x1 - x0;
    h = y1 - y0;
    dest += y0 * width + x0;
    width -= w;

    for (y = 0; y < h; y++) {
        y2 = (y * 8 / h) * 8;
        for (x = 0; x < w; x++)
            *dest++ = src[y2 + (x * 8 / w)];
        dest += width;
    }
}

// MCU一個変換
void jpeg_decode_mcu(JPEG *jpeg, UCHAR *zigzag_table)
{
    int scan, val;
    int h, v;
    int *p, hh, vv;
    int block[64], dest[64];

    // mcu_width x mcu_heightサイズのブロックを展開
    for (scan = 0; scan < jpeg->scan_count; scan++) {
        hh = jpeg->scan_h[scan];
        vv = jpeg->scan_v[scan];
        for (v = 0; v < vv; v++) {
            for (h = 0; h < hh; h++) {
                // ブロック(8x8)のデコード
                val = jpeg_decode_huff(jpeg, scan, block, zigzag_table);

                // 逆DCT
                jpeg_idct(block, dest, jpeg->base_img);
                // リサンプリング

                // 書き込みバッファ
                p = jpeg->mcu_buf + (scan << 10);

                // 拡大転送
                jpeg_mcu_bitblt(dest, p, jpeg->mcu_width,
                    jpeg->mcu_width * h / hh, jpeg->mcu_height * v / vv,
                    jpeg->mcu_width * (h + 1) / hh, jpeg->mcu_height * (v + 1) / vv);
            }
        }
    }
}

// YCrCb=>RGB

void jpeg_decode_yuv(JPEG *jpeg, int h, int v, unsigned char *rgb, int b_type)
{
    int x0, y0, x, y, x1, y1;
    int *py;
    int Y12, V;
    int mw, mh, w;

    mw = jpeg->mcu_width;
    mh = jpeg->mcu_height;

    x0 = h * jpeg->max_h * 8;
    y0 = v * jpeg->max_v * 8;

    x1 = jpeg->width - x0;
    if (x1 > mw)
        x1 = mw;
    y1 = jpeg->height - y0;
    if (y1 > mh)
        y1 = mh;
    
    py = jpeg->mcu_buf;
    rgb += (y0 * jpeg->width_buf + x0) * (b_type & 0x7f);
    w = (jpeg->width_buf - x1) * (b_type & 0x7f);

    for (y = 0; y < y1; y++) {
        for (x = 0; x < x1; x++) {
            int b, g, r;
            Y12 = py[0] << 12;
        //    U = py[1024]; /* pu */
            V = py[2048]; /* pv */

            /* blue */
            b = 128 + ((Y12 - V * 4 + py[1024] /* pu */ * 0x1C59) >> 12);
            if (b & 0xffffff00)
                b = (~b) >> 24;

            /* green */
            g = 128 + ((Y12 - V * 0x0B6C) >> 12);
            if (g & 0xffffff00)
                g = (~g) >> 24;

            /* red */
            r = 128 + ((Y12 + V * 0x166E) >> 12);
            if (r & 0xffffff00)
                r = (~r) >> 24;
            if (b_type == 0x0004) {
                rgb[0] = b;
                rgb[1] = g;
                rgb[2] = r;
                py++;
                rgb += 4;
            } else if (b_type == 0x0002) {
                r &= 0xff;
                g &= 0xff;
                b &= 0xff;
                *(short *) rgb = PIXEL16(r >> 3, g >> 2, b >> 3);
                rgb += 2;
            }
        }
        py += mw - x1;
        rgb += w;
    }
    return;
}

#define INIT_ZTABLE(i, b0, b1, b2, b3)    *(int *) &zigzag_table[i] = b0 | b1 << 8 | b2 << 16 | b3 << 24

void jpeg_decode(JPEG *jpeg, UCHAR *rgb, int b_type)
{
    int h_unit, v_unit;
    int mcu_count, h, v;

    UCHAR zigzag_table[64];

    INIT_ZTABLE( 0,  0,  1,  8, 16); INIT_ZTABLE( 4,  9,  2,  3, 10);
    INIT_ZTABLE( 8, 17, 24, 32, 25); INIT_ZTABLE(12, 18, 11,  4,  5);
    INIT_ZTABLE(16, 12, 19, 26, 33); INIT_ZTABLE(20, 40, 48, 41, 34);
    INIT_ZTABLE(24, 27, 20, 13,  6); INIT_ZTABLE(28,  7, 14, 21, 28);
    INIT_ZTABLE(32, 35, 42, 49, 56); INIT_ZTABLE(36, 57, 50, 43, 36);
    INIT_ZTABLE(40, 29, 22, 15, 23); INIT_ZTABLE(44, 30, 37, 44, 51);
    INIT_ZTABLE(48, 58, 59, 52, 45); INIT_ZTABLE(52, 38, 31, 39, 46);
    INIT_ZTABLE(56, 53, 60, 61, 54); INIT_ZTABLE(60, 47, 55, 62, 63);

    // MCUサイズ計算
    jpeg_decode_init(jpeg);

    h_unit = (jpeg->width + jpeg->mcu_width - 1) / jpeg->mcu_width;
    v_unit = (jpeg->height + jpeg->mcu_height - 1) / jpeg->mcu_height;

    // 1ブロック展開するかもしれない
    mcu_count = 0;
    for (v = 0; v < v_unit; v++) {
        for (h = 0; h < h_unit; h++) {
            mcu_count++;
            jpeg_decode_mcu(jpeg, zigzag_table);
            jpeg_decode_yuv(jpeg, h, v, rgb, b_type & 0x7fff);
            if (jpeg->interval > 0 && mcu_count >= jpeg->interval) {
                // RSTmマーカをすっ飛ばす(FF hoge)
                // hogeは読み飛ばしてるので、FFも飛ばす
                jpeg->bit_remain -= (jpeg->bit_remain & 7);
                jpeg->bit_remain -= 8;
                jpeg->mcu_preDC[0] = 0;
                jpeg->mcu_preDC[1] = 0;
                jpeg->mcu_preDC[2] = 0;
                mcu_count = 0;
            }
        }
    }
    return;
}
