/*===========================================================================*
 * moutput.c                                                                 *
 *                                                                           *
 *      Procedures concerned with quantization and RLE                       *
 *                                                                           *
 * EXPORTED PROCEDURES:                                                      *
 *      mp_quant_zig_block                                                   *
 *      mp_rle_huff_block                                                    *
 *      mp_rle_huff_pblock                                                   *
 *                                                                           *
 *===========================================================================*/

/*
 * Copyright (c) 1995 The Regents of the University of California.
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice and the following
 * two paragraphs appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

/*
 *  $Header: /n/charlie-brown/project/mm/mpeg/mpeg_dist/mpeg_encode/RCS/moutput.c,v 1.12 1995/01/19 23:08:49 eyhung Exp $
 *  $Log: moutput.c,v $
 * Revision 1.12  1995/01/19  23:08:49  eyhung
 * Changed copyrights
 *
 * Revision 1.11  1993/07/22  22:23:43  keving
 * nothing
 *
 * Revision 1.10  1993/06/30  20:06:09  keving
 * nothing
 *
 * Revision 1.9  1993/06/03  21:08:08  keving
 * nothing
 *
 * Revision 1.8  1993/02/24  18:57:19  keving
 * nothing
 *
 * Revision 1.7  1993/02/23  22:58:36  keving
 * nothing
 *
 * Revision 1.6  1993/02/23  22:54:56  keving
 * nothing
 *
 * Revision 1.5  1993/02/17  23:18:20  dwallach
 * checkin prior to keving's joining the project
 *
 * Revision 1.4  1993/01/18  10:20:02  dwallach
 * *** empty log message ***
 *
 * Revision 1.3  1993/01/18  10:17:29  dwallach
 * RCS headers installed, code indented uniformly
 *
 * Revision 1.3  1993/01/18  10:17:29  dwallach
 * RCS headers installed, code indented uniformly
 *
 */


/*==============*
 * HEADER FILES *
 *==============*/

#include "all.h"
#include "mtypes.h"
#include "mproto.h"
#include "huff.h"


/*==================*
 * STATIC VARIABLES *
 *==================*/

/* ZAG[i] is the natural-order position of the i'th element of zigzag order. */
static int ZAG[] =
{
    0, 1, 8, 16, 9, 2, 3, 10,
    17, 24, 32, 25, 18, 11, 4, 5,
    12, 19, 26, 33, 40, 48, 41, 34,
    27, 20, 13, 6, 7, 14, 21, 28,
    35, 42, 49, 56, 57, 50, 43, 36,
    29, 22, 15, 23, 30, 37, 44, 51,
    58, 59, 52, 45, 38, 31, 39, 46,
    53, 60, 61, 54, 47, 55, 62, 63
};


/*
 * possible optimization: reorder the qtable in the correct zigzag order, to
 * reduce the number of necessary lookups
 *
 * this table comes from the MPEG draft, p. D-16, Fig. 2-D.15.
 */
static int qtable[] =
{
    8, 16, 19, 22, 26, 27, 29, 34,
    16, 16, 22, 24, 27, 29, 34, 37,
    19, 22, 26, 27, 29, 34, 34, 38,
    22, 22, 26, 27, 29, 34, 37, 40,
    22, 26, 27, 29, 32, 35, 40, 48,
    26, 27, 29, 32, 35, 40, 48, 58,
    26, 27, 29, 34, 38, 46, 56, 69,
    27, 29, 35, 38, 46, 56, 69, 83};


/*=====================*
 * EXPORTED PROCEDURES *
 *=====================*/


