//
// 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 Encoder Library.
//
// Title:   JpegEncoderComponent Class Implementation
//
// Author: John M. Miano  miano@colosseumbuilders.com
//
//
//  Changes:
//
//    5-January-2000
//
//      Fixed sampling errors in sampleNtoNComponent ().
//

#include "jpgencoder.h"
#include "jpgencodercomponent.h"
#include "jfif.h"
#include "jpgcoefficient.h"
#include "jpgencoderdataunit.h"
#include "jpghuffmanencoder.h"
#include "jpgoutputstream.h"
#include "checks.h"

using namespace Colosseum ;

namespace
{
using namespace ColosseumPrivate ;

//
//  Description:
//
//    This function returns the number of pixel rows encoded for a component
//    in an interleaved scan. This number is at least as large as the image
//    height. However, since it takes into account sampling frequencies,
//    it can be larger if the height is not evenly divisible by the number
//    sample rows in an MCU.
//
//  Parameters:
//
//    imageheight : The image width
//    maxvf: The maximum horizontal sampling frequency among all components.
//
//  Return Value:
//
//    The number of pixel rows to be encoded.
//
unsigned int NoninterleavedRows (unsigned int imageheight, unsigned int maxvf)
{
  unsigned int heightmask = JPEGSAMPLEWIDTH * maxvf - 1 ;
  unsigned int rows = (imageheight + heightmask) & ~heightmask ;
  return rows ;
}
//
//  Description:
//
//    This function returns the number of pixel columns encoded for a component
//    in an interleaved scan. This number is at least as large as the image
//    width. However, since it takes into account sampling frequencies,
//    it can be larger if the width is not evenly divisible by the number
//    sample columns in an MCU.
//
//  Parameters:
//
//    imagewidth : The image height
//    maxhf: The maximum vertical sampling frequency among all components.
//
//  Return Value:
//
//    The number of pixel columns to be encoded.
//
unsigned int NoninterleavedColumns (unsigned int imagewidth, unsigned int maxhf)
{
  unsigned int widthmask = JPEGSAMPLEWIDTH * maxhf - 1 ;
  unsigned int columns = (imagewidth + widthmask) & ~widthmask ;
  return columns ;
}

}

