/*
 * libopenraw - ljpegdecompressor.cpp
 *
 * Copyright (C) 2007-2022 Hubert Figuière
 *
 * This library is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 */
/*
 * Code for JPEG lossless decoding.  Large parts are grabbed from the IJG
 * software, so:
 *
 * Copyright (C) 1991, 1992, Thomas G. Lane.
 * Part of the Independent JPEG Group's software.
 * See the file Copyright for more details.
 *
 * Copyright (c) 1993 Brian C. Smith, The Regents of the University
 * of California
 * All rights reserved.
 *
 * Copyright (c) 1994 Kongji Huang and Brian C. Smith.
 * Cornell University
 * 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 CORNELL UNIVERSITY 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 CORNELL
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * CORNELL UNIVERSITY 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 CORNELL UNIVERSITY HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */


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

#include <fcntl.h>
#include <algorithm>
#include <cstdint>

#include <boost/format.hpp>

#include <libopenraw/consts.h>
#include <libopenraw/debug.h>

#include "rawdata.hpp"
#include "exception.hpp"
#include "io/stream.hpp"
#include "trace.hpp"
#include "ljpegdecompressor.hpp"
#include "ljpegdecompressor_priv.hpp"

namespace OpenRaw {

using namespace Debug;

namespace Internals {


static void SkipVariable(IO::Stream *s);
static uint16_t Get2bytes (IO::Stream * s);
static int32_t  NextMarker(IO::Stream * );
static void GetSoi(DecompressInfo *dcPtr);
static void GetApp0(IO::Stream *);

LJpegDecompressor::LJpegDecompressor(IO::Stream *stream,
                                     RawContainer *container)
    : Decompressor(stream, container),
      m_slices(),
      m_mcuROW1(NULL), m_mcuROW2(NULL),
      m_buf1(NULL), m_buf2(NULL),
      m_bitsLeft(0),
      m_getBuffer(0)
{
}


LJpegDecompressor::~LJpegDecompressor()
{
    if(m_mcuROW1) {
        free(m_mcuROW1);
    }
    if(m_mcuROW2) {
        free(m_mcuROW2);
    }
    if(m_buf1) {
        free(m_buf1);
    }
    if(m_buf2) {
        free(m_buf2);
    }
}


void LJpegDecompressor::setSlices(const std::vector<uint16_t> & slices)
{
    uint16_t n = slices[0];
    m_slices.resize(n + 1);
    for(uint16_t i = 0; i < n; i++) {
        m_slices[i] = slices[1];
    }
    m_slices[n] = slices[2];
}

static uint32_t bitMask[] = {  0xffffffff, 0x7fffffff,
                               0x3fffffff, 0x1fffffff,
                               0x0fffffff, 0x07ffffff,
                               0x03ffffff, 0x01ffffff,
                               0x00ffffff, 0x007fffff,
                               0x003fffff, 0x001fffff,
                               0x000fffff, 0x0007ffff,
                               0x0003ffff, 0x0001ffff,
                               0x0000ffff, 0x00007fff,
                               0x00003fff, 0x00001fff,
                               0x00000fff, 0x000007ff,
                               0x000003ff, 0x000001ff,
                               0x000000ff, 0x0000007f,
                               0x0000003f, 0x0000001f,
                               0x0000000f, 0x00000007,
                               0x00000003, 0x00000001};

void FixHuffTbl (HuffmanTable *htbl);


/*
 *--------------------------------------------------------------
 *
 * FixHuffTbl --
 *
 *      Compute derived values for a Huffman table one the DHT marker
 *      has been processed.  This generates both the encoding and
 *      decoding tables.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *--------------------------------------------------------------
 */
void
FixHuffTbl (HuffmanTable *htbl)
{
    int32_t p, i, l, lastp, si;
    char huffsize[257];
    uint16_t huffcode[257];
    uint16_t code;
    int32_t size;
    int32_t value, ll, ul;

    /*
     * Figure C.1: make table of Huffman code length for each symbol
     * Note that this is in code-length order.
     */
    p = 0;
    for (l = 1; l <= 16; l++) {
        for (i = 1; i <= (int)htbl->bits[l]; i++)
            huffsize[p++] = (char)l;
    }
    huffsize[p] = 0;
    lastp = p;


    /*
     * Figure C.2: generate the codes themselves
     * Note that this is in code-length order.
     */
    code = 0;
    si = huffsize[0];
    p = 0;
    while (huffsize[p]) {
        while (((int)huffsize[p]) == si) {
            huffcode[p++] = code;
            code++;
        }
        code <<= 1;
        si++;
    }

    /*
     * Figure C.3: generate encoding tables
     * These are code and size indexed by symbol value
     * Set any codeless symbols to have code length 0; this allows
     * EmitBits to detect any attempt to emit such symbols.
     */
    memset(htbl->ehufsi, 0, sizeof(htbl->ehufsi));

    for (p = 0; p < lastp; p++) {
        htbl->ehufco[htbl->huffval[p]] = huffcode[p];
        htbl->ehufsi[htbl->huffval[p]] = huffsize[p];
    }

    /*
     * Figure F.15: generate decoding tables
     */
    p = 0;
    for (l = 1; l <= 16; l++) {
        if (htbl->bits[l]) {
            htbl->valptr[l] = p;
            htbl->mincode[l] = huffcode[p];
            p += htbl->bits[l];
            htbl->maxcode[l] = huffcode[p - 1];
        } else {
            htbl->maxcode[l] = -1;
        }
    }

    /*
     * We put in this value to ensure HuffDecode terminates.
     */
    htbl->maxcode[17] = 0xFFFFFL;

    /*
     * Build the numbits, value lookup tables.
     * These table allow us to gather 8 bits from the bits stream,
     * and immediately lookup the size and value of the huffman codes.
     * If size is zero, it means that more than 8 bits are in the huffman
     * code (this happens about 3-4% of the time).
     */
    bzero (htbl->numbits, sizeof(htbl->numbits));
    for (p=0; p<lastp; p++) {
        size = huffsize[p];
        if (size <= 8) {
            value = htbl->huffval[p];
            code = huffcode[p];
            ll = code << (8-size);
            if (size < 8) {
                ul = ll | bitMask[24+size];
            } else {
                ul = ll;
            }
            for (i=ll; i<=ul; i++) {
                htbl->numbits[i] = size;
                htbl->value[i] = value;
            }
        }
    }
}


#define RST0    0xD0	/* RST0 marker code */


#if 0
/*
 * The following variables keep track of the input buffer
 * for the JPEG data, which is read by ReadJpegData.
 */
uint8_t inputBuffer[JPEG_BUF_SIZE]; /* Input buffer for JPEG data */
int numInputBytes;		/* The total number of bytes in inputBuffer */
int maxInputBytes;		/* Size of inputBuffer */
int inputBufferOffset;		/* Offset of current byte */
#endif


/*
 * Code for extracting the next N bits from the input stream.
 * (N never exceeds 15 for JPEG data.)
 * This needs to go as fast as possible!
 *
 * We read source bytes into getBuffer and dole out bits as needed.
 * If getBuffer already contains enough bits, they are fetched in-line
 * by the macros get_bits() and get_bit().  When there aren't enough bits,
 * fillBitBuffer is called; it will attempt to fill getBuffer to the
 * "high water mark", then extract the desired number of bits.  The idea,
 * of course, is to minimize the function-call overhead cost of entering
 * fillBitBuffer.
 * On most machines MIN_GET_BITS should be 25 to allow the full 32-bit width
 * of getBuffer to be used.  (On machines with wider words, an even larger
 * buffer could be used.)
 */

#define BITS_PER_LONG	(8*sizeof(int32_t))
#define MIN_GET_BITS  (BITS_PER_LONG-7)	   /* max value for long getBuffer */

/*
 * bmask[n] is mask for n rightmost bits
 */
static int32_t bmask[] = {0x0000,
                          0x0001, 0x0003, 0x0007, 0x000F,
                          0x001F, 0x003F, 0x007F, 0x00FF,
                          0x01FF, 0x03FF, 0x07FF, 0x0FFF,
                          0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF};


/*
 * Lossless JPEG specifies data precision to be from 2 to 16 bits/sample.
 */
#define MinPrecisionBits 2
#define MaxPrecisionBits 16


/*
 *--------------------------------------------------------------
 *
 * DecoderStructInit --
 *
 *	Initalize the rest of the fields in the decompression
 *	structure.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */
void
LJpegDecompressor::DecoderStructInit (DecompressInfo *dcPtr)
    noexcept(false)
{
    int16_t ci,i;
    JpegComponentInfo *compPtr;
    int32_t mcuSize;

    /*
     * Check sampling factor validity.
     */
    for (ci = 0; ci < dcPtr->numComponents; ci++) {
        compPtr = &dcPtr->compInfo[ci];
        if ((compPtr->hSampFactor != 1) || (compPtr->vSampFactor != 1)) {
            throw DecodingException("Error: Downsampling is not supported.\n");
        }
    }

    /*
     * Prepare array describing MCU composition
     */
    if (dcPtr->compsInScan == 1) {
        dcPtr->MCUmembership[0] = 0;
    } else {
        if (dcPtr->compsInScan > 4) {
            throw DecodingException("Too many components for interleaved scan");
        }

        for (ci = 0; ci < dcPtr->compsInScan; ci++) {
            dcPtr->MCUmembership[ci] = ci;
        }
    }

    /*
     * Initialize mucROW1 and mcuROW2 which buffer two rows of
     * pixels for predictor calculation.
     */

    if ((m_mcuROW1 = (MCU *)malloc(dcPtr->imageWidth*sizeof(MCU)))==NULL) {
        throw DecodingException("Not enough memory for mcuROW1\n");
    }
    if ((m_mcuROW2 = (MCU *)malloc(dcPtr->imageWidth*sizeof(MCU)))==NULL) {
        throw DecodingException("Not enough memory for mcuROW2\n");
    }

    mcuSize=dcPtr->compsInScan * sizeof(ComponentType);
    if ((m_buf1 = (MCU)malloc(dcPtr->imageWidth * mcuSize)) == NULL) {
        throw DecodingException("Not enough memory for buf1\n");
    }
    if ((m_buf2 = (MCU)malloc(dcPtr->imageWidth * mcuSize)) == NULL) {
        throw DecodingException("Not enough memory for buf2\n");
    }

    for (i=0;i<dcPtr->imageWidth;i++) {
        m_mcuROW1[i] = m_buf1 + i * dcPtr->compsInScan;
        m_mcuROW2[i] = m_buf2 + i * dcPtr->compsInScan;
    }
}


/*
 *--------------------------------------------------------------
 *
 * fillBitBuffer --
 *
 *	Load up the bit buffer with at least nbits
 *	Process any stuffed bytes at this time.
 *
 * Results:
 *	None
 *
 * Side effects:
 *	The bitwise global variables are updated.
 *
 *--------------------------------------------------------------
 */
void
LJpegDecompressor::fillBitBuffer (IO::Stream * s,uint16_t nbits)
{
    uint8_t c, c2;

    while (m_bitsLeft < MIN_GET_BITS) {
        c = s->readByte();

        /*
         * If it's 0xFF, check and discard stuffed zero byte
         */
        if (c == 0xFF) {
            c2 = s->readByte();

            if (c2 != 0) {

                /*
                 * Oops, it's actually a marker indicating end of
                 * compressed data.  Better put it back for use later.
                 */
                s->seek(-2, SEEK_CUR);

                /*
                 * There should be enough bits still left in the data
                 * segment; if so, just break out of the while loop.
                 */
                if (m_bitsLeft >= nbits)
                    break;

                /*
                 * Uh-oh.  Corrupted data: stuff zeroes into the data
                 * stream, since this sometimes occurs when we are on the
                 * last show_bits(8) during decoding of the Huffman
                 * segment.
                 */
                c = 0;
            }
        }
        /*
         * OK, load c into getBuffer
         */
        m_getBuffer = (m_getBuffer << 8) | c;
        m_bitsLeft += 8;
    }
}



inline int32_t LJpegDecompressor::QuickPredict(int32_t col, int16_t curComp,
                                               MCU *curRowBuf,
                                               MCU *prevRowBuf,
                                               int32_t psv)
{
    int32_t left,upper,diag,leftcol;
    int32_t predictor;

    leftcol=col-1;
    upper=prevRowBuf[col][curComp];
    left=curRowBuf[leftcol][curComp];
    diag=prevRowBuf[leftcol][curComp];

    /*
     * All predictor are calculated according to psv.
     */
    switch (psv) {
    case 0:
        predictor = 0;
        break;
    case 1:
        predictor = left;
        break;
    case 2:
        predictor = upper;
        break;
    case 3:
        predictor = diag;
        break;
    case 4:
        predictor = left+upper-diag;
        break;
    case 5:
        predictor = left+((upper-diag)>>1);
        break;
    case 6:
        predictor = upper+((left-diag)>>1);
        break;
    case 7:
        predictor = (left+upper)>>1;
        break;
    default:
        LOGWARN("Warning: Undefined PSV\n");
        predictor = 0;
    }
    return predictor;
}

inline
int32_t LJpegDecompressor::show_bits8(IO::Stream * s)
{
    if (m_bitsLeft < 8) {
        fillBitBuffer(s, 8);
    }
    return (m_getBuffer >> (m_bitsLeft-8)) & 0xff;
}

inline
void LJpegDecompressor::flush_bits(uint16_t nbits)
{
    m_bitsLeft -= (nbits);
}

inline
int32_t LJpegDecompressor::get_bits(uint16_t nbits)
{
    if (m_bitsLeft < nbits)
        fillBitBuffer(m_stream, nbits);
    return ((m_getBuffer >> (m_bitsLeft -= (nbits)))) & bmask[nbits];
}

inline
int32_t LJpegDecompressor::get_bit()
{
    if (!m_bitsLeft)
        fillBitBuffer(m_stream, 1);
    return (m_getBuffer >> (--m_bitsLeft)) & 1;
}


inline
int32_t LJpegDecompressor::readBits(IO::Stream * s, uint16_t nbits)
{
    if (m_bitsLeft < nbits) {
        fillBitBuffer(s, nbits);
    }
    return ((m_getBuffer >> (m_bitsLeft -= (nbits)))) & bmask[nbits];
}



/*
 *--------------------------------------------------------------
 *
 * PmPutRow --
 *
 *      Output one row of pixels stored in RowBuf.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      One row of pixels are write to file pointed by outFile.
 *
 *--------------------------------------------------------------
 */
inline void
LJpegDecompressor::PmPutRow(MCU* RowBuf, int32_t numComp, int32_t numCol, int32_t Pt)
{
    // TODO this might be wrong in 8 bits...
    // original code was using putc which *i think* was a problem for
    // 16bpp
    int32_t comp;
    int32_t col;
    uint16_t v;

    for (col = 0; col < numCol; col++) {
        for (comp = 0; comp < numComp; comp++) {
            v = RowBuf[col][comp]<<Pt;
            m_output->append(v);
        }
    }
}

/*
 *--------------------------------------------------------------
 *
 * HuffDecode --
 *
 *	Taken from Figure F.16: extract next coded symbol from
 *	input stream.  This should becode a macro.
 *
 * Results:
 *	Next coded symbol
 *
 * Side effects:
 *	Bitstream is parsed.
 *
 *--------------------------------------------------------------
 */
inline int32_t
LJpegDecompressor::HuffDecode(HuffmanTable *htbl)
{
    int32_t rv;
    int32_t l, temp;
    int32_t code;

    /*
     * If the huffman code is less than 8 bits, we can use the fast
     * table lookup to get its value.  It's more than 8 bits about
     * 3-4% of the time.
     */
    code = show_bits8(m_stream);
    if (htbl->numbits[code]) {
        flush_bits(htbl->numbits[code]);
        rv=htbl->value[code];
    }  else {
        flush_bits(8);
        l = 8;
        while (code > htbl->maxcode[l]) {
            temp = get_bit();
            code = (code << 1) | temp;
            l++;
        }

        /*
         * With garbage input we may reach the sentinel value l = 17.
         */

        if (l > 16) {
            //LOGWARN("Corrupt JPEG data: bad Huffman code %d\n", l);
            rv = 0;		/* fake a zero as the safest result */
        } else {
            rv = htbl->huffval[htbl->valptr[l] +
                               ((int)(code - htbl->mincode[l]))];
        }
    }
    return rv;
}

/*
 *--------------------------------------------------------------
 *
 * HuffExtend --
 *
 *	Code and table for Figure F.12: extend sign bit
 *
 * Results:
 *	The extended value.
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */
static const int32_t extendTest[16] =	/* entry n is 2**(n-1) */
{0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000};

// we can't bitshift -1. So we use 0xffffffff as a value.
// gcc complain about it otherwise.
#define EXTEND(n) (int32_t)(0xffffffff << n) + 1
static const int32_t extendOffset[16] =	/* entry n is (-1 << n) + 1 */
{
    0, EXTEND(1), EXTEND(2), EXTEND(3),
    EXTEND(4), EXTEND(5), EXTEND(6), EXTEND(7),
    EXTEND(8), EXTEND(9), EXTEND(10), EXTEND(11),
    EXTEND(12), EXTEND(13), EXTEND(14), EXTEND(15)
};

inline
void HuffExtend(int32_t & x, int32_t s) noexcept
{
    // s >= 16 is an overflow. And probably will lead to a corrupt
    // output but at least not crash.
    if (s < 16 && x < extendTest[s]) {
        (x) += extendOffset[s];
    }
}

/*
 *--------------------------------------------------------------
 *
 * HuffDecoderInit --
 *
 *	Initialize for a Huffman-compressed scan.
 *	This is invoked after reading the SOS marker.
 *
 * Results:
 *	None
 *
 * Side effects:
 *	None.
 *
 *--------------------------------------------------------------
 */
void
LJpegDecompressor::HuffDecoderInit (DecompressInfo *dcPtr)
    noexcept(false)
{
    int16_t ci;
    JpegComponentInfo *compptr;

    /*
     * Initialize static variables
     */
    m_bitsLeft = 0;

    for (ci = 0; ci < dcPtr->compsInScan; ci++) {
        compptr = dcPtr->curCompInfo[ci];
        /*
         * Make sure requested tables are present
         */
        if (dcPtr->dcHuffTblPtrs[compptr->dcTblNo] == NULL) {
            throw DecodingException("Error: Use of undefined Huffman table\n");
        }

        /*
         * Compute derived values for Huffman tables.
         * We may do this more than once for same table, but it's not a
         * big deal
         */
        FixHuffTbl (dcPtr->dcHuffTblPtrs[compptr->dcTblNo]);
    }

    /*
     * Initialize restart stuff
     */
    dcPtr->restartInRows = (dcPtr->restartInterval)/(dcPtr->imageWidth);
    dcPtr->restartRowsToGo = dcPtr->restartInRows;
    dcPtr->nextRestartNum = 0;
}

/*
 *--------------------------------------------------------------
 *
 * ProcessRestart --
 *
 *	Check for a restart marker & resynchronize decoder.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	BitStream is parsed, bit buffer is reset, etc.
 *
 *--------------------------------------------------------------
 */
void
LJpegDecompressor::ProcessRestart (DecompressInfo *dcPtr)
    noexcept(false)
{
    int32_t c, nbytes;

    /*
     * Throw away any unused bits remaining in bit buffer
     */
    nbytes = m_bitsLeft / 8;
    m_bitsLeft = 0;

    /*
     * Scan for next JPEG marker
     */
    do {
        do {			/* skip any non-FF bytes */
            nbytes++;
            c = m_stream->readByte();
        } while (c != 0xFF);
        do {			/* skip any duplicate FFs */
            /*
             * we don't increment nbytes here since extra FFs are legal
             */
            c = m_stream->readByte();
        } while (c == 0xFF);
    } while (c == 0);		/* repeat if it was a stuffed FF/00 */

    if (c != (RST0 + dcPtr->nextRestartNum)) {

        /*
         * Uh-oh, the restart markers have been messed up too.
         * Just bail out.
         */
        throw DecodingException("Error: Corrupt JPEG data. "
                                "Aborting decoding...\n");
    }

    /*
     * Update restart state
     */
    dcPtr->restartRowsToGo = dcPtr->restartInRows;
    dcPtr->nextRestartNum = (dcPtr->nextRestartNum + 1) & 7;
}

/*
 *--------------------------------------------------------------
 *
 * DecodeFirstRow --
 *
 *	Decode the first raster line of samples at the start of
 *      the scan and at the beginning of each restart interval.
 *	This includes modifying the component value so the real
 *      value, not the difference is returned.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Bitstream is parsed.
 *
 *--------------------------------------------------------------
 */
void LJpegDecompressor::DecodeFirstRow(DecompressInfo *dcPtr,
                                       MCU *curRowBuf)
{
    uint16_t curComp,ci;
    int32_t s,col,compsInScan,numCOL;
    JpegComponentInfo *compptr;
    int32_t Pr,Pt,d;
    HuffmanTable *dctbl;

    Pr=dcPtr->dataPrecision;
    Pt=dcPtr->Pt;
    compsInScan=dcPtr->compsInScan;
    numCOL=dcPtr->imageWidth;

    /*
     * the start of the scan or at the beginning of restart interval.
     */
    for (curComp = 0; curComp < compsInScan; curComp++) {
        ci = dcPtr->MCUmembership[curComp];
        compptr = dcPtr->curCompInfo[ci];
        dctbl = dcPtr->dcHuffTblPtrs[compptr->dcTblNo];

        /*
         * Section F.2.2.1: decode the difference
         */
        s = HuffDecode (dctbl);
        if (s) {
            d = get_bits(s);
            HuffExtend(d,s);
        } else {
            d = 0;
        }

        /*
         * Add the predictor to the difference.
         */
        curRowBuf[0][curComp]=d+(1<<(Pr-Pt-1));
    }

    /*
     * the rest of the first row
     */
    for (col=1; col<numCOL; col++) {
        for (curComp = 0; curComp < compsInScan; curComp++) {
            ci = dcPtr->MCUmembership[curComp];
            compptr = dcPtr->curCompInfo[ci];
            dctbl = dcPtr->dcHuffTblPtrs[compptr->dcTblNo];

            /*
             * Section F.2.2.1: decode the difference
             */
            s = HuffDecode (dctbl);
            if (s) {
                d = get_bits(s);
                HuffExtend(d,s);
            } else {
                d = 0;
            }

            /*
             * Add the predictor to the difference.
             */
            curRowBuf[col][curComp]=d+curRowBuf[col-1][curComp];
        }
    }

    if (dcPtr->restartInRows) {
        (dcPtr->restartRowsToGo)--;
    }
}

/*
 *--------------------------------------------------------------
 *
 * DecodeImage --
 *
 *      Decode the input stream. This includes modifying
 *      the component value so the real value, not the
 *      difference is returned.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Bitstream is parsed.
 *
 *--------------------------------------------------------------
 */
void
LJpegDecompressor::DecodeImage(DecompressInfo *dcPtr)
{
    int32_t s,d,col,row;
    int16_t curComp, ci;
    HuffmanTable *dctbl;
    JpegComponentInfo *compptr;
    int32_t predictor;
    int32_t numCOL,numROW,compsInScan;
    MCU *prevRowBuf,*curRowBuf;
    int32_t Pt,psv;

    numCOL = dcPtr->imageWidth;
    numROW=dcPtr->imageHeight;
    compsInScan=dcPtr->compsInScan;
    Pt=dcPtr->Pt;
    psv=dcPtr->Ss;
    prevRowBuf=m_mcuROW2;
    curRowBuf=m_mcuROW1;

    /*
     * Decode the first row of image. Output the row and
     * turn this row into a previous row for later predictor
     * calculation.
     */
    DecodeFirstRow(dcPtr,curRowBuf);
    PmPutRow(curRowBuf,compsInScan,numCOL,Pt);
    std::swap(prevRowBuf,curRowBuf);

    for (row=1; row<numROW; row++) {

        /*
         * Account for restart interval, process restart marker if needed.
         */
        if (dcPtr->restartInRows) {
            if (dcPtr->restartRowsToGo == 0) {
                ProcessRestart (dcPtr);

                /*
                 * Reset predictors at restart.
                 */
                DecodeFirstRow(dcPtr,curRowBuf);
                PmPutRow(curRowBuf,compsInScan,numCOL,Pt);
                std::swap(prevRowBuf,curRowBuf);
                continue;
            }
            dcPtr->restartRowsToGo--;
        }

        /*
         * The upper neighbors are predictors for the first column.
         */
        for (curComp = 0; curComp < compsInScan; curComp++) {
            ci = dcPtr->MCUmembership[curComp];
            compptr = dcPtr->curCompInfo[ci];
            dctbl = dcPtr->dcHuffTblPtrs[compptr->dcTblNo];

            /*
             * Section F.2.2.1: decode the difference
             */
            s = HuffDecode (dctbl);
            if (s) {
                d = get_bits(s);
                HuffExtend(d,s);
            } else {
                d = 0;
            }

            curRowBuf[0][curComp]=d+prevRowBuf[0][curComp];
        }

        /*
         * For the rest of the column on this row, predictor
         * calculations are base on PSV.
         */
        for (col=1; col<numCOL; col++) {
            for (curComp = 0; curComp < compsInScan; curComp++) {
                ci = dcPtr->MCUmembership[curComp];
                compptr = dcPtr->curCompInfo[ci];
                dctbl = dcPtr->dcHuffTblPtrs[compptr->dcTblNo];

                /*
                 * Section F.2.2.1: decode the difference
                 */
                s = HuffDecode (dctbl);
                if (s) {
                    d = get_bits(s);
                    HuffExtend(d,s);
                } else {
                    d = 0;
                }
                predictor = QuickPredict(col,curComp,curRowBuf,prevRowBuf,
                                         psv);

                curRowBuf[col][curComp]=d+predictor;
            }
        }
        PmPutRow(curRowBuf,compsInScan,numCOL,Pt);
        std::swap(prevRowBuf,curRowBuf);
    }
}




/*
 *--------------------------------------------------------------
 *
 * Get2bytes --
 *
 *	Get a 2-byte unsigned integer (e.g., a marker parameter length
 *	field)
 *
 * Results:
 *	Next two byte of input as an integer.
 *
 * Side effects:
 *	Bitstream is parsed.
 *
 *--------------------------------------------------------------
 */
static inline uint16_t
Get2bytes (IO::Stream * s)
{
    uint16_t a;

    a = s->readByte();
    return (a << 8) | s->readByte();
}

/*
 *--------------------------------------------------------------
 *
 * SkipVariable --
 *
 *	Skip over an unknown or uninteresting variable-length marker
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Bitstream is parsed over marker.
 *
 *
 *--------------------------------------------------------------
 */
static inline void SkipVariable(IO::Stream * s)
{
    int32_t length;

    length = Get2bytes(s) - 2;

    s->seek(length, SEEK_CUR);
}

/*
 *--------------------------------------------------------------
 *
 * GetDht --
 *
 *	Process a DHT marker
 *
 * Results:
 *	None
 *
 * Side effects:
 *	A huffman table is read.
 *	Exits on error.
 *
 *--------------------------------------------------------------
 */
void
LJpegDecompressor::GetDht (DecompressInfo *dcPtr)
    noexcept(false)
{
    int32_t length;
    int32_t i, index, count;

    length = Get2bytes(m_stream) - 2;

    while (length) {
        index = m_stream->readByte();

        if (index < 0 || index >= 4) {
            throw DecodingException(str(boost::format("Bogus DHT index %1%")
                                        % index));
        }

        HuffmanTable *& htblptr = dcPtr->dcHuffTblPtrs[index];
        if (htblptr == NULL) {
            htblptr = (HuffmanTable *) malloc(sizeof (HuffmanTable));
            if (htblptr==NULL) {
                throw DecodingException("Can't malloc HuffmanTable");
            }
        }

        htblptr->bits[0] = 0;
        count = 0;
        for (i = 1; i <= 16; i++) {
            htblptr->bits[i] = m_stream->readByte();
            count += htblptr->bits[i];
        }

        if (count > 256) {
            throw DecodingException("Bogus DHT counts");
        }

        for (i = 0; i < count; i++)
            htblptr->huffval[i] = m_stream->readByte();

        length -= 1 + 16 + count;
    }
}

/*
 *--------------------------------------------------------------
 *
 * GetDri --
 *
 *	Process a DRI marker
 *
 * Results:
 *	None
 *
 * Side effects:
 *	Exits on error.
 *	Bitstream is parsed.
 *
 *--------------------------------------------------------------
 */
void
LJpegDecompressor::GetDri(DecompressInfo *dcPtr)
    noexcept(false)
{
    if (Get2bytes(m_stream) != 4) {
        throw DecodingException("Bogus length in DRI");
    }

    dcPtr->restartInterval = Get2bytes(m_stream);
}

/*
 *--------------------------------------------------------------
 *
 * GetApp0 --
 *
 *	Process an APP0 marker.
 *
 * Results:
 *	None
 *
 * Side effects:
 *	Bitstream is parsed
 *
 *--------------------------------------------------------------
 */
static void	GetApp0(IO::Stream *s)
{
    int32_t length;

    length = Get2bytes(s) - 2;
    s->seek(length, SEEK_CUR);
}

/*
 *--------------------------------------------------------------
 *
 * GetSof --
 *
 *	Process a SOFn marker
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Bitstream is parsed
 *	Exits on error
 *	dcPtr structure is filled in
 *
 *--------------------------------------------------------------
 */
void
LJpegDecompressor::GetSof(DecompressInfo *dcPtr)
    noexcept(false)
{
    int32_t length;
    int16_t ci;
    int32_t c;
    JpegComponentInfo *compptr;

    length = Get2bytes(m_stream);

    dcPtr->dataPrecision = m_stream->readByte();
    dcPtr->imageHeight = Get2bytes(m_stream);
    dcPtr->imageWidth = Get2bytes(m_stream);
    dcPtr->numComponents = m_stream->readByte();

    /*
     * We don't support files in which the image height is initially
     * specified as 0 and is later redefined by DNL.  As long as we
     * have to check that, might as well have a general sanity check.
     */
    if ((dcPtr->imageHeight <= 0 ) ||
        (dcPtr->imageWidth <= 0) || 
        (dcPtr->numComponents <= 0)) {
        throw DecodingException("Empty JPEG image (DNL not supported)");
    }

    if ((dcPtr->dataPrecision<MinPrecisionBits) ||
        (dcPtr->dataPrecision>MaxPrecisionBits)) {
        throw DecodingException("Unsupported JPEG data precision");
    }

    if (length != (dcPtr->numComponents * 3 + 8)) {
        throw DecodingException("Bogus SOF length");
    }

    dcPtr->compInfo = (JpegComponentInfo *) malloc
        (dcPtr->numComponents * sizeof (JpegComponentInfo));

    for (ci = 0; ci < dcPtr->numComponents; ci++) {
        compptr = &dcPtr->compInfo[ci];
        compptr->componentIndex = ci;
        compptr->componentId = m_stream->readByte();
        c = m_stream->readByte();
        compptr->hSampFactor = (int16_t)((c >> 4) & 15);
        compptr->vSampFactor = (int16_t)((c) & 15);
        (void) m_stream->readByte();   /* skip Tq */
    }
}

/*
 *--------------------------------------------------------------
 *
 * GetSos --
 *
 *	Process a SOS marker
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Bitstream is parsed.
 *	Exits on error.
 *
 *--------------------------------------------------------------
 */
void
LJpegDecompressor::GetSos (DecompressInfo *dcPtr)
    noexcept(false)
{
    int32_t length;
    int32_t i;
    uint16_t n, ci, c, cc;
    JpegComponentInfo *compptr;

    length = Get2bytes (m_stream);

    /* 
     * Get the number of image components.
     */
    n = m_stream->readByte();
    dcPtr->compsInScan = n;
    length -= 3;

    if (length != (n * 2 + 3) || n < 1 || n > 4) {
        throw DecodingException("Bogus SOS length");
    }


    for (i = 0; i < n; i++) {
        cc = m_stream->readByte();
        c = m_stream->readByte();
        length -= 2;

        for (ci = 0; ci < dcPtr->numComponents; ci++)
            if (cc == dcPtr->compInfo[ci].componentId) {
                break;
            }

        if (ci >= dcPtr->numComponents) {
            throw DecodingException("Invalid component number in SOS");
        }

        compptr = &dcPtr->compInfo[ci];
        dcPtr->curCompInfo[i] = compptr;
        compptr->dcTblNo = (c >> 4) & 15;
    }

    /*
     * Get the PSV, skip Se, and get the point transform parameter.
     */
    dcPtr->Ss = m_stream->readByte();
    (void)m_stream->readByte();
    c = m_stream->readByte();
    dcPtr->Pt = c & 0x0F;
}

/*
 *--------------------------------------------------------------
 *
 * GetSoi --
 *
 *	Process an SOI marker
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Bitstream is parsed.
 *	Exits on error.
 *
 *--------------------------------------------------------------
 */
static inline void
GetSoi (DecompressInfo *dcPtr)
{

    /*
     * Reset all parameters that are defined to be reset by SOI
     */
    dcPtr->restartInterval = 0;
}

/*
 *--------------------------------------------------------------
 *
 * NextMarker --
 *
 *      Find the next JPEG marker Note that the output might not
 *	be a valid marker code but it will never be 0 or FF
 *
 * Results:
 *	The marker found.
 *
 * Side effects:
 *	Bitstream is parsed.
 *
 *--------------------------------------------------------------
 */
static int32_t
NextMarker(IO::Stream *s)
{
    int32_t c;

    do {
        /*
         * skip any non-FF bytes
         */
        do {
            c = s->readByte();
        } while (c != 0xFF);
        /*
         * skip any duplicate FFs without incrementing nbytes, since
         * extra FFs are legal
         */
        do {
            c = s->readByte();
        } while (c == 0xFF);
    } while (c == 0);		/* repeat if it was a stuffed FF/00 */

    return c;
}

/*
 *--------------------------------------------------------------
 *
 * ProcessTables --
 *
 *	Scan and process JPEG markers that can appear in any order
 *	Return when an SOI, EOI, SOFn, or SOS is found
 *
 * Results:
 *	The marker found.
 *
 * Side effects:
 *	Bitstream is parsed.
 *
 *--------------------------------------------------------------
 */
LJpegDecompressor::JpegMarker
LJpegDecompressor::ProcessTables (DecompressInfo *dcPtr)
{
    int c;

    while (1) {
        c = NextMarker (m_stream);

        switch (c) {
        case M_SOF0:
        case M_SOF1:
        case M_SOF2:
        case M_SOF3:
        case M_SOF5:
        case M_SOF6:
        case M_SOF7:
        case M_JPG:
        case M_SOF9:
        case M_SOF10:
        case M_SOF11:
        case M_SOF13:
        case M_SOF14:
        case M_SOF15:
        case M_SOI:
        case M_EOI:
        case M_SOS:
            return ((JpegMarker)c);

        case M_DHT:
            GetDht (dcPtr);
            break;

        case M_DQT:
            LOGWARN("Not a lossless JPEG file.\n");
            break;

        case M_DRI:
            GetDri (dcPtr);
            break;

        case M_APP0:
            GetApp0(m_stream);
            break;

        case M_RST0:		/* these are all parameterless */
        case M_RST1:
        case M_RST2:
        case M_RST3:
        case M_RST4:
        case M_RST5:
        case M_RST6:
        case M_RST7:
        case M_TEM:
            LOGWARN("Warning: unexpected marker 0x%x", c);
            break;

        default:		/* must be DNL, DHP, EXP, APPn, JPGn, COM,
                         * or RESn */
            SkipVariable (m_stream);
            break;
        }
    }
}

/*
 *--------------------------------------------------------------
 *
 * ReadFileHeader --
 *
 *	Initialize and read the file header (everything through
 *	the SOF marker).
 *
 * Results:
 *	None
 *
 * Side effects:
 *	Exit on error.
 *
 *--------------------------------------------------------------
 */
void
LJpegDecompressor::ReadFileHeader (DecompressInfo *dcPtr)
    noexcept(false)
{
    int c, c2;

    /*
     * Demand an SOI marker at the start of the file --- otherwise it's
     * probably not a JPEG file at all.
     */
    c = m_stream->readByte();
    c2 = m_stream->readByte();
    if ((c != 0xFF) || (c2 != M_SOI)) {
        throw DecodingException(str(boost::format("Not a JPEG file. "
                                                  "marker is %1% %2%\n")
                                    % c % c2));
    }

    GetSoi (dcPtr);		/* OK, process SOI */

    /*
     * Process markers until SOF
     */
    c = ProcessTables (dcPtr);

    switch (c) {
    case M_SOF0:
    case M_SOF1:
    case M_SOF3:
        GetSof(dcPtr);
        break;

    default:
        LOGWARN("Unsupported SOF marker type 0x%x\n", c);
        break;
    }
}

/*
 *--------------------------------------------------------------
 *
 * ReadScanHeader --
 *
 *	Read the start of a scan (everything through the SOS marker).
 *
 * Results:
 *	1 if find SOS, 0 if find EOI
 *
 * Side effects:
 *	Bitstream is parsed, may exit on errors.
 *
 *--------------------------------------------------------------
 */
int32_t
LJpegDecompressor::ReadScanHeader (DecompressInfo *dcPtr)
{
    int c;

    /*
     * Process markers until SOS or EOI
     */
    c = ProcessTables (dcPtr);

    switch (c) {
    case M_SOS:
        GetSos (dcPtr);
        return 1;

    case M_EOI:
        return 0;

    default:
        LOGWARN("Unexpected marker 0x%x\n", c);
        break;
    }
    return 0;
}


RawDataPtr LJpegDecompressor::decompress()
{
    DecompressInfo dcInfo;
    try {
        ReadFileHeader(&dcInfo);
        ReadScanHeader (&dcInfo);

        m_output = RawDataPtr(new RawData);
        m_output->setDataType(OR_DATA_TYPE_RAW);
        uint32_t bpc = dcInfo.dataPrecision;

        m_output->setBpc(bpc);
        m_output->setWhiteLevel((1 << bpc) - 1);
        /*uint16_t *dataPtr = (uint16_t*)*/
        m_output->allocData(dcInfo.imageWidth
                          * sizeof(uint16_t)
                          * dcInfo.imageHeight
                          * dcInfo.numComponents);

        LOGDBG1("dc width = %d dc height = %d\n", dcInfo.imageWidth,
                dcInfo.imageHeight);
        /* consistently the real width is the JPEG width * numComponent
         * On CR2 and DNG.
         */
        uint32_t width = dcInfo.imageWidth * dcInfo.numComponents;
        m_output->setDimensions(width, dcInfo.imageHeight);
        m_output->setSlices(m_slices);
        DecoderStructInit(&dcInfo);
        HuffDecoderInit(&dcInfo);
        DecodeImage(&dcInfo);
        // TODO handle the error properly
    }
    catch(...)
    {
        LOGERR("Decompression error\n");
    }
    return std::move(m_output);
}

}
}

/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0))
  indent-tabs-mode:nil
  fill-column:80
  End:
*/