void    UnQuantZig(FlatBlock in, Block out, int qscale, boolean iblock)
{
    register int index;
    int     start;
    int     position;
    register int            qentry;
    int     level, coeff;
    register int16 temp;

    if ( iblock )
    {
        ((int16 *)out)[0] = (int16)(in[0]*qtable[0]);

        start = 1;
    }
    else
        start = 0;

    for ( index = start; index < DCTSIZE_SQ; index++ )
    {
        position = ZAG[index];

        if (iblock)
            qentry = qtable[position] * qscale;
        else
            qentry = 16 * qscale;

        level = in[index];
        coeff = (level * qentry) >> 3;
        if (level < 0) {
            coeff += (coeff & 1);
        } else {
            coeff -= (coeff & 1);
        }

        ((int16 *)out)[position] = coeff;
    }

#ifdef BLEAH
    for ( index = 0; index < 64; index++ )
        fprintf(stdout, "DCT[%d] = %d\n", index,
                ((int16 *)out)[index]);
#endif
}



/*
 * --------------------------------------------------------------
 *
 * mp_quant_zig_block --
 *
 * Quantizes and zigzags a block -- removing information
 *
 * Results: TRUE iff resulting 'out' is non-zero, FALSE if all
 *          zero
 *
 * Side effects: Modifies the out block.
 *
 * --------------------------------------------------------------
 */
boolean mp_quant_zig_block(Block in, FlatBlock out, int qscale, int iblock)
{
    register int i;
    register int y, x;
    register int16 temp;
    register int qentry;
    int start;
    boolean nonZero = FALSE;

    DBG_PRINT(("mp_quant_zig_block...\n"));
    if (iblock) {
        /*
         * the DC coefficient is handled specially -- it's not
         * sensitive to qscale, but everything else is
         */
        temp = ((int16 *) in)[ZAG[0]];
        qentry = qtable[ZAG[0]];
        if (temp < 0) {
            temp = -temp;
            temp += qentry >> 1;
            temp /= qentry;
            temp = -temp;
        } else {
            temp += qentry >> 1;
            temp /= qentry;
        }
        if ( temp != 0 )
            nonZero = TRUE;
        out[0] = temp;
        start = 1;
    } else
        start = 0;

    for (i = start; i < DCTSIZE_SQ; i++) {
        x = ZAG[i] % 8;
        y = ZAG[i] / 8;
        temp = in[y][x];
        DBG_PRINT(("    in[%d][%d] = %d;  ", y, x, temp));

        if (iblock)
            qentry = qtable[ZAG[i]] * qscale;
        else
            qentry = 16 * qscale;

        DBG_PRINT(("quantized with %d = ", qentry));

        if (temp < 0) {
            temp = -temp;
            temp *= 8;
            temp += qentry >> 1;
            temp /= qentry;
            temp = -temp;
        } else {
            temp *= 8;
            temp += qentry >> 1;
            temp /= qentry;
        }
        if ( temp != 0 )
            nonZero = TRUE;
        out[i] = temp;
        DBG_PRINT(("%d\n", temp));
    }

    return nonZero;
}



/*
 * --------------------------------------------------------------
 *
 * mp_rle_huff_block --
 *
 * Given a FlatBlock, generates the Huffman bits
 *
 * Results: None.
 *
 * Side effects: Output bits changed
 *
 * --------------------------------------------------------------
 */