namespace ColosseumPrivate
{

//
//  Description:
//
//    Class Default Constructor
//

JpegEncoderComponent::JpegEncoderComponent ()
: output_stream (0),
  dct_coefficients (0),
  du_rows (0),
  du_cols (0),
  eob_run (0),
  eob_start_du_row (0),
  eob_start_du_col (0),
  eob_start_position (0),
  v_frequency (1),
  h_frequency (1),
  ac_table (0),
  dc_table (0),
  component_buffer (0)
{
  return ;
}

//
//  Description:
//
//    Class Destructor
//
JpegEncoderComponent::~JpegEncoderComponent ()
{
  freeDynamicStorage () ;
  return ;
}

//
//  Description:
//
//    This function gets rid of all the memory allocated by the component.
//    This allows the encoder to free memory after each image is written
//    rather than waiting until the decoder is deleted.
//
void JpegEncoderComponent::freeDynamicStorage ()
{
  delete [] dct_coefficients ; dct_coefficients = 0 ;
  return ;
}

//
//  Description:
//
//    This function is use to gather Huffman statistics for DC coefficients.
//    The calling sequences is idential to PrintDcData (). Only the huffvalue
//    parameter is used here.
//
//  Parameters:
//    huffvalue:  The Huffman value (not code) to process.
//
void JpegEncoderComponent::gatherDcData (int huffvalue, int)
{
  dc_table->incrementFrequency (huffvalue) ;
  return ;
}

//
//  Description:
//
//    This function is use to gather Huffman statistics for AC coefficients.
//    The calling sequences is idential to PrintAcData (). Only the huffvalue
//    parameter is used here.
//
//    If huffval==-1 then an unencoded bit string would be output by
//    PrintAcData (). In that situation this function does nothing.
//
//  Parameters:
//    huffvalue:  The Huffman value (not code) to process
//
void JpegEncoderComponent::gatherAcData (int huffvalue, int, int)
{
  if (huffvalue >= 0)
  {
    ac_table->incrementFrequency (huffvalue) ;
  }
  return ;
}

//
//  Description:
//
//    This function is use to output Huffman-encoded data for a DC value.
//
//  Parameters:
//    huffvalue:  The Huffman value
//    bits: The additional data bits
//
//    8-bit DC values are in the range 0..11. The value specifies the number
//    of additional bits of refining data (bits).
//

void JpegEncoderComponent::printDcData (int huffvalue, int bits)
{
  UBYTE2 huffcode ;
  UBYTE1 huffsize ;

  // Section F.1.2.1
  dc_table->encode (huffvalue, huffcode, huffsize) ;
  output_stream->outputBits (huffcode, huffsize) ;
  if (huffvalue != 0)
    output_stream->outputBits (bits, huffvalue) ;
  return ;
}

//
//  Description:
//
//    This function is use to output Huffman-encoded data for an AC value.
//
//  Parameters:
//    huffvalue:  The Huffman value
//    value: The additional data bits
//    size: The number of additional data bits.
//
//    When huffvalue==-1 this function is also used to output unencoded bit
//    strings.
//
void JpegEncoderComponent::printAcData (int huffvalue, int value, int size)
{
  UBYTE2 huffcode ;
  UBYTE1 huffsize ;

  // Section F.1.2.2.1
  if (huffvalue >= 0)
  {
    ac_table->encode (huffvalue, huffcode, huffsize) ;
    output_stream->outputBits (huffcode, huffsize) ;
  }
  if (size != 0)
    output_stream->outputBits (value, size) ;
  return ;
}
//
//  Description:
//
//    This function is used for two purposes in a sequential scan:
//
//      o To gather statistics for generating Huffman Tables
//      o To encode and output a data unit.
//
//    The dcfunction and acfunction arguments are determine which of these
//    actions are performed. If these arguments are PrintDcData () and
//    PrintAcData () the data unit is encoded and written to the output
//    stream.  If the arguments are GatherDcData () and GatherAcData ()
//    usage statistics are gathered.
//
//    While creating a separate function for each purpose may have been clearer,
//    it would create maintenance problems because they would have to be kept
//    in strict synchronization with each other.
//
//  Parameters:
//    row,col: Data unit position
//    dcfunction: Function for outputting DC coefficient values.
//    acfunction: Function for outputting AC coefficient values.
//
//    This function is of the type COMPONENTPASSFUNCTION.
//
void JpegEncoderComponent::encodeSequential (
                            unsigned int row, unsigned int col,
                            DCOUTPUTFUNCTION dcfunction,
                            ACOUTPUTFUNCTION acfunction,
                            unsigned int, unsigned int, unsigned int)
{
  JpegCoefficientBlock &du = dct_coefficients [row * du_cols + col] ;

  // DC value calculation
  // Section F.1.2.1.3
  int diff = du [0] - last_dc_value ;
  last_dc_value = du [0] ;

  // Break the DC coefficient into a category (Table F.12) and
  // additional bits.
  int bits ;
  if (diff >= 0)
  {
    bits = diff ;
  }
  else
  {
    diff = -diff ;
    bits = ~diff ;
  }
  int ssss = 0 ;  // Category
  while (diff != 0)
  {
    ++ ssss ;
    diff >>= 1 ;
  }
  (this->*dcfunction) (ssss, bits) ;

  // AC coefficient coding
  // F.1.2.2.3 Figure F.2
  int zerorun = 0 ; // Called rrrr in the specification.
  for (unsigned int index = 1 ; index < JPEGSAMPLESIZE ; ++ index)
  {
    if (du [JpegZigZagInputOrder (index)] != 0)
    {
      // 16 is the longest run of zeros that can be encoded except for the
      // final EOB code.
      while (zerorun >= 16)
      {
        // 0xF0 is the code to skip 16 zeros (Figure F.1)
        (this->*acfunction) (0xF0, 0, 0) ;
        zerorun -= 16 ;
      }

      // Non-zero AC coefficients are encoded with
      // 8-bit Huffman-encoded values of the form rrrrssss followed by
      // 1..10 additional bits of data. rrrr is the number of zero values
      // to skip (0..15). ssss is the category (1..10) which specifies the
      // number of additional raw bits to follow. (Figure F.1)
      int value = du [JpegZigZagInputOrder (index)] ;
      int bits ;
      if (value >= 0)
      {
        bits = value ;
      }
      else
      {
        value = -value ;
        bits = ~value ;
      }
      int ssss = 0 ;
      while (value != 0)
      {
        value >>= 1 ;
        ++ ssss ;
      }

      int rrrrssss = (zerorun << 4) | ssss ;
      (this->*acfunction) (rrrrssss, bits, ssss) ;
      zerorun = 0 ;
    }
    else
    {
      ++ zerorun ;
    }
  }
  // The code 0x00 indicates all remaining AC coefficients are zero.
  if (zerorun > 0)
  {
    (this->*acfunction) (0, 0, 0) ;
  }

  return ;
}

//
//  Description:
//
//    This function outputs an End of Band run in an initial AC coefficient
//    scan of a progressive frame.
//
//  Parameters:
//    acfunction: Function for outputting AC coefficient values
//
void JpegEncoderComponent::printEobRun (ACOUTPUTFUNCTION acfunction)
{
  // Figure G.4
  if (eob_run != 0)
  {
    unsigned int bits = eob_run ;
    unsigned int value = bits >> 1 ;
    unsigned int ssss = 0 ; // Category (Table G.1)
    while (value != 0)
    {
      value >>= 1 ;
      ++ ssss ;
    }
    (this->*acfunction)(ssss << 4, bits, ssss) ;
    eob_run = 0 ;
  }

  return ;
}                               

//
//  Description:
//
//    This function is use to output a data unit for the first pass
//    DC progressive scan. The DC coefficients are encoded in the same manner
//    as in a sequential scan except for the point transform.
//
//    This function gets called twice for each data unit in the scan. The
//    first pass is used to gather Huffman statistics and the second is
//    used to Huffman-encode the data and write it to the output stream.
//    We use pointers to the statistics/output functions to ensure that
//    both passes are performed in the exact same manner.
//
//  Parameters:
//    row,col: Data unit position
//    dcfunction: Function for outputting DC coefficient values.
//    successiveapproximation: Successive Approximation
//
//    This function is of the type COMPONENTPASSFUNCTION.
//
void JpegEncoderComponent::progressiveDcFirst (
                          unsigned int row, unsigned int col,
                          DCOUTPUTFUNCTION dcfunction, ACOUTPUTFUNCTION,
                          unsigned int, unsigned int,
                          unsigned int successiveapproximation)
{
  // G.1.2.1

  // DC value calculation
  // A.4
  int value = dct_coefficients [row * du_cols + col][0]
            >> successiveapproximation ;

  // Section F.1.2
  int diff = value - last_dc_value ;
  last_dc_value = value ;

  // Break the difference into a category for Huffman coding and additional
  // raw bits for refinement.
  int bits ;
  if (diff >= 0)
  {
    bits = diff ;
  }
  else
  {
    diff = -diff ;
    bits = ~diff ;
  }
  int ssss = 0 ;  // Category
  while (diff != 0)
  {
    ++ ssss ;
    diff >>= 1 ;
  }
  (this->*dcfunction) (ssss, bits) ;

  return ;
}

//
//  Description:
//
//    This function outputs DC coefficient data for refining scans in a
//    progressive frame. This is the only thing simple about progressive
//    JPEG. In this scan we simply encode an additional bit for each
//    DC coefficient.
//
//    Since there is no Huffman coding for this refining DC scans this function
//    only gets called once per data unit in a scan. Therefore we do not
//    use the output function pararmeters.
//
//  Parameters:
//    row,col: Data unit position
//    ssa:  Successive Approximation
//
//    This function is of the type COMPONENTPASSFUNCTION.
//
void JpegEncoderComponent::progressiveDcRefine (
                            unsigned int row, unsigned int col,
                            DCOUTPUTFUNCTION, ACOUTPUTFUNCTION,
                            unsigned int, unsigned int,
                            unsigned int ssa)
{
  // Section G.1.2.1
  int value = (dct_coefficients [row * du_cols + col][0] >> ssa) & 0x1 ;
  output_stream->outputBits (value, 1) ;
  return ;
}

//
//  Description:
//
//    This function encodes a data unit for the first AC coefficient scan
//    for a spectral range in a progressive frame. The procedure here
//    is taken literally from the JPEG specification.
//
//    The AC encoding complexity is significantly increased over that of
//    sequential scans because End of Bands can span data units.
//
//  Parameters:
//    row,col: Data unit position
//    acfunction: Function for outputting AC coefficient values
//    sss:  Spectral Selection Start
//    sse:  Spectral Selection End
//    ssa:  Successive Approximation
//
void JpegEncoderComponent::progressiveAcFirst (
                            unsigned int row, unsigned int col,
                            ACOUTPUTFUNCTION acfunction,
                            unsigned int sss, unsigned int sse,
                            unsigned int ssa)
{
  JpegCoefficientBlock &du = dct_coefficients [row * du_cols + col] ;

  // G.1.2.2 Figure G.3
  unsigned int zerorun = 0 ;
  for (unsigned int ii = sss ; ii <= sse ; ++ ii)
  {
    int value = du [JpegZigZagInputOrder (ii)]  ;
    // Point Transform
    value = value / (1 << ssa) ;

    if (value == 0)
    {
      ++ zerorun ;
    }
    else
    {
      printEobRun (acfunction) ;
      // Figure G.5
      while (zerorun >= 16)
      {
        (this->*acfunction)(0xF0, 0, 0) ;
        zerorun -= 16 ;
      }

      int bits ;
      if (value >= 0)
      {
        bits = value ;
      }
      else
      {
        value = -value ;
        bits = ~value ;
      }
      int ssss = 0 ;
      while (value != 0)
      {
        ++ ssss ;
        value >>= 1 ;
      }
      unsigned int rrrrssss = (zerorun << 4) | ssss ;
      (this->*acfunction) (rrrrssss, bits, ssss) ;
      zerorun = 0 ;

      if (ii >= sse)
        return ;
    }
  }
  ++ eob_run ;

  // Do not allow the EOB run to exceed 0x7FFF.
  // G.1.2.2
  if (eob_run == 0x7FFF)
    printEobRun (acfunction) ;
  return ;
}

//
//  Description:
//
//    This function encodes the AC coefficients for a refining scan in a
//    progressive frame.
//
//    The JPEG standard is nebulous here (Section G.1.2.3). It is
//    unfortunate that for such a brain-damaged encoding method as
//    this that they should be unclear. In addition to the complexity
//    of having EOB runs span data units, data does not get written
//    out in the order it occurs.
//
//    This is why there are no section references in the code other than
//    the one above. I am simply guessing here, sorry. I created this code by
//    guessing and running the output through various applications that handle
//    progressive JPEG until I could find no complaints.
//
//    If you thing this is bad wait until you get to the part about decoding
//    progressive scans (G.2)!
//
//  Parameters:
//    row,col: Data unit position
//    acfunction: Function for outputting AC coefficient values
//    sss:  Spectral Selection Start
//    sse:  Spectral Selection End
//    ssa:  Successive Approximation
//
void JpegEncoderComponent::progressiveAcRefine (
                            unsigned int row, unsigned int col,
                            ACOUTPUTFUNCTION acfunction,
                            unsigned int sss, unsigned int sse,
                            unsigned int ssa)
{
  JpegCoefficientBlock &du = dct_coefficients [row * du_cols + col] ;
  // Number of zero coefficients - Note that existing non-zero coefficients
  // are not included in this count.
  unsigned int zerorun = 0 ;
  // The start of the zero run.
  unsigned int zerostart = sss ;
  // Number of existing non-zero coefficients - used only for error checking.
  unsigned int correctioncount = 0 ;

  for (unsigned int ii = sss ; ii <= sse ; ++ ii)
  {
    // Point Transform
    int value = du [JpegZigZagInputOrder (ii)] / (1 << ssa) ;

    // We have three types of values:
    //  o A Zero
    //  o A coefficient that was zero in all previous scan that we are
    //    going to make non-zero in this scan (value = +/-1)
    //  o An coefficient that was made non-zero in a previous scan
    //      (value > 1 OR value < -1)
    if (value == 0)
    {
      ++ zerorun ;
    }
    else if (value == 1 || value == -1)
    {
      // If way have an EOB run then print it out.
      printRefineEobRun (acfunction, sss, sse, ssa) ;

      // The longest zero run we can have is 16.
      while (zerorun >= 16)
      {
        (this->*acfunction)(0xF0, 0, 0) ;
        zerorun -= 16 ;

        // Refine all the existing coefficients skipped by the zero run.
        for (int zerocount = 0 ;
             zerocount < 16 ;
             ++ zerostart)
        {
          ASSERT (zerostart >= sss && zerostart <= sse && correctioncount <= JPEGSAMPLESIZE) ; // Invalid Zero Run

          int oldvalue = du [JpegZigZagInputOrder (zerostart)] / (1 << ssa) ;
          if (oldvalue < 0)
            oldvalue = - oldvalue ;
          if (oldvalue > 1)
          {
            (this->*acfunction)(-1, (oldvalue & 0x1), 1) ;
            -- correctioncount ;
          }
          else if (oldvalue == 0)
          {
            // Because we need to count only zero values we our loop counter
            // gets incremented here.
            ++ zerocount ;
          }
          else
          {
            // If the value is +/- 1 we should have already processed it.
            throw JpegEncoder::JpegError ("INTERNAL ERROR - Bad Value") ;
          }
        }
      }

      // This is the first time this value has been nonzero.
      int output = (zerorun << 0x4) | 1 ;
      if (value > 0)
        (this->*acfunction)(output, 1, 1) ;
      else
        (this->*acfunction)(output, 0, 1) ;
      zerorun = 0 ;

      // No go back and refine all the previously non-zero coefficients
      // skipped by this zero run.
      for (unsigned int jj = zerostart ; jj < ii ; ++ jj)
      {
        int oldvalue = du [JpegZigZagInputOrder (jj)] / (1 << ssa) ;
        if (oldvalue < 0)
          oldvalue = - oldvalue ;
        if (oldvalue > 1)
        {
          (this->*acfunction)(-1, (oldvalue & 0x1), 1) ;
          -- correctioncount ;
        }
      }
      zerostart = ii + 1 ;
      if (ii == sse)
        return ;  // All finished with this data unit.
    }
    else
    {
      // We only use this counter for error checking. It contains the
      // number of previously non-zero coefficients that we have skipped
      /// as part of a zero run.
      ++ correctioncount ;
    }
  }
  // If we get here then the data unit ends with a string of zero coefficients
  // or previously non-zero coefficients that we are skipping.
  if (eob_run == 0)
  {
    // We are beginning and End of Band run. Mark the starting position
    // including the spectral position.
    eob_start_du_row = row ;
    eob_start_du_col = col ;
    eob_start_position = zerostart ;
  }
  ++ eob_run ;
  // G.1.2.2
  if (eob_run == 0x7FFF)
    printRefineEobRun (acfunction, sss, sse, ssa) ;
  return ;
}

//
//  Description:
//
//    This function Resets the End of Band run counters. It should be called
//    before beginning to output a scan.
//
void JpegEncoderComponent::resetEobRun ()
{
  eob_run = 0 ;

  // We use values here that should make an error easier to detect.
  eob_start_du_row = du_cols * du_rows ;
  eob_start_du_col = du_cols * du_rows ;
  eob_start_position = JPEGSAMPLESIZE ;

  return ;
}

//
//  Description:
//
//    This function outputs an End of Band run in a refining AC ceofficient
//    scan of a progressive frame. As for the rest of the refining AC scan
//    data I am mostly guessing here. I have taken the process from
//    Figure G.4 and the vague description in Section G.1.2.3. It seems to
//    work.
//
//  Parameters:
//    acfunction: Function for outputting AC coefficient values
//    sss:  Spectral Selection Start
//    sse:  Spectral Selection End
//    ssa:  Successive Approximation
//
void JpegEncoderComponent::printRefineEobRun (ACOUTPUTFUNCTION acfunction,
                            unsigned int sss, unsigned int sse,
                            unsigned int ssa)
{
  if (eob_run != 0)
  {
    unsigned int bits = eob_run ;
    unsigned int value = bits >> 1 ;
    unsigned int ssss = 0 ; // Category (Table G.1)
    while (value != 0)
    {
      value >>= 1 ;
      ++ ssss ;
    }
    (this->*acfunction)(ssss << 4, bits, ssss) ;

    // Now that we have output the EOB run we need to refine each coefficient
    // that we skipped that had previously been non-zero.
    unsigned int  eobcounter = 0 ;
    unsigned int row = eob_start_du_row ;
    unsigned int col = eob_start_du_col ;
    while (eobcounter < eob_run)
    {
      JpegCoefficientBlock &du
        = dct_coefficients [row * du_cols + col] ;
      for (unsigned int kk = eob_start_position ;
           kk <= sse ;
           ++ kk)
      {
        int value = du [JpegZigZagInputOrder (kk)] / (1<< ssa) ;
        if (value < 0)
          value = - value ;
        if (value > 1)
        {
          (this->*acfunction)(-1, (value & 0x1), 1) ;
        }
      }
      // Except for the first data unit we go to the first spectral value
      // in the scan.
      eob_start_position = sss ;

      ++ eobcounter ;
      ++ col ;
      if (col == du_cols)
      {
        col = 0 ;
        ++ row ;
        ASSERT (row < du_cols || eobcounter == eob_run) ; // EOB run extends past last row
      }
    }

    // Some values that are certain to cause errors if improperly used.
    eob_start_position = JPEGSAMPLESIZE ;
    eob_start_du_row = du_cols * du_rows ;
    eob_start_du_col = du_cols * du_rows ;

    eob_run = 0 ; // Reset the counter.
  }

  return ;
}

//
//  Description
//
//    This function sets the horizontal sampling frequency for the
//    component.
//
//  Parameters:
//    value:  The new sampling frequency.
//
void JpegEncoderComponent::setHorizontalFrequency (unsigned int value)
{
  CHECKARGUMENT (value <= JPEGMAXSAMPLINGFREQUENCY && value >= JPEGMINSAMPLINGFREQUENCY) ;
  h_frequency = value ;
  return ;
}

//
//  Description
//
//    This function sets the vertical sampling frequency for the
//    component.
//
//  Parameters:
//    value:  The new sampling frequency.
//
void JpegEncoderComponent::setVerticalFrequency (unsigned int value)
{
  CHECKARGUMENT (value <= JPEGMAXSAMPLINGFREQUENCY && value >= JPEGMINSAMPLINGFREQUENCY) ;

  v_frequency = value ;
  return ;
}

void JpegEncoderComponent::rgbConvert (Colosseum::JpegEncoder &encoder, 
                                       const BitmapImage &image, 
                                       unsigned int maxhf, unsigned int maxvf,
                                       JpegEncoderComponent &ycomponent,
                                       JpegEncoderComponent &cbcomponent,
                                       JpegEncoderComponent &crcomponent) 
{
  const int progressscale = 8 ;
  unsigned int progress = 0 ;
  unsigned int progressincrement = (100 << progressscale) / image.getHeight () ;

  // Image dimensions rounded up to a multiple of JpegSampleWidth x Max Sampling Frequency

  unsigned int columns = NoninterleavedColumns (image.getWidth (), maxhf) ;
  unsigned int rows = NoninterleavedRows (image.getHeight (), maxvf) ;
  ycomponent.allocateComponentBuffer (image.getWidth (), image.getHeight (), maxhf, maxvf) ;
  cbcomponent.allocateComponentBuffer (image.getWidth (), image.getHeight (), maxhf, maxvf) ;
  crcomponent.allocateComponentBuffer (image.getWidth (), image.getHeight (), maxhf, maxvf) ;

  JPEGSAMPLE *ypointer = ycomponent.component_buffer ;
  JPEGSAMPLE *cbpointer = cbcomponent.component_buffer ;
  JPEGSAMPLE *crpointer = crcomponent.component_buffer ;
  unsigned int ii ; // Loop counters are declared here
  unsigned int jj ; // because they are used outside the loops/
  const BitmapImage::Pixel *pixel = &image [0] ;
  for (ii = 0 ; ii < image.getHeight () ; ++ ii)
  {
    JPEGSAMPLE yvalue ;
    JPEGSAMPLE cbvalue ;
    JPEGSAMPLE crvalue ;
    for (jj = 0 ; jj < image.getWidth () ; ++ jj, ++ pixel)
    {
      yvalue = RgbToY (pixel->red, pixel->green, pixel->blue) ;
      cbvalue = RgbToCb (pixel->red, pixel->green, pixel->blue) ;
      crvalue = RgbToCr (pixel->red, pixel->green, pixel->blue) ;
      *ypointer = yvalue ; ++ ypointer ;
      *cbpointer = cbvalue ; ++ cbpointer ;
      *crpointer = crvalue ; ++ crpointer ;
    }
    // Extend the last row
    for ( ; jj < columns ; ++ jj)
    {
      *ypointer = yvalue ; ++ ypointer ;
      *cbpointer = cbvalue ; ++ cbpointer ;
      *crpointer = crvalue ; ++ crpointer ;
    }
    progress += progressincrement ;
    encoder.callProgressFunction (progress >> progressscale) ;
  }
  for ( ; ii < rows ; ++ ii)
  {
    // Copy from the previous row
    *ypointer = ypointer [-columns] ; ++ ypointer ;
    *cbpointer = crpointer [-columns] ; ++ cbpointer ;
    *crpointer = crpointer [-columns] ; ++ crpointer ;
  }
  encoder.callProgressFunction (100) ;
  return ;
}

void JpegEncoderComponent::grayScaleConvert (Colosseum::JpegEncoder &encoder,
                                             const Colosseum::BitmapImage &image, 
                                             JpegEncoderComponent &ycomponent) 
{
  ycomponent.du_rows = (image.getHeight () + JPEGSAMPLEWIDTH - 1) / JPEGSAMPLEWIDTH ;
  ycomponent.du_cols = (image.getWidth () + JPEGSAMPLEWIDTH - 1) / JPEGSAMPLEWIDTH ;

  ycomponent.dct_coefficients = new JpegCoefficientBlock [ycomponent.du_cols * ycomponent.du_rows] ;

  const int progressscale = 8 ;
  unsigned int progress = 0 ;
  unsigned int progressincrement = (100 << progressscale) / (ycomponent.du_rows) ;

  JpegEncoderDataUnit data ;
  unsigned int index = 0 ;

  for (unsigned int ii = 0 ; ii < ycomponent.du_rows ; ++ ii)
  {
    for (unsigned int jj = 0 ; jj < ycomponent.du_cols ; ++ jj)
    {
      for (unsigned int kk = 0 ; kk < JPEGSAMPLEWIDTH ; ++ kk)
      {
        for (unsigned int ll = 0 ; ll < JPEGSAMPLEWIDTH ; ++ ll)
        {
          unsigned int row = ii * JPEGSAMPLEWIDTH + kk ;
          unsigned int column = jj * JPEGSAMPLEWIDTH + ll ;
          if (row < image.getHeight () && column < image.getWidth ()) 
          {
            const BitmapImage::Pixel *pixel = &image [row * image.getWidth () + column] ;
            JPEGSAMPLE yvalue = RgbToY (pixel->red, pixel->green, pixel->blue) ;
            data [kk][ll] = yvalue ;
          }
          else
          {
            data [kk][ll] = JPEGMIDPOINTSAMPLEVALUE ;
          }
        }
      }

      data.ForwardDct (*ycomponent.quantization_table, ycomponent.dct_coefficients [index]) ; 
      ++ index ;
    }
    progress += progressincrement ;
    encoder.callProgressFunction (progress >> progressscale) ;
  }
  encoder.callProgressFunction (100) ;
  return ;
}


void JpegEncoderComponent::sample1to1Component (Colosseum::JpegEncoder &encoder)
{
  const int progressscale = 8 ;
  unsigned int progress = 0 ;
  unsigned int progressincrement = (100 << progressscale) / (du_rows) ;

  JpegEncoderDataUnit data ;
  unsigned int index = 0 ;
  JPEGSAMPLE *row0 = &component_buffer [0] ;
  JPEGSAMPLE *row1 = &component_buffer [component_buffer_columns] ;
  JPEGSAMPLE *row2 = &component_buffer [component_buffer_columns*2] ;
  JPEGSAMPLE *row3 = &component_buffer [component_buffer_columns*3] ;
  JPEGSAMPLE *row4 = &component_buffer [component_buffer_columns*4] ;
  JPEGSAMPLE *row5 = &component_buffer [component_buffer_columns*5] ;
  JPEGSAMPLE *row6 = &component_buffer [component_buffer_columns*6] ;
  JPEGSAMPLE *row7 = &component_buffer [component_buffer_columns*7] ;

  for (unsigned int ii = 0 ; ii < du_rows ; ++ ii)
  {
    for (unsigned int jj = 0 ; jj < du_cols ; ++ jj)
    {
      for (unsigned int kk = 0 ; kk < JPEGSAMPLEWIDTH ; ++ kk)
      {
        data [0][kk] = *row0 ; ++ row0 ;
        data [1][kk] = *row1 ; ++ row1 ;
        data [2][kk] = *row2 ; ++ row2 ;
        data [3][kk] = *row3 ; ++ row3 ;
        data [4][kk] = *row4 ; ++ row4 ;
        data [5][kk] = *row5 ; ++ row5 ;
        data [6][kk] = *row6 ; ++ row6 ;
        data [7][kk] = *row7 ; ++ row7 ;
      }

      data.ForwardDct (*quantization_table, dct_coefficients [index]) ;
      ++ index ;
    }
    row0 = row7 ;
    row1 = &row0 [component_buffer_columns] ;
    row2 = &row1 [component_buffer_columns] ;
    row3 = &row2 [component_buffer_columns] ;
    row4 = &row3 [component_buffer_columns] ;
    row5 = &row4 [component_buffer_columns] ;
    row6 = &row5 [component_buffer_columns] ;
    row7 = &row6 [component_buffer_columns] ;

    progress += progressincrement ;
    encoder.callProgressFunction (progress >> progressscale) ;
  }
  encoder.callProgressFunction (100) ;
  return ;
}


void JpegEncoderComponent::sampleNtoNComponent (Colosseum::JpegEncoder &encoder,
                                                unsigned int maxhf,
                                                unsigned int maxvf)
{
  unsigned int vperiod = maxvf / v_frequency ;
  unsigned int hperiod = maxhf / h_frequency ;



  JpegEncoderDataUnit data ;
  unsigned int index = 0 ;

  const int progressscale = 8 ;
  unsigned int progress = 0 ;
  unsigned int progressincrement = (100 << progressscale) / (du_rows) ;

  for (unsigned int durow = 0 ; durow < du_rows ; ++ durow)
  {
    // Offset of the upper left corner of the MCU from the start of the image.
    unsigned int mcuoffset = durow * component_buffer_columns * vperiod * JPEGSAMPLEWIDTH ;
    for (unsigned int ducol = 0 ; ducol < du_cols  ; ++ ducol)
    {
      unsigned int sums [JPEGSAMPLEWIDTH][JPEGSAMPLEWIDTH] ;
      memset (sums, 0, sizeof (sums)) ;

      JPEGSAMPLE *rowbuffer =&component_buffer [mcuoffset] ;
      for (unsigned int mcurow = 0 ; mcurow  < JPEGSAMPLEWIDTH ; ++ mcurow)
      {
        for (unsigned int ii = 0 ; ii < vperiod ; ++ ii)
        {
          unsigned int offset  = 0 ;
          for (unsigned int mcucol = 0 ; mcucol < JPEGSAMPLEWIDTH ; ++ mcucol)
          {
            for (unsigned int jj = 0 ; jj < hperiod ; ++ jj, ++ offset)
            {
              sums [mcurow][mcucol] += rowbuffer [offset] ;
            }
          }
          rowbuffer += component_buffer_columns ;
        }
      }
      for (unsigned int ii = 0 ; ii < JPEGSAMPLEWIDTH ; ++ ii)
      {
        for (unsigned int jj = 0 ; jj < JPEGSAMPLEWIDTH ; ++ jj)
        {
          data [ii][jj] = sums [ii][jj] / (vperiod * hperiod) ;
        }
      }

      data.ForwardDct (*quantization_table, dct_coefficients [index]) ;
      mcuoffset += JPEGSAMPLEWIDTH * hperiod ;
      ++ index ;
    }
    progress += progressincrement ;
    encoder.callProgressFunction (progress >> progressscale) ;
  }
  encoder.callProgressFunction (100) ;
  return ;
}


void JpegEncoderComponent::sampleComponent (Colosseum::JpegEncoder &encoder,
                                            unsigned int maxhf,
                                            unsigned int maxvf)
{
  ASSERT (component_buffer_rows % JPEGSAMPLEWIDTH == 0) ;
  ASSERT (component_buffer_columns % JPEGSAMPLEWIDTH == 0) ;

  du_rows = v_frequency * component_buffer_rows / JPEGSAMPLEWIDTH / maxvf ;
  du_cols = h_frequency * component_buffer_columns / JPEGSAMPLEWIDTH / maxvf ;

  dct_coefficients = new JpegCoefficientBlock [du_cols * du_rows] ;

  if (h_frequency == maxhf && v_frequency == maxvf)
    sample1to1Component (encoder) ;
  else
    sampleNtoNComponent (encoder, maxhf, maxvf) ;


  delete [] component_buffer ; component_buffer = 0 ;
  return ;

}

void JpegEncoderComponent::allocateComponentBuffer (
                                unsigned int imagewidth,
                                unsigned int imageheight,
                                unsigned int maxhf,
                                unsigned int maxvf)
{
  unsigned int columns = NoninterleavedColumns (imagewidth, maxhf) ;
  unsigned int rows = NoninterleavedRows (imageheight, maxvf) ;

  ASSERT (rows % JPEGSAMPLEWIDTH == 0)
  ASSERT (columns % JPEGSAMPLEWIDTH == 0)

  delete [] component_buffer ;
  component_buffer = new JPEGSAMPLE [rows * columns] ;

  component_buffer_columns = columns ;
  component_buffer_rows = rows ;

  noninterleaved_du_rows
     = (imageheight * maxvf
        + v_frequency * JPEGSAMPLEWIDTH - 1) // Rounding
     / (v_frequency * JPEGSAMPLEWIDTH);
  noninterleaved_du_colummns
     = (imagewidth * maxhf
        + h_frequency * JPEGSAMPLEWIDTH - 1) // Rounding
     / (h_frequency * JPEGSAMPLEWIDTH);

  return ;
}


} // End Namespace Colosseum Private
