/*------------------------------------------------------------------------
 *
 * Copyright (c) 1997-1998 by Cornell University.
 * 
 * See the file "license.txt" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * ppmtogif
 *
 * Sugata sugata@cs.cornell.edu
 *
 * Usage : ppmtojpg inputPPm outputJPG
 *
 * Reads a PPM file and encode it into a JPG file.
 *
 *------------------------------------------------------------------------
 */

#include "dvmbasic.h"
#include "dvmjpeg.h"
#include "dvmcolor.h"
#include "dvmpnm.h"

#ifdef WIN32
#include "windows.h"
#endif


void ReadPPM(char *, PnmHdr **, ByteImage **, ByteImage **, ByteImage **);
int main(int argc, char *argv[])
{

#ifdef DO_TIMING
  LARGE_INTEGER curr;
  __int64 start, stop, f;
#endif

    FILE *out;
    int i;
    PnmHdr *hdr;
    ByteImage *r, *g, *b, *y, *u, *v, *cr, *cg, *cb;
    ScImage *scy, *scu, *scv;
    int w, h, bw, bh;
    BitStream *outBs;
    BitParser *outBp;
    JpegHdr *jpegHdr;
    JpegScanHdr *scanHdr;
    JpegHuffTable *huffTable;

    /*
     * Check arguments and read in the input PPM.
     */
    if (argc != 3) {
	fprintf(stderr, "usage : ppmtojpg <inputppm> <outputjpg>\n");
	exit(1);
    }
    out = fopen(argv[2], "wb");
    ReadPPM(argv[1], &hdr, &r, &g, &b);

#ifdef DO_TIMING
  QueryPerformanceFrequency(&curr);
  f = curr.QuadPart;
  QueryPerformanceCounter(&curr);
  start = curr.QuadPart;
#endif

    /*
     * Find the dimension of the input image.
     */
    w = PnmHdrGetWidth(hdr);
    h = PnmHdrGetHeight(hdr);

    /*
     * Initialize the JPEG header.  We uses the standard quantization
     * table and the standard Huffman table.  We are encoding the image
     * using 4:2:0 sampling, so the block width and height are 2x2 for y
     * 1x1 for u, and 1x1 for v.  We also make u and v plane share the same
     * quantization table.
     */
    jpegHdr = JpegHdrNew();
    JpegHdrSetWidth(jpegHdr, w);
    JpegHdrSetHeight(jpegHdr, h);
    JpegHdrSetNumOfComponents(jpegHdr, 3);
    JpegHdrSetRestartInterval(jpegHdr, 0);
    JpegHdrSetPrecision(jpegHdr, 8);
    JpegHdrSetBlockWidth(jpegHdr, 0, 2);
    JpegHdrSetBlockWidth(jpegHdr, 1, 1);
    JpegHdrSetBlockWidth(jpegHdr, 2, 1);
    JpegHdrSetMaxBlockWidth(jpegHdr, 2);
    JpegHdrSetBlockHeight(jpegHdr, 0, 2);
    JpegHdrSetBlockHeight(jpegHdr, 1, 1);
    JpegHdrSetBlockHeight(jpegHdr, 2, 1);
    JpegHdrSetMaxBlockHeight(jpegHdr, 2);
    JpegHdrSetQtId(jpegHdr, 0, 0);
    JpegHdrSetQtId(jpegHdr, 1, 1);
    JpegHdrSetQtId(jpegHdr, 2, 1);
    JpegHdrStdQtInit(jpegHdr);
    JpegHdrStdHtInit(jpegHdr);

    /*
     * Initialize the scan header.
     */
    scanHdr = JpegScanHdrNew();
    JpegScanHdrSetNumOfComponents(scanHdr, 3);
    JpegScanHdrSetScanId(scanHdr, 0, 0);
    JpegScanHdrSetScanId(scanHdr, 1, 1);
    JpegScanHdrSetScanId(scanHdr, 2, 2);
    JpegScanHdrSetDcId(scanHdr, 0, 0);
    JpegScanHdrSetDcId(scanHdr, 1, 1);
    JpegScanHdrSetDcId(scanHdr, 2, 1);
    JpegScanHdrSetAcId(scanHdr, 0, 0);
    JpegScanHdrSetAcId(scanHdr, 1, 1);
    JpegScanHdrSetAcId(scanHdr, 2, 1);

    /*
     * Creates and initialize a new huffman table for encoding.
     */
    huffTable = JpegHuffTableNew(3);
    JpegHuffTableInit(jpegHdr, scanHdr, huffTable);

    /*
     * Creates a new bitstream.  Assumes the the JPEG image is smaller
     * than the PPM, so a BitStream of size 3*w*h is enough for the output
     * JPEG.
     */
    outBs = BitStreamNew(3*w*h); 
    outBp = BitParserNew();
    BitParserWrap(outBp, outBs);

    /*
     * Encodes the image start code, quantization table, huffman table,
     * header and scan header.
     */
    JpegStartCodeEncode(outBp);
    i = JpegHdrQtEncode(jpegHdr, outBp);
    i = JpegHdrHtEncode(jpegHdr, outBp);
    i = JpegHdrEncode(jpegHdr, 1, outBp);
    i = JpegScanHdrEncode(jpegHdr, scanHdr, outBp);
    
    /*
     * We clip the input r, g, b into strips of size w x 16, and encodes
     * each strips.
     */
    bw = w/8;
    scy = ScNew(bw, 2);
    scu = ScNew(bw/2, 1);
    scv = ScNew(bw/2, 1);
    y = ByteNew(w, 16);
    u = ByteNew(w/2, 8);
    v = ByteNew(w/2, 8);
    cr = ByteClip(r, 0, 0, 0, 0);
    cg = ByteClip(g, 0, 0, 0, 0);
    cb = ByteClip(b, 0, 0, 0, 0);
    JpegScanIncEncode420(jpegHdr, scanHdr, huffTable, NULL, scu, scv, outBp);
    bh = h >> 4;
    for (i = 0; i < bh; i++) {
	ByteReclip(r, 0, i<<4, w, 16, cr);
	ByteReclip(g, 0, i<<4, w, 16, cg);
	ByteReclip(b, 0, i<<4, w, 16, cb);
	RgbToYuv420(cr, cg, cb, y, u ,v);
	ByteToSc(y, scy);
	ByteToSc(u, scu);
	ByteToSc(v, scv);
	JpegScanIncEncode420(jpegHdr, scanHdr, huffTable, scy, scu, scv, outBp);
    }
    JpegScanIncEncode420(jpegHdr, scanHdr, huffTable, NULL, scu, scv, outBp);
    JpegEndCodeEncode(outBp);

#ifdef DO_TIMING
  QueryPerformanceCounter(&curr);
  stop = curr.QuadPart;
  printf("TOTAL TIME : %f ms\n", (stop - start)/(double)f*1000.0);
#endif

    BitStreamFileWriteSegment(outBs, out, 0, BitParserTell(outBp));

    /*
     * Clean up.
     */
    fclose(out);
    ByteFree(y);
    ByteFree(u);
    ByteFree(v);
    ByteFree(r);
    ByteFree(g);
    ByteFree(b);
    ScFree(scy);
    ScFree(scu);
    ScFree(scv);
    JpegHdrFree(jpegHdr);
    JpegScanHdrFree(scanHdr);
    JpegHuffTableFree(huffTable);

    return 0;
}