void    mp_rle_huff_block(FlatBlock in, BitBucket *out)
{
    register int i;
    register int nzeros = 0;
    register int16 cur;
    register int16 acur;
    register uint32 code;
    register int nbits;

    /*
     * yes, Virginia, we start at 1.  The DC coefficient is handled
     * specially, elsewhere.  Not here.
     */
    for (i = 1; i < DCTSIZE_SQ; i++) {
        cur = in[i];
        acur = ABS(cur);
        if (cur) {
            if (nzeros < HUFF_MAXRUN && acur < huff_maxlevel[nzeros]) {
                /*
                 * encode using the Huffman tables
                 */

                DBG_PRINT(("rle_huff %02d: Run %02d, Level %02d\n", i, nzeros, cur));
                assert(cur);

                code = (huff_table[nzeros])[acur];
                nbits = (huff_bits[nzeros])[acur];

                assert(nbits);

                if (cur < 0)
                    code |= 1;  /* the sign bit */
                Bitio_Write(out, code, nbits);
            } else {
                /*
                 * encode using the escape code
                 */
                DBG_PRINT(("Escape\n"));
                Bitio_Write(out, 0x1, 6);       /* ESCAPE */
                DBG_PRINT(("Run Length\n"));
                Bitio_Write(out, nzeros, 6);    /* Run-Length */

                assert(cur != 0);

                /*
                 * this shouldn't happen, but the other
                 * choice is to bomb out and dump core...
                 */
                if (cur < -255)
                    cur = -255;
                else if (cur > 255)
                    cur = 255;

                DBG_PRINT(("Level\n"));
                if (acur < 128) {
                    Bitio_Write(out, cur, 8);
                } else {
                    if (cur < 0) {
                        Bitio_Write(out, 0x8001 + cur + 255, 16);
                    } else
                        Bitio_Write(out, cur, 16);
                }
            }
            nzeros = 0;
        } else
            nzeros++;
    }
    DBG_PRINT(("End of block\n"));
    Bitio_Write(out, 0x2, 2);   /* end of block marker */
}



/*
 * --------------------------------------------------------------
 *
 * mp_rle_huff_pblock --
 *
 * Given a FlatBlock, generates the Huffman bits for P DCT
 *
 * Results: None.
 *
 * Side effects: Output bits changed
 *
 * --------------------------------------------------------------
 */

void    mp_rle_huff_pblock(FlatBlock in, BitBucket *out)
{
    register int i;
    register int nzeros = 0;
    register int16 cur;
    register int16 acur;
    register uint32 code;
    register int nbits;
    boolean first_dct = TRUE;

    /*
     * yes, Virginia, we start at 0.
     */
    for (i = 0; i < DCTSIZE_SQ; i++) {
        cur = in[i];
        acur = ABS(cur);
        if (cur) {
            if (nzeros < HUFF_MAXRUN && acur < huff_maxlevel[nzeros]) {
                /*
                 * encode using the Huffman tables
                 */

                DBG_PRINT(("rle_huff %02d: Run %02d, Level %02d\n", i, nzeros, cur));
                assert(cur);

                if ( first_dct && (nzeros == 0) && (acur == 1) )
                {
                    /* actually, only needs = 0x2 */
                    code = (cur == 1) ? 0x2 : 0x3;
                    nbits = 2;
                }
                else
                {
                    code = (huff_table[nzeros])[acur];
                    nbits = (huff_bits[nzeros])[acur];
                }

                assert(nbits);

                if (cur < 0)
                    code |= 1;  /* the sign bit */
                Bitio_Write(out, code, nbits);
                first_dct = FALSE;
            } else {
                /*
                 * encode using the escape code
                 */
                DBG_PRINT(("Escape\n"));
                Bitio_Write(out, 0x1, 6);       /* ESCAPE */
                DBG_PRINT(("Run Length\n"));
                Bitio_Write(out, nzeros, 6);    /* Run-Length */

                assert(cur != 0);

                /*
                 * this shouldn't happen, but the other
                 * choice is to bomb out and dump core...
                 */
                if (cur < -255)
                    cur = -255;
                else if (cur > 255)
                    cur = 255;

                DBG_PRINT(("Level\n"));
                if (acur < 128) {
                    Bitio_Write(out, cur, 8);
                } else {
                    if (cur < 0) {
                        Bitio_Write(out, 0x8001 + cur + 255, 16);
                    } else
                        Bitio_Write(out, cur, 16);
                }

                first_dct = FALSE;
            }
            nzeros = 0;
        } else
            nzeros++;
    }

    /* actually, should REALLY return FALSE and not use this! */
    if ( first_dct )    /* have to give a first_dct even if all 0's */
    {
        fprintf(stdout, "HUFF called with all-zero coefficients\n");
        fprintf(stdout, "exiting...\n");
        exit(1);
    }

    DBG_PRINT(("End of block\n"));
    Bitio_Write(out, 0x2, 2);   /* end of block marker */
}



