//
// Copyright (c) 1997,1998 Colosseum Builders, Inc.
// All rights reserved.
//
// Colosseum Builders, Inc. makes no warranty, expressed or implied
// with regards to this software. It is provided as is.
//
// See the README.TXT file that came with this software for restrictions
// on the use and redistribution of this file or send E-mail to
// info@colosseumbuilders.com
//

//
// JPEG Decoder Class Implementation
//
// Author:  John M. Miano miano@colosseumbuilders.com
//
#include <iostream>
#include <climits>

#include "jpgdecoder.h"
#include "jfif.h"
#include "jpghuffmandecoder.h"
#include "bitmapimage.h"
#include "jpgdecoderquantization.h"
#include "jpgdecodercomponent.h"
#include "jpginputfilestream.h"
#include "jpginputmapstream.h"

using namespace std ;
using namespace ColosseumPrivate ;


namespace Colosseum
{

//
//  Description:
//
//    Class Default Constructor
//
JpegDecoder::JpegDecoder ()
{
  initialize () ;
  return ;
}
//
//  Description:
//
//    Class copy constructor
//
JpegDecoder::JpegDecoder (const JpegDecoder &source)
{
  initialize () ;
  doCopy (source) ;
  return ;
}

//
//  Description:
//
//    Class Destructor
//
JpegDecoder::~JpegDecoder ()
{
  delete [] ac_tables ; ac_tables = 0 ;
  delete [] dc_tables ; dc_tables = 0 ;

  delete [] quantization_tables ; quantization_tables = 0 ;
  delete [] components ; components = 0 ;
  delete [] component_indices ; component_indices = 0 ;
  delete [] scan_components ; scan_components = 0 ;
  return ;
}
//
//  Description:
//
JpegDecoder &JpegDecoder::operator=(const JpegDecoder &source)
{
  doCopy (source) ;
  return *this ;
}

//
//  Description:
//
//    Class initialization procudure for use by constructors.
//
void JpegDecoder::initialize ()
{
  verbose_flag = false ;
  strict_jfif = false ;
  processing_image = false ;

  ac_tables = new JpegHuffmanDecoder [JPEGMAXHUFFMANTABLES] ;
  dc_tables = new JpegHuffmanDecoder [JPEGMAXHUFFMANTABLES] ;

  quantization_tables = new JpegDecoderQuantizationTable [JPEGMAXQUANTIZATIONTABLES] ;
  components = new JpegDecoderComponent [JPEGMAXCOMPONENTSPERFRAME] ;
  component_indices = new unsigned int [JPEGMAXCOMPONENTSPERFRAME] ;

  scan_components = new JpegDecoderComponent * [JPEGMAXCOMPONENTSPERFRAME] ;

  use_filters = false ;
  return ;
}
//
//  Description:
//
//    Copy function. This function is called by the assignment operator
//    and copy constructor to copy the state of one object to another.
//    we only copy user configurable parameters. We do not support
//    copying while a decode operation is in progress.
//
void JpegDecoder::doCopy (const JpegDecoder &source)
{
  verbose_flag = source.verbose_flag ;
  strict_jfif = source.strict_jfif ;
  BitmapImageCoder::operator=(source) ;
  return ;
}

//
//  Description:
//
//    This function reads the Start of Image Marker and the JFIF APP0
//    marker that begin a JPEG file.
//
//    The JFIF standard states "The JPEG FIF APP0 marker is mandatory
//     right after the SOI marker."
//
//    I have come across some JPEG files that have a COM marker between
//    the SOI marker and APP0. This code will reject these non-conforming
//    files.
//

void JpegDecoder::readStreamHeader (JpegInputStream &inputstream)
{
  if (inputstream.getByte () != SOB)
    throw JpegBadStream ("Missing SOI Marker") ;
  if (inputstream.getByte () != SOI)
    throw JpegBadStream ("Missing SOI Marker") ;
  if (inputstream.getByte () != SOB)
    throw JpegBadStream ("Missing JFIF APP0 Marker") ;
  if (inputstream.getByte () != APP0)
    throw JpegBadStream ("Missing JFIF APP0 Marker") ;

  JfifHeader header ;
  inputstream.read (reinterpret_cast<char*>(&header), sizeof (header)) ;
  if (memcmp ("JFIF", reinterpret_cast<char *>(header.identifier), 4) != 0)
    throw JpegBadStream ("Not a JFIF file") ;

  if (verbose_flag)
  {
    cout << "{ Start Of Image }" << endl ;
    cout << "{ JFIF APP0 Marker" << endl ;
    cout << "  Length: " << dec << BigEndianToSystem (header.length) << endl ;
    cout << "  Version: " << dec << (unsigned int) header.version [0]
         << "." << (unsigned int) header.version [0] << endl ;
    // density unit = 0 => Only the aspect ratio is specified.
    // density unit = 1 => Density in pixels per inch.
    // density unit = 2 => Density in pixels per centimeter.
    cout << "  Density Unit: " ;
    switch (header.units)
    {
    case 0:   cout << " (aspect ratio)" << endl ;     break ;
    case 1:   cout << " (pixels per inch)" << endl ;  break ;
    case 2:   cout << " (pixels/cm)" << endl ;        break ;
    default:  cout << " (????)" << endl ;             break ;
    }
    cout << "  X Density: " << dec << BigEndianToSystem (header.xdensity) << endl ;
    cout << "  Y Density: " << dec << BigEndianToSystem (header.xdensity) << endl ;
    cout << "  Thumbnail Width: " << dec << (unsigned int) header.xthumbnail << endl ;
    cout << "  Thumbnail Height: " << dec << (unsigned int) header.xthumbnail << endl ;
    cout << "}" << endl ;
  }

  // Skip over any thumbnail data.
  for (int ii = sizeof (header) ; ii < BigEndianToSystem (header.length) ; ++ ii)
    inputstream.getByte () ;

  return ;
}

//
//  Description:
//
//    This function reads the next marker in the input
//    stream. If the marker is followed by a data block
//    this function dispatches a routine to read the
//    data.
//
void JpegDecoder::readMarker (JpegInputStream &inputstream)
{
  while (inputstream.moreData ())
  {
    UBYTE1 type = inputstream.getByte () ;
    switch (type)
    {
    case SOB:
      // According to E.1.2, 0xFF is allowed as fill when a
      // marker is expected.
      break ;
    case SOI:
      if (verbose_flag)
        cout << "{ Start Of Image }" << endl ;
      return ; // SOI has no data.
    case DQT:
      readQuantization (inputstream) ;
      return ;
    case DHP:
      throw JpegBadStream ("DHP marker not supported") ;

    // The only difference between a Sequential DCT Frame
    // (SOF0) and an extended Sequential DCT Frame (SOF1)
    // is that a baseline frame may only have 2 DC and 2 AC
    // Huffman tables per scan (F.1.2) and and extended
    // Sequential Frame may have up to 4 DC and 4 AC Huffman
    // tables per scan (F.1.3). Both are decoded identically
    // for 8-bit precision. Extended Sequential frames may
    // use 12-bit precision (F, Table B.2) which we do not
    // support.
    case SOF0:
    case SOF1:
    case SOF2:
      readStartOfFrame (inputstream, type) ;
      return ;
    case SOF3:
      throw JpegBadStream ("Lossless Huffman Coding Not Supported") ;
    case SOF5:
      throw JpegBadStream (
        "Differential Sequential Huffman Coding Not Supported") ;
    case SOF6:
      throw JpegBadStream (
        "Differential Progressive Huffman Coding Not Supported") ;
    case SOF7:
      throw JpegBadStream (
        "Differential Lossless Huffman Coding Not Supported") ;

    // These are markers for frames using arithmetic coding.
    // Arithmetic coding is covered by patents so we ignore
    // this type.
    case SOF9:
    case SOFA:
    case SOFB:
    case SOFD:
    case SOFE:
    case SOFF:
      throw JpegBadStream (
        "Cannot read image - Arithmetic Coding covered by patents") ;
    case DHT:
      readHuffmanTable (inputstream) ;
      return ;
    case SOS:
      readStartOfScan (inputstream) ;
      return ;
    case DRI:
      readRestartInterval (inputstream) ;
      return ;
    case EOI:
      eoi_found = true ;
      if (verbose_flag)
        cout << "{ End Of Image }" << endl ;
      return ; // End of Image marker has no data
    case APP0:
    case APP1:
    case APP2:
    case APP3:
    case APP4:
    case APP5:
    case APP6:
    case APP7:
    case APP8:
    case APP9:
    case APPA:
    case APPB:
    case APPC:
    case APPD:
    case APPE:
    case APPF:
    case COM:
      readApplication (type, inputstream) ;
      return ;
    default:
      // We call ReadByte to make sure the problem
      // is not a premature EOF.
      inputstream.getByte () ; 
      throw JpegBadStream (
      "Unknown, unsupported, or reserved marker encountered") ;
    }
  }
  throw JpegBadStream ("Premature end of file") ;
}

//
//  Description:
//
//    This method reads an application or comment marker
//    from the input stream.
//
//  Parameters:
//    type:  The marker type
//
void JpegDecoder::readApplication (UBYTE1 type, JpegInputStream &inputstream)
{
  unsigned int length = inputstream.getBigEndianWord () ;
  char id [512] ;
  int ii = 0 ;

  id [ii] = inputstream.getByte () ;
  for (ii = 1 ; id [ii - 1] != '\000'
                && ii < sizeof (id)
                && ii < length - sizeof (UBYTE2) ; ++ ii)
  {
    id [ii] = inputstream.getByte () ;
  }

  for ( ; ii < length - sizeof (UBYTE2) ; ++ ii)
    (void) inputstream.getByte () ;

  if (verbose_flag)
  {
    if (type == COM)
      cout << "( Comment Marker" << endl ;
    else
    cout << "{ APP" << hex << (UBYTE2) (type & 0x0F) << " Marker" << endl ;
    cout << "Length: " << dec << length << endl ;
    cout << "ID: " << id << endl ;
    cout << "}" << endl ;
  }
  return ;
}

//
//  Description:
//
//    The function reads a Define Huffman Table marker from the input
//    stream.
//
void JpegDecoder::readHuffmanTable (JpegInputStream &inputstream)
{
  // Section B.2.4.2

  if (verbose_flag)
    cout << "{ Define Huffman Table" << endl ;

  UBYTE2 length = inputstream.getBigEndianWord () ;
  unsigned int remaining = length - sizeof (length) ;
  while (remaining > 0)
  {
    UBYTE1 data = inputstream.getByte () ; -- remaining ;

    // Tc in standard 0=>DC, 1=>AC
    unsigned int tableclass = data >> 4 ; 
    unsigned int id = data & 0x0F ; // Th in standard
    if (id > 3)
    {
      throw JpegBadStream (
        "Huffman Table Index outside range [0..3]") ;
    }
    if (verbose_flag)
    {
      cout << "   Table Index " << (int) id << endl ;
      if (tableclass == 0)
        cout << "   Table Class: DC" << endl ;
      else
        cout << "   Table Class: AC" << endl ;
    }
    if (id > 3)
    {
      cout << "Bad index " << id << endl ;
      return ;
    }

    JpegHuffmanDecoder *table ;
    if (tableclass != 0)
      table = &ac_tables [id] ;
    else
      table = &dc_tables [id] ;

    // Read the table data into the table object
    remaining -= table->readTable (inputstream) ;

    if (verbose_flag)
    {
      table->printOn (cout) ;
    }
  }

  if (verbose_flag)
    cout << "}" << endl ;
  return ;
}

//
//  Description:
//
//    This function reads a DQT marker from the input stream.
//
void JpegDecoder::readQuantization (JpegInputStream &inputstream)
{
  // Defined in Section B.2.4.1

  UBYTE2 length = inputstream.getBigEndianWord () ;
  UBYTE1 data ;

  // Maintain a counter for the number of bytes remaining to be read in
  // the quantization table.
  int remaining = length - sizeof (length) ;

  if (verbose_flag)
  {
    cout << "{ Define Quantization Table" << endl ;
    cout << "  Length: " << length << endl ;
  }
  while (remaining > 0)
  {
    data = inputstream.getByte () ; -- remaining ;
    unsigned int precision = data >> 4 ;    // Pq in standard
    unsigned int index = data & 0x0F ;      // Tq in standard

    if (index >= JPEGMAXQUANTIZATIONTABLES)
      throw JpegBadStream ("Quantization Table Index Too Large") ;

    if (verbose_flag)
    {
      cout << "  Table Index: " << dec << index << endl ;
      cout << "  Table Precision: " << dec << precision << endl ;
    }

    switch (precision)
    {
    case 1:
      remaining -= sizeof(UBYTE2) * JPEGSAMPLESIZE ;
      break ;
    case 0:
      remaining -= sizeof (UBYTE1) * JPEGSAMPLESIZE ;
      break ;
    }

    // Read the table data into the table object
    quantization_tables [index].readTable (inputstream, precision) ;

    if (verbose_flag)
    {
      cout << "  Table Values: " ;
      quantization_tables [index].printOn (cout) ;
      cout << endl << "}" << endl ;
    }
  }
  return ;
}

//
//  Description:
//
//    This function reads a define restart interval marker
//    from the input stream.
//
void JpegDecoder::readRestartInterval (JpegInputStream &inputstream)
{
  // Section B.2.4.4

  UBYTE2 length = inputstream.getBigEndianWord () ;
  restart_interval = inputstream.getBigEndianWord () ;
  if (verbose_flag)
  {
    cout << "{ Define Restart Interval" << endl ;
    cout << "  Length:  " << dec << length << endl ; // Should be 4
    cout << "  Interval: " << dec << restart_interval << endl ;
    cout << "}" << endl ;
   }
   return ;
}

//
//  Description:
//
//    The function reads a start of frame marker from the input stream.
//
//  Parameters:
//    type:  The marker type for the frame
//
void JpegDecoder::readStartOfFrame (JpegInputStream &inputstream, UBYTE1 type)
{
  if (type == SOF2)
    progressive_frame = true ;
  else
    progressive_frame = false ;

  // Section B.2.2
  // Read in the image dimensions
  unsigned int length = inputstream.getBigEndianWord () ;
  unsigned int dataprecision = inputstream.getByte () ;  // P in standard
  if (dataprecision != 8)
    throw JpegBadStream ("Only 8-bit data supported") ;

  frame_height = inputstream.getBigEndianWord () ;            // Y in standard
  frame_width = inputstream.getBigEndianWord () ;             // X in standard
  component_count = inputstream.getByte () ;   // Nf in standard

  // JFIF only allows 1 or 3 components.
  if (component_count != 1 && component_count != 3)
    throw JpegBadStream ("JFIF only supports 1 and 3 component streams") ;

  frame_type = type ;

  if (verbose_flag)
  {
    cout << "{ Start Of Frame " << endl ;
    cout << "  Length: " << dec << length << endl ;
    cout << "  Precision: " << dec << dataprecision << endl ;
    cout << "  Height: " << dec << frame_height << endl ;
    cout << "  Width: " << dec << frame_width << endl ;
    cout << "  Component Count: " << component_count << endl ;
  }

  if (length != (component_count * 3 + 8))
    throw JpegBadStream ("Invalid Frame Size") ;

  // Rread the component descriptions
  max_horizontal_frequency = 0 ;
  max_vertical_frequency = 0 ;
  for (unsigned int ii = 0 ; ii < component_count ; ++ ii)
  {
    unsigned int ID = inputstream.getByte () ;  // Ci in standard
    unsigned int qtable ;

    // While JPEG does not put these restrictions on component IDs
    // the JFIF standard does.
    if (strict_jfif)
    {
    if (component_count == 1 && ID != 1)
      throw JpegBadStream ("Component ID not 1") ;
    else if (ID != ii + 1)
      throw JpegBadStream ("Invalid Component ID or ID out of order") ;
    }
    
    component_indices [ii] = ID ;

    UBYTE1 data = inputstream.getByte () ;
    components [ID].horizontalFrequency (data >> 4) ; // Hi in standard
    components [ID].verticalFrequency (data & 0xF) ;  // Vi in standard
    qtable= inputstream.getByte () ;  // Tqi in standard
    if (qtable >= JPEGMAXQUANTIZATIONTABLES)
      throw JpegBadStream ("Bad Quantization Table Index") ;
    components [ID].setQuantizationTable (quantization_tables [qtable]) ;

    // Keep track of the largest values for horizontal and vertical
    // frequency.
    if (components [ID].horizontalFrequency ()
        > max_horizontal_frequency)
    {
      max_horizontal_frequency =
        components [ID].horizontalFrequency () ;
    }

    if (components [ID].verticalFrequency ()
          > max_vertical_frequency)
    {
      max_vertical_frequency =
        components [ID].verticalFrequency () ;
    }

    if (verbose_flag)
    {
      cout << "   Component " << ID << endl ;
      cout << "   Horizontal Frequency: "
           << components [ID].horizontalFrequency () << endl ;
      cout << "   Vertical Frequency: "
           << components [ID].verticalFrequency () << endl ;
      cout << "   Quantization Table: "
           << qtable << endl ;
    }
  }

  calculateMcuDimensions () ;

  // Allocate storage for the image.
  current_image->setSize (frame_width, frame_height) ;

  if (verbose_flag)
    cout << "}" << endl ;

  sof_found = true ;
  return ;
}

//
//  Description:
//
//    This function reads a start of scan marker and the scan data
//    following the marker.
//
void JpegDecoder::readStartOfScan (JpegInputStream &inputstream)
{
  unsigned int ii ;

  if (! sof_found)
    throw JpegBadStream ("Scan found before frame defined") ;

  // Section B.2.3

  UBYTE2 length = inputstream.getBigEndianWord () ;
  if (verbose_flag)
  {
    cout << "{ Start Of Scan " << endl ;
    cout << "  Length:  " << dec << length << endl ;
  }

  scan_component_count = inputstream.getByte () ;  // Ns in standard
  // Right now we can only handle up to three components.
  if (scan_component_count > 3 || scan_component_count < 1)
    throw JpegBadStream ("Invalid component count in scan") ;

  for (ii = 0 ; ii < scan_component_count ; ++ ii)
  {
    UBYTE1 componentID = inputstream.getByte () ;  // Csi in standard

    scan_components [ii] = &components [componentID] ;
    // If the horizontal frequency is zero then the component was not
    // defined in the SOFx marker.
    if (scan_components [ii]->horizontalFrequency () == 0)
      throw JpegBadStream ("Component Not Defined") ;

    UBYTE1 rb = inputstream.getByte () ;
    unsigned int actable = rb & 0x0F ;
    unsigned int dctable = rb >> 4 ;

    scan_components [ii]->setHuffmanTables (
                              dc_tables [dctable],
                              ac_tables [actable]) ;
    if (verbose_flag)
    {
      cout << "  Component ID: "
           << dec << (unsigned int) componentID << endl ;
      cout << "  DC Entropy Table: "
           << dec << dctable << endl  ;
      cout << "  AC Entropy Table: "
           << dec << actable << endl  ;
    }
  }

  unsigned int spectralselectionstart = inputstream.getByte () ; // Ss in standard
  unsigned int spectralselectionend = inputstream.getByte ()  ;  // Se in standard

  UBYTE1 ssa = inputstream.getByte () ;
  unsigned int successiveapproximationhigh = ssa >> 4 ;  // Ah in standard
  unsigned int successiveapproximationlow = ssa & 0x0F ; // Al in standard

  if (verbose_flag)
  {
    cout << " Spectral Selection Start: "
         << dec << (unsigned int) spectralselectionstart << endl ;
    cout << " Spectral Selection End: "
         << dec << (unsigned int) spectralselectionend << endl ;
    cout << " Successive Approximation High: "
         << dec << successiveapproximationhigh << endl ;
    cout << " Successive Approximation Low: "
         << dec << successiveapproximationlow << endl  ;
    cout << "}" << endl ;
  }

  for (ii = 0 ; ii < scan_component_count ; ++ ii)
  {
    if (progressive_frame)
    {
      scan_components [ii]->checkQuantizationTable () ;
      if (spectralselectionstart == 0)
        scan_components [ii]->checkDcTable () ;
      else
        scan_components [ii]->checkAcTable () ;
    }
    else
    {
      scan_components [ii]->checkQuantizationTable () ;
      scan_components [ii]->checkDcTable () ;
      scan_components [ii]->checkAcTable () ;
    }
    scan_components [ii]->allocateComponentBuffers (*this) ;

  }

  ++ current_scan ;
  inputstream.enterBitMode (CHAR_BIT) ;
  if (progressive_frame)
  {
    readProgressiveScanData (inputstream,
                          spectralselectionstart,
                          spectralselectionend,
                          successiveapproximationhigh,
                          successiveapproximationlow) ;
  }
  else
  {
    readSequentialScanData (inputstream) ;
  }
  inputstream.exitBitMode () ;

  callProgressFunction (100) ;
  return ;
}

//
//  Description:
//
//    This function determines for non-interlaced scans:
//
//     o The dimensions in pixels for an MCU
//     o The number of MCU rows and columns needed to encode the scan.
//
void JpegDecoder::calculateMcuDimensions ()
{
  mcu_height = max_vertical_frequency * JPEGSAMPLEWIDTH ;
  mcu_width = max_horizontal_frequency * JPEGSAMPLEWIDTH ;
  mcu_rows = (frame_height + mcu_height - 1) / mcu_height ;
  mcu_cols = (frame_width + mcu_width - 1) / mcu_width ;
  return ;
}

//
//  Dimensions:
//
//    This function calls the progress function if it has
//    been supplied by the user.
//
//  Parameters:
//    progress: The progress percentage.
//
void JpegDecoder::callProgressFunction (unsigned int progress)
{
  if (progress_function == 0)
    return ;

  bool abort = false ;
  unsigned int percent = progress ;
  if (percent > 100)
    percent = 100 ;
  if (progress_function != 0)
  {
    progress_function (*this,
                       progress_data,
                       current_scan,
                       scan_count,
                       percent,
                       abort) ;
  }

  if (abort)
    throw GraphicsAbort () ;
  return ;
}

//
//  Description:
//
//    This function reads the scan data for progressive scans.
//
//    All we do here is determine if we are processing a DC
//    scan (sss==sse==0) or AC scan and if we are processing
//    the first scan for the spectral selection (sah==0) or
//    subsequent scan.
//
//  Parameters:
//    sss: Spectral Selection Start (0..63)
//    sse: Spectral Selection End (sse..63)
//    sah: Successive Approximation High
//    sal: Successive Approximation Low
//
void JpegDecoder::readProgressiveScanData (JpegInputStream &inputstream, 
                                           unsigned int sss, unsigned int sse,
                                           unsigned int sah, unsigned int sal)
{
  if (sss == 0)
  {
    if (sse != 0)
      throw JpegBadStream ("Progressive scan contains DC and AC data") ;

    if (sah == 0)
    {
      readDcFirst (inputstream, sal) ;
    }
    else
    {
      readDcRefine (inputstream, sal) ;
    }
  }
  else
  {
    if (sah == 0)
    {
      readAcFirst (inputstream, sss, sse, sal) ;
    }
    else
    {
      readAcRefine (inputstream, sss, sse, sal) ;
    }
  }
  return ;
}

//
//  Description:
//
//    This funtion reads the scan data for the first DC scan for
//    one or more components.
//
//  Parameters:
//
//    ssa:  Successive Approximation
//
void JpegDecoder::readDcFirst (JpegInputStream &inputstream, unsigned int ssa)
{
  resetDcDifferences () ;
  unsigned int restartcount = 0 ;

  if (scanIsInterleaved ())
  {
    for (unsigned int mcurow = 0 ;
         mcurow < mcu_rows ;
         ++ mcurow)
    {
      callProgressFunction (mcurow * 100 / mcu_rows) ;
      for (unsigned int mcucol = 0 ;
           mcucol < mcu_cols ;
           ++ mcucol, ++restartcount)
      {
        if (restart_interval != 0
            && restart_interval == restartcount)
        {
          resetDcDifferences () ;
          processRestartMarker (inputstream) ;
          restartcount = 0 ;
        }
        for (unsigned int cc = 0 ; cc < scan_component_count ; ++ cc)
        {
          for (unsigned int cy = 0 ;
               cy < scan_components [cc]->verticalFrequency () ;
               ++ cy)
          {
            unsigned int durow = scan_components [cc]->verticalFrequency ()
                                 * mcurow + cy ;
            for (unsigned int cx = 0 ;
                 cx < scan_components [cc]->horizontalFrequency () ;
                 ++ cx)
            {
              unsigned int ducol = scan_components [cc]->horizontalFrequency ()
                                   * mcucol + cx ;

              scan_components [cc]->decodeDcFirst (inputstream, durow, ducol, ssa) ;
            }
          }
        }
      }
    }
  }
  else
  {
    for (unsigned int row = 0 ;
         row < scan_components [0]->noninterleavedRows () ;
         ++ row)
    {
      callProgressFunction (row * 100 / scan_components [0]->noninterleavedRows ()) ;
      for (unsigned int col = 0 ;
           col < scan_components [0]->noninterleavedCols () ;
           ++ col, ++restartcount)
      {
        if (restart_interval != 0 && restart_interval == restartcount)
        {
          resetDcDifferences () ;
          processRestartMarker (inputstream) ;
          restartcount = 0 ;
        }
        scan_components [0]->decodeDcFirst (inputstream, row, col, ssa) ;
      }
    }
  }
  return ;
}

//
//  Description:
//
//    This function reads the scan data for a refining DC scan.
//
//  Parameters:
//    ssa:  The successive approximation value for this scan.
//
void JpegDecoder::readDcRefine (JpegInputStream &inputstream, unsigned int ssa)
{
  resetDcDifferences () ;
  unsigned int restartcount = 0 ;

  if (scanIsInterleaved ())
  {
    for (unsigned int mcurow = 0 ; mcurow < mcu_rows ; ++ mcurow)
    {
      callProgressFunction (mcurow * 100 / mcu_rows) ;
      for (unsigned int mcucol = 0 ;
           mcucol < mcu_cols ;
           ++ mcucol, ++restartcount)
      {
        if (restart_interval != 0 && restart_interval == restartcount)
        {
          resetDcDifferences () ;
          processRestartMarker (inputstream) ;
          restartcount = 0 ;
        }
        for (unsigned int cc = 0 ; cc < scan_component_count ; ++ cc)
        {
          for (unsigned int cy = 0 ;
               cy < scan_components [cc]->verticalFrequency () ;
               ++ cy)
          {
            unsigned int durow = scan_components [cc]->verticalFrequency ()
                                 * mcurow + cy ;
            for (unsigned int cx = 0 ;
                 cx < scan_components [cc]->horizontalFrequency () ;
                 ++ cx)
            {
              unsigned int ducol = scan_components [cc]->horizontalFrequency ()
                                   * mcucol + cx ;

              scan_components [cc]->decodeDcRefine (inputstream, durow, ducol, ssa) ;
            }
          }
        }
      }
    }
  }
  else
  {
    for (unsigned int row = 0 ;
         row < scan_components [0]->noninterleavedRows () ;
         ++ row)
    {
      callProgressFunction (row * 100 / scan_components [0]->noninterleavedRows ()) ;
      for (unsigned int col = 0 ;
           col < scan_components [0]->noninterleavedCols () ;
           ++ col, ++restartcount)
      {
        if (restart_interval != 0 && restart_interval == restartcount)
        {
          resetDcDifferences () ;
          processRestartMarker (inputstream) ;
          restartcount = 0 ;
        }
        scan_components [0]->decodeDcRefine (inputstream, row, col, ssa) ;
      }
    }
  }
  return ;
}

//
//  Description:
//
//    This function reads the scan data for the first AC scan for a
//    component. Progressive scans that read AC data cannot be
//    interleaved.
//
//  Parameters:
//    sss:  Spectral Selection Start
//    sse:  Spectral Selection End
//    ssa:  Spectral Selection
//

void JpegDecoder::readAcFirst (JpegInputStream &inputstream,
                               unsigned int sss,
                               unsigned int sse,
                               unsigned int ssa)
{
  resetDcDifferences () ;

  unsigned int restartcount = 0 ;
  for (unsigned int row = 0 ;
       row < scan_components [0]->noninterleavedRows () ;
       ++ row)
  {
    callProgressFunction (row * 100 / scan_components [0]->noninterleavedRows ()) ;
    for (unsigned int col = 0 ;
         col < scan_components [0]->noninterleavedCols () ;
         ++ col, ++restartcount)
    {
      if (restart_interval != 0 && restart_interval == restartcount)
      {
        resetDcDifferences () ;
        processRestartMarker (inputstream) ;
        restartcount = 0 ;
      }
      scan_components [0]->decodeAcFirst (inputstream,
                                          row, col,
                                          sss, sse,
                                          ssa) ;
    }
  }
  return ;
}

//
//  Description:
//
//    This function reads the scan data for a refining AC scan for a
//    component. Progressive scans that read AC data cannot be
//    interleaved.
//
//  Parameters:
//    sss:  Spectral Selection Start
//    sse:  Spectral Selection End
//    ssa:  Spectral Selection
//

void JpegDecoder::readAcRefine (JpegInputStream &inputstream,
                                unsigned int sss,
                                unsigned int sse,
                                unsigned int ssa)
{
  resetDcDifferences () ;

  unsigned int restartcount = 0 ;
  for (unsigned int row = 0 ;
       row < scan_components [0]->noninterleavedRows () ;
       ++ row)
  {
    callProgressFunction (row * 100 / scan_components [0]->noninterleavedRows ()) ;
    for (unsigned int col = 0 ;
         col < scan_components [0]->noninterleavedCols () ;
         ++ col, ++restartcount)
    {
      if (restart_interval != 0 && restart_interval == restartcount)
      {
        resetDcDifferences () ;
        processRestartMarker (inputstream) ;
        restartcount = 0 ;
      }
      scan_components [0]->decodeAcRefine (inputstream,
                                           row, col,
                                           sss, sse,
                                           ssa) ;
    }
  }
  return ;
}

//
//  Parameters:
//
//    The function reads the scan data for a sequential scan. All
//    we do here is determine whether or not we have an interleaved
//    or non-interleaved scan then call a function that handles
//    the scan type.

void JpegDecoder::readSequentialScanData (JpegInputStream &inputstream)
{
  expected_restart = 0 ;
  if (scanIsInterleaved ())
  {
    readSequentialInterleavedScan (inputstream) ;
  }
  else
  {
    readSequentialNonInterleavedScan (inputstream) ;
  }
  return ;
}

//
//  Description:
//
//    This function reads the scan data for an interleaved scan.
//

void JpegDecoder::readSequentialInterleavedScan (JpegInputStream &inputstream)
{
  resetDcDifferences () ;

  unsigned int restartcount = 0 ;
  for (unsigned int mcurow = 0 ; mcurow < mcu_rows ; ++ mcurow)
  {
    callProgressFunction (mcurow * 100 / mcu_rows) ;
    for (unsigned int mcucol = 0 ; mcucol < mcu_cols ;
         ++ mcucol, ++restartcount)
    {
      if (restart_interval != 0 && restart_interval == restartcount)
      {
        processRestartMarker (inputstream) ;
        restartcount = 0 ;
      }
      for (unsigned int cc = 0 ; cc < scan_component_count ; ++ cc)
      {
        for (unsigned int cy = 0 ;
             cy < scan_components [cc]->verticalFrequency () ;
             ++ cy)
        {
          unsigned int durow = scan_components [cc]->verticalFrequency ()
                               * mcurow + cy ;
          for (unsigned int cx = 0 ;
               cx < scan_components [cc]->horizontalFrequency () ;
               ++ cx)
          {
            unsigned int ducol =
                   scan_components [cc]->horizontalFrequency ()
                   * mcucol + cx ;

            scan_components [cc]->decodeSequential (
                                      inputstream,
                                      durow,
                                      ducol) ;
          }
        }
      }
    }
  }
  return ;
}

//
//  Description:
//
//    This function reads the scan data for a non-interleaved scan.
//

void JpegDecoder::readSequentialNonInterleavedScan (JpegInputStream &inputstream)
{
  resetDcDifferences () ;

  unsigned int restartcount = 0 ;
  for (unsigned int row = 0 ;
       row < scan_components [0]->noninterleavedRows () ;
       ++ row)
  {
    callProgressFunction (row * 100 / scan_components [0]->noninterleavedRows ()) ;
    for (unsigned int col = 0 ;
         col < scan_components [0]->noninterleavedCols () ;
         ++ col, ++ restartcount)
    {
      if (restart_interval != 0
          && restart_interval == restartcount)
      {
        processRestartMarker (inputstream) ;
        restartcount = 0 ;
      }
      scan_components [0]->decodeSequential (inputstream, row, col) ;
    }
  }
  return ;
}

//
//  Description:
//
//    This function resets the DC difference values for all components
//    for the current scan.
//
//    This function gets called before each scan is processed and
//    whenever a restart marker is read.
//

void JpegDecoder::resetDcDifferences ()
{
  for (unsigned int ii = 0 ; ii < scan_component_count ; ++ ii)
    scan_components [ii]->resetDcDifference () ;
  return ;
}

//
//  Description:
//
//    This function reads a restart marker from the input stream.
//    It gets called byte functions that read scan data whenever
//    a restart marker is expected. An exception is raise if the
//    correct restart marker is not next in the input stream.
//
void JpegDecoder::processRestartMarker (JpegInputStream &inputstream)
{
  inputstream.exitBitMode () ;
  UBYTE1 data = inputstream.getByte () ;
  if (data != 0xFF)
    throw JpegBadStream ("Missing Restart Marker") ;
  // According to E.1.2 0xFF can be used as a fill character
  // before the marker.
  while (data == 0xFF)
    data = inputstream.getByte () ;
  if (data < RST0 || data > RST7)
    throw JpegBadStream ("Missing Restart Marker") ;

  // Restart markers RST0..RST7 should come in sequence.
  if ((0x0F & data) != expected_restart)
    throw JpegBadStream ("Incorrect Restart Marker") ;

  // Move the counter to the next restart marker
  ++ expected_restart ;
  expected_restart %= 8 ;

  // Reset the DC coefficent differences to zero.
  resetDcDifferences () ;
  inputstream.enterBitMode (CHAR_BIT) ;
  return ;
}

//
//  Description:
//
//    This function reads an image from a JPEG stream. The
//    stream needs to have been opened in binary mode.
//
//  Parameters:
//    istrm: Input stream
//    image: The output image
//

void JpegDecoder::readImage (JpegInputStream &inputstream,
                             BitmapImage &image)
{
  unsigned char data ;

  current_scan = 0 ;
  scan_count = 0 ;
  current_image = &image ;

  if (progress_function != 0)
    getScanCount (inputstream) ;

  restart_interval = 0 ;  // Clear the restart interval ;
  try
  {
    processing_image = true ;
    current_image->clearImage () ;
    eoi_found = false ;
    sof_found = false ;

    // Read the required SOI and APP0 markers at the start of the image.
    readStreamHeader (inputstream) ;

    data = inputstream.getByte () ;
    while (inputstream.moreData () && ! eoi_found)
    {
      if (data == SOB)
      {
        readMarker (inputstream) ;
        if (eoi_found)
          break ;
      }
      data = inputstream.getByte () ;
      if (! inputstream.moreData ())
        throw JpegBadStream ("Premature end of file") ;
    }
  }
  catch (GraphicsAbort)
  {
    freeAllocatedResources () ;
    current_image = 0 ;
  }
  catch (...)
  {
    updateImage () ;
    freeAllocatedResources () ;
    current_image = 0 ;
    processing_image = false ;
    throw ;
  }
  updateImage () ;
  processing_image = false ;

  // Some people say we should not have this check here. If it bothers you
  // remove it.
  if (! eoi_found)
  {
    throw JpegBadStream("End of Image Marker Not Found") ;
  }
  // We do no want an exception so do not call ReadByte ()
  // Sometimes there can be trailing end of record markers.
  inputstream.read ((char *) &data, sizeof (data)) ;
  while ((data == '\r' || data == '\n') && inputstream.moreData ())
    inputstream.read ((char *) &data, sizeof (data)) ;
  if (inputstream.moreData ())
  {
    throw JpegBadStream ("Extra Data After End of Image Marker") ;
  }

  freeAllocatedResources () ;
  current_image = 0 ;
  return ;
}

//
//  Description:
//
//    This function scans a stream and counts the number of scans.  This
//    allows an exact count for a progress function.
//
void JpegDecoder::getScanCount (JpegInputStream &inputstream)
{
  // Save the stream position so we can go back
  // when we are finished.
  InputByteStream::POSITIONTYPE startpos = inputstream.tellg () ;

  // Count the number of SOS markers.
  scan_count = 0 ;
  while (inputstream.moreData ())
  {
    UBYTE1 data = inputstream.getByte () ;
    if (data == SOB)
    {
      while (data == SOB)
      {
        data = inputstream.getByte () ;
      }
      if (data == SOS)
        ++ scan_count ;
      else if (data == EOI)
        break ;
    }
  }
  // Go back to where we were in the stream.
  inputstream.seekg (startpos) ;
  return ;
}

//
//  Description:
//
//    This function writes the image data that has been read so
//    far to the image. This function gets called after reading
//    the entire image stream.  The user can also call this function
//    from a progress function to display progressive images,
//    multi-scan sequential images, or just to display the image
//    as it is being read (slow).
//

void JpegDecoder::updateImage ()
{
  if (current_image == 0)
    throw JpegError ("Not reading an image") ;

  if (current_scan > 0)
  {
    if (progressive_frame)
    {
      for (unsigned int ii = 0 ; ii < component_count ; ++ ii)
      {
        components [component_indices [ii]].progressiveInverseDct () ;
        components [component_indices [ii]].upsampleImage (use_filters) ;
      }
    }
    else
    {
      for (int ii = 0 ; ii < component_count ; ++ ii)
      {
        components [component_indices [ii]].upsampleImage (use_filters) ;
      }
    }

    switch (component_count)
    {
    case 3:
      JpegDecoderComponent::convertRgb (components [component_indices [0]],
                                        components [component_indices [1]],
                                        components [component_indices [2]],
                                        *current_image) ;
      break ;
    case 1:
      JpegDecoderComponent::convertGrayscale (
                               components [component_indices [0]],
                               *current_image) ;
      break ;
    }
  }
  return ;
}

//
//  Description:
//
//    This function frees all the memory dynamically allocated
//    during the image decoding process.  
//
void JpegDecoder::freeAllocatedResources ()
{
  if (current_scan > 0)
  {
    for (unsigned int ii = 0 ; ii < component_count ; ++ ii)
    {
      components [component_indices [ii]].freeComponentBuffers () ;
    }
  }
  return ;
}

//
//  Description:
//
//    This function is only used in progressive images. It refines
//    a non-zero AC coefficient. It is called at various times while
//    processing a progressive scan that refines AC coefficients.
//
//  Parameters:
//    value: (in/out) The value to refine
//    ssa: The succesive approximation for the scan.
//    decoder: The JPEG decoder
//

void JpegDecoder::refineAcCoefficient (JpegInputStream &inputstream,
                                       BYTE2 &value,
                                       unsigned int ssa)
{
  // Section G.1.2.3
  if (value > 0)
  {
    if (inputstream.nextBit () != 0)
    {
      value += (1 << ssa) ;
    }
  }
  else if (value < 0)
  {
    if (inputstream.nextBit () != 0)
    {
      value += (-1 << ssa) ;
    }
  }
  return ;
}

void JpegDecoder::readImageFile (const std::string &filename, BitmapImage &image)
{
#if ! defined (USEMAP)
  JpegInputFileStream inputstream ;
#else
  JpegInputMapStream inputstream ;
#endif
  const char *name = filename.c_str () ;
  inputstream.open (name) ;
  if (! inputstream)
    throw JpegError ("Can't open input file") ;
  try
  {
    readImage (inputstream, image) ;
  }
  catch (InputByteStream::StreamError &error)
  {
    // Convert input stream errors to JPEG errors.
    throw JpegError (error.what ()) ;
  }
  return ;
}

bool JpegDecoder::isProgressive () const
{
  return progressive_frame ;
}

unsigned int JpegDecoder::mcuRows () const
{
  return mcu_rows ;
}

unsigned int JpegDecoder::mcuCols () const
{
  return mcu_cols ;
}

unsigned int JpegDecoder::restartInterval () const
{
  return restart_interval ;
}

unsigned int JpegDecoder::frameHeight () const
{
  return frame_height ;
}

unsigned int JpegDecoder::frameWidth () const
{
  return frame_width ;
}

unsigned int JpegDecoder::maxVFrequency () const
{
  return max_vertical_frequency ;
}

unsigned int JpegDecoder::maxHFrequency () const
{
  return max_horizontal_frequency ;
}

bool JpegDecoder::scanIsInterleaved () const
{
  if (scan_component_count != 1)
    return true ;
  else
    return false ;
}

bool JpegDecoder::getVerbose () const
{
  return verbose_flag ;
}

void JpegDecoder::setVerbose (bool value)
{
  verbose_flag = value ;
  return ;
}

bool JpegDecoder::getUseFilters () const
{
  return use_filters ;
}

void JpegDecoder::setUseFilters (bool value)
{
  use_filters = value ;
  return ;
}


} // End Namespace Colosseum