//
// 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 Component Class Implementation
//
// Author:  John M. Miano  miano@colosseumbuilders.com
//

#include <iostream>

#include "jpgdecodercomponent.h"
#include "jpgdecoderquantization.h"
#include "jpgdecoder.h"
#include "jpgcoefficient.h"
#include "jpgpvt.h"
#include "bitmapimage.h"

using namespace std ;
using namespace Colosseum ;
using namespace ColosseumPrivate ;

namespace
{

inline void RefineAcCoefficient (Colosseum::JpegInputStream &inputstream,
                                 unsigned int ssa,
                                 BYTE2 &value)
{
  // 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 ;
}

//
//  Description:
//
//    SequentialOnly
//
//    This function extends the sign bit of a decoded value.
//
//  Parameters:
//    vv: The bit value
//    tt: The length of the bit value
//

inline int Extend (int vv, int tt)
{
  // Extend function defined in Section F.2.2.1 Figure F.12
   // The tt'th bit of vv is the sign bit. One is for
   // positive values and zero is for negative values.
   int vt = 1 << (tt - 1) ;
   if (vv < vt)
   {
      vt = (-1 << tt) + 1 ;
      return vv + vt ;
   }
   else
   {
      return vv ;
   }
}

//
//  Description:
//
//    This function calculates the value for a pixel using a triangle filter.
//
//    The filtering is performed as follows:
//
//    o We make the origin at the upper left corner and have the positive
//      axis go down and to the left.
//    o We place the component values [x,y] at (0,0), (hperiod, 0),
//      (0, vperiod), (hperiod, vperiod).
//    o We assume the physical pixels are located at
//
//      (1/2, 1/2), (3/2, 1/2) ... (hperiod - 1/2, 1/2)
//      (1/2, 3/2), (3/2, 3/2, ... (hperiod - 1/2, 3/2)
//        ...
//      (1/2, vperiod - 1/2), (3/2, vperiod - 1/2), ... (hperiod - 1/2, vperiod - 1/2)
//
//     o We calculate the filter value for each pixel using a linear weighting
//       of the four component values based upon the distance from each.
//
//  Parameters:
//
//  xpos, ypos: The position of the pixel relative to the upper left
//              corner. Since the positions are always fractions (N + 1/2) the
//              input valus are multiplied by 2.
//  ul, ur, ll, lr: The component values at the upper left, upper right, lower
//                   left, and lower right.
//  hperiod, vperiod: The sampling for the component. Each component value
//                    represents the value of hperiod x vperiod pixels.
//
//

inline JPEGSAMPLE TriangleFilter (int xpos, int ypos,
                                  int ul, int ur,
                                  int ll, int lr,
                                  unsigned int hperiod,
                                  unsigned int vperiod)
{
  int result = ((ul * (2 * hperiod - xpos) + ur * xpos) * (2 * vperiod - ypos)
                + (ll * (2 * hperiod - xpos) + lr * xpos) * ypos
                + 4 * hperiod * vperiod - 1)
             / (4 * hperiod * vperiod) ;
  ASSERT (result >= 0 && result <= JPEGMAXSAMPLEVALUE) ;
  return result ;
}

//
//  Description:
//
//    This function triangle filters a block of pixels at the corner of a
//    data unit. At the corner there are (hperiod/2) * (vperiod/2) pixels.
//
//  Parameters:
//
//    startx, starty: The position of the upper left pixel in the block relative
//                    to the positions of the component values. These values
//                    are scaled by 2 so they will not be fractions. For an
//                    upper left corner these values will be
//                    (hperiod + 1,  vperiod + 1), upper right (1, vperiod + 1),
//                    lower left (hperiod + 1, 1), lower right (1, 1).
//    startposition: The location to output the upper left pixel.
//    rowwidth: The number of pixels per row in the output buffer.
//    up, up, ll, lr: The component values as the four corners.
//    hperiod, vperiod: The component sampling.
//
inline void FilterCorner (unsigned int startx,
                          unsigned int starty,
                          JPEGSAMPLE *startposition,
                          unsigned int rowwidth,
                          int ul, int ur,
                          int ll, int lr,
                          unsigned int hperiod,
                          unsigned int vperiod)
{
  unsigned int yy = starty ;
  unsigned int rowoffset = 0 ;
  for (unsigned int ii = 0 ; ii < vperiod / 2 ; ++ ii, yy += 2)
  {
    unsigned int xx = startx ;
    unsigned int offset = rowoffset ;
    for (unsigned int jj = 0 ; jj < hperiod / 2 ; ++ jj, xx += 2)
    {
      startposition [offset] = TriangleFilter (xx, yy,
                                               ul, ur,
                                               ll, lr,
                                               hperiod, vperiod) ;
      ++ offset ;
    }
    rowoffset += rowwidth ;
  }
  return ;
}

//
//  Description:
//
//    This function triangle filters a block of pixels at the top or bottom of
//    data unit (but not at the corner). Each block contains
//    hperiod * (vperiod / 2) pixels.
//
//  Parameters:
//
//    starty: The position of the frst row of pixels pixels in the block relative
//            to the positions of the component values. These values are scaled
//            by 2 to eliminate fractions. For an upper row, this value is vperiod + 1,
///           for a bottom row it is 1.
//    startposition: The location to output the upper left pixel.
//    rowwidth: The number of pixels per row in the output buffer.
//    up, up, ll, lr: The component values as the four corners.
//    hperiod, vperiod: The component sampling.
//
inline void FilterTopOrBottom (unsigned int starty,
                               JPEGSAMPLE *startposition,
                               unsigned int rowwidth,
                               int ul, int ur,
                               int ll, int lr,
                               unsigned int hperiod,
                               unsigned int vperiod)
{
  unsigned int rowoffset = 0 ;
  for (unsigned int ii = 0, yy = starty ; ii < vperiod / 2 ; ++ ii, yy += 2)
  {
    unsigned int xx = 1 ;
    unsigned int offset = rowoffset ;
    for (unsigned int jj = 0 ; jj < hperiod ; ++ jj, xx += 2)
    {
      startposition [offset] = TriangleFilter (xx, yy,
                                               ul, ur,
                                               ll, lr,
                                               hperiod, vperiod) ;
      ++ offset ;
    }
    rowoffset += rowwidth ;
  }
  return ;
}

//
//  Description:
//
//    This function triangle filters a block of pixels at either side of a data
//    unit (but not at the corner). Each block contains
//    (hperiod / 2) * vperiod pixels.
//
//  Parameters:
//
//    startx: The position of the fist column of pixels pixels in the block relative
//            to the positions of the component values. These values are scaled
//            by 2 to eliminate fractions. For pixels at the left side of a data
//            unit, this value is hperiod + 1. At the right size it is 1.
//    startposition: The location to output the upper left pixel.
//    rowwidth: The number of pixels per row in the output buffer.
//    up, up, ll, lr: The component values as the four corners.
//    hperiod, vperiod: The component sampling.
//
inline void FilterSide (unsigned int startx,
                        JPEGSAMPLE *startposition,
                        unsigned int rowwidth,
                        int ul, int ur,
                        int ll, int lr,
                        unsigned int hperiod,
                        unsigned int vperiod)
{
  unsigned int rowoffset = 0 ;
  for (unsigned int ii = 0, yy = 1 ; ii < vperiod ; ++ ii, yy +=2)
  {
    unsigned int offset = rowoffset ;
    for (unsigned int jj = 0, xx = startx ; jj < hperiod / 2 ; ++ jj, xx += 2)
    {
      startposition [offset] = TriangleFilter (xx, yy,
                                               ul, ur,
                                               ll, lr,
                                               hperiod, vperiod) ;
      ++ offset ;
    }
    rowoffset += rowwidth ;
  }
  return ;
}

//
//  Description:
//
//    This function triangle filters a vperiod x hperiod block of pixels that
///   are not at the edge of the data unit.
//
//  Parameters:
//
//    startposition: The location to output the upper left pixel.
//    rowwidth: The number of pixels per row in the output buffer.
//    up, up, ll, lr: The component values as the four corners.
//    hperiod, vperiod: The component sampling.
//
inline void FilterMiddle (JPEGSAMPLE *startposition,
                          unsigned int rowwidth,
                          int ul, int ur,
                          int ll, int lr,
                          unsigned int hperiod,
                          unsigned int vperiod)
{
  unsigned int rowoffset = 0 ;
  for (unsigned int ii = 0, yy = 1 ; ii < vperiod ; ++ ii, yy +=2)
  {
    unsigned int offset = rowoffset ;
    for (unsigned int jj = 0, xx = 1 ; jj < hperiod ; ++ jj, xx += 2)
    {
      startposition [offset] = TriangleFilter (xx, yy,
                                               ul, ur,
                                               ll, lr,
                                               hperiod, vperiod) ;
      ++ offset ;
    }
    rowoffset += rowwidth ;
  }
  return ;
}

//
//  Decription:
//
//    This function triangle filters a data unit.
//
//  Parameters:
//
//    startposition: The location to output the upper left pixel.
//    rowwidth: The number of pixels per row in the output buffer.
//    hperiod, vperiod: The component sampling.
//    upperleft, uppercenter, upperright, left: Adjacent data units (used
//                                              to filter pixels at the edge.
//    center:  The data unit to filter.
//    right, lowerleft, lowercenter, lowerright: More adjacent data units.
//
//
inline void FilterDataUnit (
                JPEGSAMPLE *startposition,
                unsigned int rowwidth,
                unsigned int hperiod,
                unsigned int vperiod,
                const JpegDecoderDataUnit &upperleft,
                const JpegDecoderDataUnit &uppercenter,
                const JpegDecoderDataUnit &upperright,
                const JpegDecoderDataUnit &left,
                const JpegDecoderDataUnit &center,
                const JpegDecoderDataUnit &right,
                const JpegDecoderDataUnit &lowerleft,
                const JpegDecoderDataUnit &lowercenter,
                const JpegDecoderDataUnit &lowerright)
{
  const unsigned int LAST = JPEGSAMPLEWIDTH - 1 ;

  unsigned int offset = 0 ;
  // Upper Right Corner
  FilterCorner (hperiod + 1, vperiod + 1,
                &startposition [offset], rowwidth,
                upperleft [LAST][LAST], uppercenter [LAST][0],
                left [0][LAST], center [0][0],
                hperiod, vperiod) ;
  offset += hperiod /2 ;
  // Top Edges
  for (unsigned int ii = 0 ; ii < LAST ; ++ ii)
  {
    FilterTopOrBottom (vperiod + 1,
                       &startposition [offset], rowwidth,
                       uppercenter [LAST][ii], uppercenter [LAST][ii + 1],
                       center [0][ii], center [0][ii + 1],
                       hperiod, vperiod) ;
    offset += hperiod ;
  }
  // Upper Left Corner
  FilterCorner (1, vperiod + 1,
                &startposition [offset], rowwidth,
                uppercenter [LAST][LAST], upperright [LAST][0],
                center [0][LAST], right [0][0],
                hperiod, vperiod) ;
  offset += (hperiod + vperiod * rowwidth) /2 - JPEGSAMPLEWIDTH * hperiod ;
  ASSERT (offset == rowwidth * vperiod / 2) ;

  for (unsigned int mcurow = 0 ; mcurow < LAST ; ++ mcurow)
  {
    // Left Edge
    FilterSide (hperiod + 1,
               &startposition [offset], rowwidth,
                left [mcurow][LAST], center [mcurow][0],
                left [mcurow +1][LAST], center [mcurow + 1][0],
                hperiod, vperiod) ;
    offset += hperiod / 2 ;

    // Middle
    for (unsigned int mcucol = 0 ; mcucol < LAST ; ++ mcucol)
    {
      FilterMiddle (&startposition [offset], rowwidth,
                    center [mcurow][mcucol], center [mcurow][mcucol + 1],
                    center [mcurow + 1][mcucol], center [mcurow + 1][mcucol + 1],
                    hperiod, vperiod) ;
      offset += hperiod ;
    }
    // Right Edge
    FilterSide (1,
                &startposition [offset], rowwidth,
                center [mcurow][LAST], right [mcurow][0],
                center [mcurow + 1][LAST], right [mcurow + 1][0],
                hperiod, vperiod) ;

    offset += (hperiod / 2 + vperiod * rowwidth) - JPEGSAMPLEWIDTH * hperiod ;
  }

  // Bottom Left Corner
  FilterCorner (hperiod + 1, 1,
                &startposition [offset], rowwidth,
                left [LAST][LAST], center [LAST][0],
                lowerleft [0][LAST], lowercenter [0][0],
                hperiod, vperiod) ;
  offset += hperiod / 2 ;
  // Bottom Edge
BILLSELLSPOOP
  for (unsigned int ii = 0 ; ii < LAST ; ++ ii)
  {
    FilterTopOrBottom (1,
                       &startposition [offset], rowwidth,
                       center [LAST][ii], center [LAST][ii + 1],
                       lowercenter [0][ii], lowercenter [0][ii + 1],
                       hperiod, vperiod) ;
    offset += hperiod ;
  }
ENDBILLSELLSPOOP
  // Bottom Right Corner
  FilterCorner (1, 1,
                &startposition [offset], rowwidth,
                center [LAST][LAST], right [LAST][0],
                lowercenter [0][LAST], lowerright [0][0],
                hperiod, vperiod) ;


  return ;
}

//
//  Description:
//
//    This function copies the component values at the edge of a data
//    unit to the 8 surrounding data units so that each value at the edge
//    of the center is the same as the adjacent value in the ajoining
//    data unit. This is used to create dummy data units at the edge of an
//    image.
//
//  Parameters:
//    center: The data unit to copy
//    upperleft, uppercenter, upperight,
//    left, right,
//    lowerleft, lowercenter, lowerright: The dummy data units to copy to.
//
inline void FillEdges (
                const JpegDecoderDataUnit &center,
                JpegDecoderDataUnit &upperleft,
                JpegDecoderDataUnit &uppercenter,
                JpegDecoderDataUnit &upperright,
                JpegDecoderDataUnit &left,
                JpegDecoderDataUnit &right,
                JpegDecoderDataUnit &lowerleft,
                JpegDecoderDataUnit &lowercenter,
                JpegDecoderDataUnit &lowerright)
{
  const int LAST = JPEGSAMPLEWIDTH - 1 ;
  upperleft [LAST][LAST] = center [0][0] ;
  lowerleft [0][LAST] = center [LAST][0] ;
  upperright [LAST][0] = center [0][LAST] ;
  lowerright [0][0] = center [LAST][LAST] ;
  for (unsigned int ii = 0 ; ii < JPEGSAMPLEWIDTH ; ++ ii)
  {
    left [ii][LAST] = center [ii][0] ;
    uppercenter [LAST][ii] = center [0][ii] ;
    right [ii][0] = center [ii][LAST] ;
    lowercenter [0][ii] = center [LAST][ii] ;
  }
}

}

namespace ColosseumPrivate
{
//
//  Description:
//
//    Class default constructor
//
JpegDecoderComponent::JpegDecoderComponent ()
: horizontal_frequency (0),
  vertical_frequency (0),
  v_sampling (0),
  h_sampling (0),
  last_dc_value (0),
  ac_table (0),
  dc_table (0),
  quantization_table (0),
  eob_run (0),
  noninterleaved_rows (0),
  noninterleaved_cols (0),
  data_units (0),
  coefficient_blocks (0),
  upsample_data (0)
{
  return ;
}

//
//  Description:
//
//    Class Destructor
//
JpegDecoderComponent::~JpegDecoderComponent ()
{
  delete [] data_units ; data_units = 0 ;
  delete [] upsample_data ; upsample_data = 0 ;
  return ;
}

//
//  Description:
//
//    This function sets the horizontal sampling frequency
//    for the component.
//
//  Parameters:
//    value: The sampling factor (1..4)
//
void JpegDecoderComponent::horizontalFrequency (unsigned int value)
{
  if (value < JPEGMINSAMPLINGFREQUENCY || value > JPEGMAXSAMPLINGFREQUENCY)
    throw JpegDecoder::JpegError ("Sampling Frequency Outside the Range 1-4") ;

  horizontal_frequency = value ;
  return ;
}

//
//  Description:
//
//    This function sets the vertical sampling frequency
//    for the component.
//
//  Parameters:
//    value: The sampling factor (1..4)
//
void JpegDecoderComponent::verticalFrequency (unsigned int value)
{
  if (value < JPEGMINSAMPLINGFREQUENCY || value > JPEGMAXSAMPLINGFREQUENCY)
    throw JpegDecoder::JpegError ("Vertical Sampling Frequency Outside the Range 1-4") ;

  vertical_frequency = value ;

  return ;
}

//
//  Description:
//
//    This function associates a quantization table with the component.
//
//  Parameters:
//    table:  The quantization table
//
void JpegDecoderComponent::setQuantizationTable (
                                     JpegDecoderQuantizationTable &table)
{
  quantization_table = &table ;
  return ;
}

//
//  Description:
//
//    This function determines the dimensions for the component and allocates
//    the storage to hold the component's data.
//
//  Parameters:
//    decoder:  The jpeg decoder this component belongs to.
//
void JpegDecoderComponent::allocateComponentBuffers (
                               const JpegDecoder &decoder)
{
  if (data_units == 0)
  {
    image_height = decoder.frameHeight () ;
    image_width = decoder.frameWidth () ;

    // Determine sampling for the component. This is the amount of
    // stretching needed for the component.
    v_sampling = decoder.maxVFrequency () / vertical_frequency ;
    h_sampling = decoder.maxHFrequency () / horizontal_frequency ;

    // Determine the component's dimensions in a non-interleaved scan.
    noninterleaved_rows = (image_height
                           + v_sampling * JPEGSAMPLEWIDTH - 1)
                          / (v_sampling * JPEGSAMPLEWIDTH) ;
    noninterleaved_cols = (image_width
                           + h_sampling * JPEGSAMPLEWIDTH - 1)
                          / (h_sampling * JPEGSAMPLEWIDTH) ;

    du_rows = decoder.mcuRows () * vertical_frequency ;
    du_cols = decoder.mcuCols () * horizontal_frequency ;

    data_units = new JpegDecoderDataUnit [du_rows * du_cols] ;
  }

  if (decoder.isProgressive () && coefficient_blocks == 0)
  {
    unsigned int count = du_rows * du_cols ;
    coefficient_blocks = new JpegCoefficientBlock [count] ;
    memset (coefficient_blocks,
            0,
            sizeof (JpegCoefficientBlock) * count) ;
  }
  return ;
}

//
//  Description:
//
//    This function frees the memory allocated by the component
//    during the decompression process.
//
void JpegDecoderComponent::freeComponentBuffers ()
{
  delete [] data_units ; data_units = 0 ;
  delete [] coefficient_blocks ; coefficient_blocks = 0 ;
  delete [] upsample_data ; upsample_data = 0 ;
  return ;
}

//
//  Description:
//
//    This function asigned Huffman tables to the component.
//
//  Parameters:
//    dc:  The DC Huffman table
//    ac:  The AC Huffman table
//
void JpegDecoderComponent::setHuffmanTables (JpegHuffmanDecoder &dc,
                                             JpegHuffmanDecoder &ac)
{
  dc_table = &dc ;
  ac_table = &ac ;
  return ;
}

//
//  Description:
//
//    This function ensures that this component has a defined
//    AC table assigned to it. If not, it throws an exception.
//
void JpegDecoderComponent::checkAcTable ()
{
  // If this occurs then we have a programming error.
  ASSERT (ac_table != 0) ;

  if (! ac_table->tableDefined ())
    throw JpegDecoder::JpegBadStream ("AC Table Not Defined") ;

  return ;
}

//
//  Sequential and Progressive
//
//  This function is called before processing a scan. It ensures that the
//  DC Huffman table used by the component has actually been defined.
//
void JpegDecoderComponent::checkDcTable ()
{
  ASSERT (dc_table != 0)

  // This condition could be caused by a corrupt JPEG stream.
  if (! dc_table->tableDefined ())
    throw JpegDecoder::JpegError ("DC Table Not Defined") ;

  return ;
}

//
//  Description:
//
//    Sequential and Progressive
//
//    This function is called before processing a scan. It ensures that the
//    Quantization table used by the component has actually been defined.
//
void JpegDecoderComponent::checkQuantizationTable ()
{
  if (quantization_table == 0)
    throw JpegDecoder::JpegError ("INTERNAL ERROR - Quantization Table Not Assigned") ;

  if (! quantization_table->tableDefined ())
    throw JpegDecoder::JpegBadStream ("Quantization Table Not Defined") ;

  return ;
}

//
//  Description:
//
//    This function decodes a data unit in a sequential scan.
//
//  Parameters:
//    decoder: The decoder that owns this component
//    mcurow, mcucol:  The row and column for this data unit.
//
void JpegDecoderComponent::decodeSequential (JpegInputStream &inputstream,
                                             unsigned int mcurow,
                                             unsigned int mcucol)
{
  JpegCoefficientBlock data ;
  memset (&data, 0, sizeof (data)) ;

  // Decode the DC differce value.
  // Section F.2.2.1
  unsigned int count ; // called T in F.2.2.1
  count = dc_table->decode (inputstream) ;
  int bits = inputstream.getBits (count) ;
  int diff = Extend (bits, count) ;

  // Create the DC value from the difference and the previous DC value.
  int dc = diff + last_dc_value ;
  last_dc_value = dc ;
  data [0] = dc ;

  // Decode the AC coefficients.
  // Section F.2.2.2 Figure F.13
  for (unsigned int kk = 1 ; kk < JPEGSAMPLESIZE ; ++ kk)
  {
    UBYTE2 rs = ac_table->decode (inputstream) ;
    UBYTE2 ssss = (UBYTE2) (rs & 0xF) ;
    UBYTE2 rrrr = (UBYTE2) (rs >> 0x4) ;

    if (ssss == 0)
    {
      // ssss is zero then rrrr should either be 15 or zero according to
      // Figure F.1. 0 means that the rest of the coefficients are zero
      // while 15 means the next 16 coefficients are zero. We are not checking
      // for other values because Figure F.13 shows values other than 15
      // as being treated as zero.
      if (rrrr  != 15)
        break ;
      kk += 15 ; // Actually 16 since one more gets added by the loop.
    }
    else
    {
      // If ssss is non-zero then rrrr gives the number of zero coefficients
      // to skip.

      kk += rrrr ;
      if (kk >= JPEGSAMPLESIZE)
        throw JpegDecoder::JpegBadStream ("Coefficient Index out of Range") ;

      // Receive and extend the additional bits.
      // Section F2.2.2 Figure F.14
      int bits = inputstream.getBits (ssss) ;
      int value = Extend (bits, ssss) ;
      data [JpegZigZagInputOrder (kk)] = value ;
    }
  }
  data_units [mcurow * du_cols + mcucol].inverseDCT (data,
                                                     *quantization_table) ;
  return ;
}

//
//  Description:
//
//    This function upsamples the data for the component. Here we take
//    the values from the data_units array and copy it to the
//    upsample_data. If the horizontal or vertical sampling frequencies
//    are less than the maximum for the image then we need to
//    stretch the data during the copy.
//
//  Parameters:
//
//    usefilter: true => Upsample using triangle filter if appropriate.
//
void JpegDecoderComponent::upsampleImage (bool usefilter)
{
  const unsigned int rowwidth = du_cols * h_sampling * JPEGSAMPLEWIDTH ;
  const unsigned int columns = du_rows * v_sampling * JPEGSAMPLEWIDTH ;
  const unsigned int imagesize = rowwidth * columns ;
  if (imagesize == 0)
    return ;  // No data for this component yet.

  if (upsample_data == 0)
    upsample_data = new JPEGSAMPLE [imagesize] ;

  if (JpegDecoder::getVerbose (JpegDecoder::PostDctTrace))
  {
    cout << "{ Post DCT Component " << endl ;
    for (unsigned int ii = 0, du = 0 ; ii < du_rows ; ++ ii)
    {
      for (unsigned int jj = 0 ; jj < du_cols ; ++ jj, ++ du)
      {
        cout << ii << "," << jj << ":" << endl ;
        data_units [du].printOn (cout) ;
      }
    }
    cout << "}" << endl ;
  }

  // Simple case where component does not need to be upsampled.
  if (v_sampling == 1 && h_sampling == 1)
  {
    upSample1To1 () ;
  }
  else
  {
    // The triangle filter only workd for 2:1 and 4:1 sampling.
    if (usefilter
        && (v_sampling % 2 == 0 || v_sampling == 1)
        && (h_sampling % 2 == 0 || h_sampling == 1))
    {
      triangleFilter () ;
    }
    else
    {
      blockFilter () ;
    }
  }

  if (JpegDecoder::getVerbose (JpegDecoder::UpsampleTrace))
  {
    cout << "{ Upsample Component " << endl ;
    for (unsigned int index = 0, ii = 0 ; ii < columns ; ++ ii)
    {
      cout << dec << ii << ") " ;
      for (unsigned int jj = 0 ; jj < rowwidth ; ++ jj, ++ index)
      {
        cout << hex << static_cast<unsigned int>(upsample_data [index]) << " " ;
      }
      cout << endl ;
    }
    cout << "}" << endl ;
  }
  return ;
}

//
//  Description:
//
//    This static member function grayscale converts component
//    image data in the upsample_data array and writes it to the
//    the output image.  Actually for a grayscale conversion all
//    we do is copy.
//
//  Parameters:
//    cc:  The component
//    image:  The output image
//
void JpegDecoderComponent::convertGrayscale (JpegDecoderComponent &cc,
                                             BitmapImage &image)
{
  unsigned int rowstart = 0 ;
  BitmapImage::Pixel *pixel = &image [0] ;
  for (unsigned int ii = 0 ; ii < image.getHeight () ; ++ ii)
  {
    unsigned int offset = rowstart ;
    for (unsigned int jj = 0 ; jj < image.getWidth () ; ++ jj, ++ pixel)
    {
      pixel->red   = cc.upsample_data [offset] ;
      pixel->green = cc.upsample_data [offset] ;
      pixel->blue  = cc.upsample_data [offset] ;
      pixel->alpha = 0xFFU ;
      ++ offset ;
    }
    rowstart += cc.du_cols * cc.h_sampling * JPEGSAMPLEWIDTH ;
  }
  return ;
}

//
//  Description:
//
//    This static member function converts the upsample_data in three
//    components from YCbCr to RGB and writes it to an image.
//
//  Parameters:
//    c1: The component containing the Y data
//    c2: Ditto for Cb
//    c3: Ditto for Cr
//    image: The output image
//
void JpegDecoderComponent::convertRgb (JpegDecoderComponent &c1,
                                       JpegDecoderComponent &c2,
                                       JpegDecoderComponent &c3,
                                       BitmapImage &image)
{
  using namespace Jfif ;
  // One may wish to display an intermediate version of the image during a
  // progressive JPEG expansion. However, we cannot do a color conversion
  // until we have data from al three components.
  if (c1.upsample_data == 0
      || c2.upsample_data == 0
      || c3.upsample_data == 0)
  {
    // If we get here then do not yet have data for all components.
    return ;
  }

  // YCbCr to RGB COlor Conversion
  // We have to do this in two loops because the source row width is padded
  // to round up the MCU width.
  unsigned int rowstart = 0 ;
  BitmapImage::Pixel *pixel = &image [0] ;
  for (unsigned int ii = 0 ; ii < image.getHeight () ; ++ ii)
  {
    unsigned int offset = rowstart ;
    for (unsigned int jj = 0 ; jj < image.getWidth () ; ++jj, ++ pixel)
    {
      JPEGSAMPLE yvalue = c1.upsample_data [offset] ;
      JPEGSAMPLE cbvalue = c2.upsample_data [offset] ;
      JPEGSAMPLE crvalue = c3.upsample_data [offset] ;
      pixel->red = YCbCrToR (yvalue, cbvalue, crvalue) ;
      pixel->green = YCbCrToG (yvalue, cbvalue, crvalue) ;
      pixel->blue = YCbCrToB (yvalue, cbvalue, crvalue) ;
      pixel->alpha = 0xFFU ;
      ++ offset ;
    }
    rowstart += c1.du_cols * c1.h_sampling * JPEGSAMPLEWIDTH ;
  }

  // Color Conversion Tracing
  if (JpegDecoder::getVerbose (JpegDecoder::ColorConvertTrace))
  {
    cout << "{ RGB Conversion " << endl ;
    BitmapImage::Pixel *pixel = &image [0] ;
    for (unsigned int ii = 0 ; ii < image.getHeight () ; ++ ii)
    {
      cout << ii << ") " ;
      unsigned int offset = rowstart ;
      for (unsigned int jj = 0 ; jj < image.getWidth () ; ++jj, ++ pixel)
      {
        cout << "{"
             << hex << static_cast <unsigned int>(pixel->red) << ", "
             << hex << static_cast <unsigned int>(pixel->green) << ", "
             << hex << static_cast <unsigned int>(pixel->blue) << "} " ;
      }
      cout << endl;
    }
    cout << "}" << endl ;
  }
  return ;
}

//
//  Description:
//
//    Progressive Only
//
//    This function decodes the DC coefficient for a data unit in the first
//    DC scan for the component.
//
//    According to G.2 "In order to avoid repetition, detail flow diagrams
//    of progressive decoder operation are not included. Decoder operation is
//    defined by reversing the function of each stop described in the encoder
//    flow charts, and performing the steps in reverse order."
//
//  Parameters:
//    decoder:  The JPEG decoder
//    row:  The data unit row
//    col:  The data unit column
//    ssa:  Successive Approximation
//
void JpegDecoderComponent::decodeDcFirst (JpegInputStream &inputstream,
                                          unsigned int row,
                                          unsigned int col,
                                          unsigned int ssa)
{
  // We decode the first DC coeffient that same way as in a sequential
  // scan except for the point transform according to G.1.2.1

  // Section F.2.2.1
  unsigned int count ; // called T in F.2.2.1
  count = dc_table->decode (inputstream) ;
  int bits = inputstream.getBits (count) ;
  int diff = Extend (bits, count) ;
  int value = diff + last_dc_value ;
  last_dc_value = value ;
  coefficient_blocks [row * du_cols + col][0] = (value << ssa) ;
  return ;
}

//
//  Description:
//
//    Progressive Only
//    
//    This function decodes the DC coefficient for a data unit in refining
//    DC scans for the component.
//
//    According to G.2 "In order to avoid repetition, detail flow diagrams
//    of progressive decoder operation are not included. Decoder operation is
//    defined by reversing the function of each stop described in the encoder
//    flow charts, and performing the steps in reverse order."
//
//  Parameters:
//    decoder:  The JPEG decoder
//    row:  The data unit row
//    col:  The data unit column
//    ssa:  Successive Approximation
//
void JpegDecoderComponent::decodeDcRefine (JpegInputStream &inputstream,
                                           unsigned int row,
                                           unsigned int col,
                                           unsigned int ssa)
{
  // Reversing G.1.2.1
  if (inputstream.nextBit() != 0)
  {
    coefficient_blocks [row * du_cols + col][0] |= (1 << ssa) ;
  }
  return ;
}

//
//  Description:
//
//    Progressive Only
//
//    This function decodes the AC coefficients for a data unit in the first
//    AC scans for a spectral range within the component.
//
//    According to G.2 "In order to avoid repetition, detail flow diagrams
//    of progressive decoder operation are not included. Decoder operation is
//    defined by reversing the function of each stop described in the encoder
//    flow charts, and performing the steps in reverse order."
//
//    This function comes from reversing the steps in Figures G.3-G.5.
//
//  Parameters:
//    decoder:  The JPEG decoder
//    row:  The data unit row
//    col:  The data unit column
//    sss:  Spectral Selection Start
//    sse:  Spectral Selection End
//    ssa:  Successive Approximation
//
void JpegDecoderComponent::decodeAcFirst (JpegInputStream &inputstream,
                                          unsigned int row,
                                          unsigned int col,
                                          unsigned int sss,
                                          unsigned int sse,
                                          unsigned int ssa)
{
  JpegCoefficientBlock &data =
  				coefficient_blocks [row * du_cols + col] ;
  if (eob_run > 0)
  {
    // If a previous call created a nonzero EOB run then we decrement the
    // counter and return.
    -- eob_run ;
  }
  else
  {
    for (unsigned int kk = sss ; kk <= sse ; )
    {
      // Decode the next value in the input stream.
      UBYTE2 rs = ac_table->decode (inputstream) ;
      UBYTE1 ssss = (UBYTE1) (rs & 0xF) ;
      UBYTE1 rrrr = (UBYTE1) (rs >> 0x4) ;

      if (ssss == 0)
      {
        if (rrrr == 15)
        {
          // A zero value ssss with rrrr == 15 means to skip
          // 16 zero coefficients.
            kk += 16 ;
        }
        else
        {
          // A zero value ssss with rrrr != 15 means to create
          // End of Band run.

          // The EOB run includes the current block. This is why we
          // do no processing for rrrr = 0 and substract one when
          // rrrrr != 0.
          if (rrrr != 0)
          {
            int bits = inputstream.getBits (rrrr) ;
            eob_run = (1 << rrrr) + bits - 1 ;
          }
          break ;
        }
      }
      else
      {
        // When ssss != 0, rrrr gives the number of zero elements to skip
        // before the next non-zero coefficient.
        kk += rrrr ;
        if (kk >= JPEGSAMPLESIZE)
          throw JpegDecoder::JpegBadStream ("Data out of range") ;

        // Extend the value and store.
        int bits = inputstream.getBits (ssss) ;
        int value = Extend (bits, ssss) ;
        data[JpegZigZagInputOrder (kk)] = (value << ssa) ;
        ++ kk ;
      }
    }
  }
  return ;
}

//
//  Description:
//
//    Progressive Only
//
//    This function decodes the AC coefficients for a data unit in the
//    refining AC scans for a spectral range within the component.
//
//    According to G.2 "In order to avoid repetition, detail flow diagrams
//    of progressive decoder operation are not included. Decoder operation is
//    defined by reversing the function of each stop described in the encoder
//    flow charts, and performing the steps in reverse order."
//
//    Section G.1.2.3 defines how to encode refining scans for AC
//    coefficients. Unfortunately this section is vague and
//    undecipherable. Reversing an undecipherable process results
//    in something unimaginable. This is a "best-guess" interpretation
//    that seems to work.
//
//    The basic process at work is that zero counts do not include nonzero
//    values. Whenever we skip a value due to zero count or End of Band runs
//    we have to read one bit to refine each non-zero value we skip. The
//    process is ugly and it means that data is encoding out of order.
//
//  Parameters:
//    decoder:  The JPEG decoder
//    row:  The data unit row
//    col:  The data unit column
//    sss:  Spectral Selection Start
//    sse:  Spectral Selection End
//    ssa:  Successive Approximation
//                                                            
void JpegDecoderComponent::decodeAcRefine (JpegInputStream &inputstream,
                                           unsigned int row,
                                           unsigned int col,
                                           unsigned int sss,
                                           unsigned int sse,
                                           unsigned int ssa)
{
  JpegCoefficientBlock &data =
  				coefficient_blocks [row * du_cols + col] ;
  // kk is incremented within the loop.
  for (unsigned int kk = sss ; kk <= sse ;)
  {
    if (eob_run != 0)
    {
      // An EOB run has caused us to skip entire data units. We need
      // to refine any previously non-zero coefficients.
      // Notice that we do not initialize kk here. We could be using
      // an EOB run to skip all the remaining coefficients in the current
      // one.

      for ( ; kk <= sse ; ++ kk)
      {
        if (data [JpegZigZagInputOrder (kk)] != 0)
        {
          RefineAcCoefficient (inputstream, 
                               ssa,
                               data [JpegZigZagInputOrder (kk)]) ;
        }
      }
      -- eob_run ;
    }
    else
    {
      UBYTE2 rs = ac_table->decode (inputstream) ;
      UBYTE1 ssss = (UBYTE1) (rs & 0xF) ;
      UBYTE1 rrrr = (UBYTE1) (rs >> 0x4) ;

      if (ssss == 0)
      {
        // ssss == 0 means that we either have an EOB run or we need to
        // 16 non-zero coefficients.

        if (rrrr == 15)
        {
          // ssss == 0 and rrrr == 15 => Skip over 16 zero coefficients
          for (unsigned int ii = 0 ;
              kk <= sse  && ii < 16 ;
              ++ kk)
          {
            if (kk >  sse)
              throw JpegDecoder::JpegBadStream ("Corrupt Scan Data") ;

            if (data [JpegZigZagInputOrder (kk)] != 0)
            {
              RefineAcCoefficient (inputstream, 
                                   ssa,
                                   data [JpegZigZagInputOrder (kk)]) ;
            }
              else
            {
              ++ ii ;
            }
          }
        }
        else
        {
          // We are reading an EOB run.
          if (rrrr == 0)
          {
            eob_run = 1 ;
          }
          else
          {
            int bits = inputstream.getBits (rrrr) ;
            eob_run = (1 << rrrr) + bits ;
          }
        }
      }
      else if (ssss == 1)
      {
        // ssss == 1 means that we are creating a new non-zero
        // coefficient. rrrr gives the number of zero coefficients to
        // skip before we reach this one.
        // Save the value for the new coefficient. Unfortunately the data
        // is stored out of order.
        int newvalue = inputstream.nextBit () ;

        // Skip the zero coefficients.
        for (unsigned int zerocount = 0 ;
             kk <  JPEGSAMPLESIZE
              && (zerocount < rrrr
                  || data [JpegZigZagInputOrder (kk)] != 0) ;
             ++ kk)
        {
          if (kk >  sse)
            throw JpegDecoder::JpegBadStream ("Error in progressive scan") ;

          if (data [JpegZigZagInputOrder (kk)] != 0)
          {
            RefineAcCoefficient (inputstream, 
                                 ssa,
                                 data [JpegZigZagInputOrder (kk)]) ;
          }
          else
          {
            ++ zerocount ;
          }
        }

        if (kk >  sse)
          throw JpegDecoder::JpegBadStream ("Error in progressive scan") ;

        if (newvalue)
        {
          data [JpegZigZagInputOrder (kk)] = (1 << ssa) ;
        }
        else
        {
          data [JpegZigZagInputOrder (kk)] = (-1 << ssa) ;
        }
        ++ kk ;
      }
      else
      {
        // The value of SSSS must be zero or one. Since we add data one
        // bit at at a time data values larger than 1 make no sense.
        throw JpegDecoder::JpegBadStream ("Invalid value in input stream") ;
      }
    }
  }
  return ;
}

//
//  Description:
//
//    Progressive Only
//
//    This function performs the IDCT on all the data in
//    coefficient_blocks and stores the result in data_units.
//
//    This function gets called whenever the image data is written
//    to the image.  For a sequential image the IDCT only needs to
//    performed once no matter how often the data gets updated in the
//    image but to continuously update the progressive image data
//    an update after each scan gives new results.
//
void JpegDecoderComponent::progressiveInverseDct ()
{
  // If UpdateImage gets called before the image is completely
  // decoded the these values may be 0.
  if (data_units == 0 || coefficient_blocks == 0)
    return ;

  unsigned int limit = du_cols * du_rows ;
  for (unsigned int ii = 0 ; ii < limit ; ++ ii)
  {
    data_units [ii].inverseDCT (coefficient_blocks [ii], *quantization_table) ;
  }
  return ;
}


//
//  This function performs pseudo"upsampling" for components that don't need to
//  be upsampled. For such components we just make a straight copy of the
//  upsample data.
//
void JpegDecoderComponent::upSample1To1 ()
{
  unsigned output = 0 ;
  unsigned int startdu = 0 ;
  for (unsigned int durow = 0 ; durow < du_rows ; ++ durow)
  {
    for (unsigned int ii = 0 ; ii < JPEGSAMPLEWIDTH ; ++ ii)
    {
      unsigned int du = startdu ;
	    for (unsigned int ducol = 0 ; ducol < du_cols ; ++ ducol)
      {
        JPEGSAMPLE *dataunits = data_units [du][ii] ;
        memcpy (&upsample_data [output], dataunits, sizeof (*dataunits) * JPEGSAMPLEWIDTH) ;
        output += JPEGSAMPLEWIDTH ;
        ++ du ;
    	}
    }
    startdu += du_cols ;
  }
  return ;
}

//
//  This function upsamples a component that has a sampling frequency less
//  than the maximum for the image. In such a component, each compressed value
//  represents multiple pixels.
//
//  The function upsamples using a "block filter" which simply means the
//  compressed values are stretched across multiple pixels by duplicating them.
//
void JpegDecoderComponent::blockFilter ()
{
  unsigned output = 0 ;
  unsigned int startdu = 0 ;
  for (unsigned int durow = 0 ; durow < du_rows ; ++ durow)
  {
    for (unsigned int ii = 0 ; ii < JPEGSAMPLEWIDTH ; ++ ii)
    {
      for (unsigned int vv = 0 ; vv < v_sampling ; ++ vv)
      {
        unsigned int du = startdu ;
  	    for (unsigned int ducol = 0 ; ducol < du_cols ; ++ ducol)
        {
          unsigned int jj ;
          JPEGSAMPLE *dataunits = data_units [du][ii] ;
           for (jj = 0 ; jj < h_sampling ; ++ jj)
           {
             upsample_data [output] = dataunits [0] ; ++ output ;
           }
           for (jj = 0 ; jj < h_sampling ; ++ jj)
           {
             upsample_data [output] = dataunits [1] ; ++ output ;
           }
           for (jj = 0 ; jj < h_sampling ; ++ jj)
           {
             upsample_data [output] = dataunits [2] ; ++ output ;
           }
           for (jj = 0 ; jj < h_sampling ; ++ jj)
           {
             upsample_data [output] = dataunits [3] ; ++ output ;
           }
           for (jj = 0 ; jj < h_sampling ; ++ jj)
           {
             upsample_data [output] = dataunits [4] ; ++ output ;
           }
           for (jj = 0 ; jj < h_sampling ; ++ jj)
           {
             upsample_data [output] = dataunits [5] ; ++ output ;
           }
           for (jj = 0 ; jj < h_sampling ; ++ jj)
           {
             upsample_data [output] = dataunits [6] ; ++ output ;
           }
           for (jj = 0 ; jj < h_sampling ; ++ jj)
           {
             upsample_data [output] = dataunits [7] ; ++ output ;
           }
           ++ du ;
        }
      }
    }
    startdu += du_cols ;
  }
  return ;
}

//
//  Description:
//
//    If the width of the image is such that the image does not span a complete
//    data unit, this function copies the rightmost column to the next column
//    in the last data units of each data unit row.
//
//
void JpegDecoderComponent::extendRightColumn ()
{
  if (image_width % (h_sampling * JPEGSAMPLEWIDTH) != 0)
  {
    unsigned int col = (image_width % (h_sampling * JPEGSAMPLEWIDTH) + h_sampling - 1) / h_sampling ;

    for (unsigned int durow = 0 ; durow < du_rows ; ++ durow)
    {
      for (unsigned int row = 0 ; row < JPEGSAMPLEWIDTH ; ++ row)
      {
        data_units [du_cols * durow - 1][row][col] =
         data_units [du_cols * durow - 1][row][col - 1] ;
      }
    }
    // Handle the lower right corner.
    if (image_height % (v_sampling * JPEGSAMPLEWIDTH) != 0)
    {
      unsigned int row = (image_height % (v_sampling * JPEGSAMPLEWIDTH) + v_sampling - 1) / v_sampling ; ;
      data_units [du_cols * du_rows - 1][row][col] =
        data_units [du_cols * du_rows - 1][row - 1][col - 1] ;
    }

  }
  return ;
}

//
//  Description:
//
//    If the image height is such that it does not span an integral number
//    of data unit rows, this function copies the last row of the image to
//    the next row.
//
void JpegDecoderComponent::extendBottomRow ()
{
  // If the image height does not fill the data unit height, extend the last row.
  if (image_height % (v_sampling * JPEGSAMPLEWIDTH) != 0)
  {
    unsigned int row = (image_height % (v_sampling * JPEGSAMPLEWIDTH) + v_sampling - 1) / v_sampling ;

    for (unsigned int du = du_cols * (du_rows - 1) ;
         du < du_cols * du_rows ;
         ++ du)
    {
      for (unsigned int col = 0 ; col < JPEGSAMPLEWIDTH ; ++ col)
      {
        data_units [du][row][col] = data_units [du][row - 1][col] ;
      }
    }
  }
  return ;
}


void JpegDecoderComponent::triangleFilter ()
{
  ASSERT (h_sampling % 2 == 0 && v_sampling % 2 == 0) ;

  const unsigned int rowwidth = du_cols * h_sampling * JPEGSAMPLEWIDTH ;
  const unsigned int LAST = JPEGSAMPLEWIDTH - 1 ;

  extendRightColumn () ;
  extendBottomRow () ;


  // These are dummy data units that are used to fill in round the edges.
  JpegDecoderDataUnit upperleft ;
  JpegDecoderDataUnit uppercenter ;
  JpegDecoderDataUnit upperright ;
  JpegDecoderDataUnit left ;
  JpegDecoderDataUnit right ;
  JpegDecoderDataUnit lowerleft ;
  JpegDecoderDataUnit lowercenter ;
  JpegDecoderDataUnit lowerright ;

  if (du_rows == 1 && du_cols == 1)
  {
    // Special case where the component has just one data unit.
    FillEdges (data_units [0],
               upperleft, uppercenter, upperright,
               left, right,
               lowerleft, lowercenter, lowerright) ;
    FilterDataUnit (upsample_data, rowwidth,
                    h_sampling, v_sampling,
                    upperleft, uppercenter, upperright,
                    left, data_units [0], right,
                    lowerleft, lowercenter, lowerright) ;
  }
  else if (du_rows == 1)
  {
    // Special case where the component has just one row of data units.
    unsigned int offset = 0 ;
    FillEdges (data_units [0],
           upperleft, uppercenter, upperright,
           left, right,
           lowerleft, lowercenter, lowerright) ;
    // Except in the case of a component with one data unit,
    // FillEdges makes a mistake with at least two edge positions.
    upperright [LAST][0] = data_units [1][0][0] ;
    lowerright [0][0] = data_units [1][LAST][0] ;

    FilterDataUnit (&upsample_data [offset], rowwidth,
                    h_sampling, v_sampling,
                    upperleft, uppercenter, upperright,
                    left, data_units [0], data_units [1],
                    lowerleft, lowercenter, lowerright) ;
    offset += h_sampling * JPEGSAMPLEWIDTH ;

    for (unsigned int du = 1 ; du < du_cols - 1 ; ++ du)
    {
      FillEdges (data_units [du],
             upperleft, uppercenter, upperright,
             left, right,
             lowerleft, lowercenter, lowerright) ;
      upperleft [LAST][LAST] = data_units [du - 1][0][LAST] ;
      lowerleft [0][LAST] = data_units [du - 1][LAST][LAST] ;
      upperright [LAST][0] = data_units [du + 1][0][0] ;
      lowerright [0][0] = data_units [du + 1][LAST][0] ;

      FilterDataUnit (&upsample_data [offset], rowwidth,
                      h_sampling, v_sampling,
                      upperleft, uppercenter, upperright,
                      data_units [du - 1], data_units [du], data_units [du + 1],
                      lowerleft, lowercenter, lowerright) ;
      offset += h_sampling * JPEGSAMPLEWIDTH ;
    }

    FillEdges (data_units [du_cols - 1],
               upperleft, uppercenter, upperright,
               left, right,
               lowerleft, lowercenter, lowerright) ;
    upperleft [LAST][LAST] = data_units [du_cols - 2][0][LAST] ;
    lowerleft [0][LAST] = data_units [du_cols - 2][LAST][LAST] ;
    FilterDataUnit (&upsample_data [offset], rowwidth,
                    h_sampling, v_sampling,
                    upperleft, uppercenter, upperright,
                    data_units [du_cols - 1], data_units [du_cols], right,
                    lowerleft, lowercenter, lowerright) ;
  }
  else if (du_cols == 1)
  {
    // Special case of a component with a single column of data units.
    unsigned int offset = 0 ;
    FillEdges (data_units [0],
               upperleft, uppercenter, upperright,
               left, right,
               lowerleft, lowercenter, lowerright) ;
    lowerleft  [0][LAST] = data_units [1][0][0] ;
    lowerright [0][0] = data_units [1][0][LAST] ;
    FilterDataUnit (&upsample_data [offset], rowwidth,
                    h_sampling, v_sampling,
                    upperleft, uppercenter, upperright,
                    left, data_units [0], right,
                    lowerleft, data_units [1], lowerright) ;
    offset += v_sampling * h_sampling * JPEGSAMPLESIZE ;

    for (unsigned int du = 1 ; du < du_rows - 1 ; ++ du)
    {
      FillEdges (data_units [du],
                 upperleft, uppercenter, upperright,
                 left, right,
                 lowerleft, lowercenter, lowerright) ;
      upperleft  [LAST][LAST] = data_units [du - 1][LAST][0] ;
      upperright [LAST][0] = data_units [du - 1][LAST][LAST] ;
      lowerleft  [0][LAST] = data_units [du + 1][0][0] ;
      lowerright [0][0] = data_units [du + 1][0][LAST] ;
      FilterDataUnit (&upsample_data [offset], rowwidth,
                      h_sampling, v_sampling,
                      upperleft, data_units [du - 1], upperright,
                      left, data_units [du], right,
                      lowerleft, data_units [du + 1], lowerright) ;
      offset += v_sampling * h_sampling * JPEGSAMPLESIZE ;
    }
    FillEdges (data_units [du_cols - 1],
               upperleft, uppercenter, upperright,
               left, right,
               lowerleft, lowercenter, lowerright) ;
    upperleft [LAST][LAST] = data_units [du_cols - 2][LAST][0] ;
    upperright [LAST][0] = data_units [du_cols - 2][LAST][LAST] ;
    FilterDataUnit (&upsample_data [offset], rowwidth,
                    h_sampling, v_sampling,
                    upperleft, data_units [du_cols - 1], upperright,
                    left, data_units [du_cols], right,
                    lowerleft, lowercenter, lowerright) ;
  }
  else
  {
    // General case. The component has at least 2 rows and two columns of data
    // units.
    const unsigned int LAST = JPEGSAMPLEWIDTH - 1 ;

    unsigned int du = 0 ;
    unsigned int offset = 0 ;

    // Upper Left Corner
    FillEdges (data_units [du],
               upperleft, uppercenter, upperright,
               left, right,
               lowerleft, lowercenter, lowerright) ;
    upperright [LAST][0] = data_units [du + 1][0][0] ;
    lowerleft [0][LAST] = data_units [du + du_cols][0][0] ;

    FilterDataUnit (&upsample_data [offset], rowwidth,
                    h_sampling, v_sampling,
                    upperleft, uppercenter, upperright,
                    left, data_units [du], data_units [du + 1],
                    lowerleft, data_units [du + du_cols], data_units [du + du_cols + 1]) ;
    ++ du ;
    offset += h_sampling * JPEGSAMPLEWIDTH ;

    // Top Row
    for (unsigned int ii = 1 ; ii < du_cols - 1 ; ++ ii)
    {
      FillEdges (data_units [du],
                 upperleft, uppercenter, upperright,
                 left, right,
                 lowerleft, lowercenter, lowerright) ;
      upperleft [LAST][LAST] = data_units [du - 1][0][LAST] ;
      upperright [LAST][0] = data_units [du + 1][0][0] ;

      FilterDataUnit (&upsample_data [offset], rowwidth,
                      h_sampling, v_sampling,
                      upperleft, uppercenter, upperright,
                      data_units [du - 1], data_units [du], data_units [du + 1],
                      data_units [du + du_cols  - 1], data_units [du + du_cols], data_units [du + du_cols + 1]) ;
      ++ du ;
      offset += h_sampling * JPEGSAMPLEWIDTH ;
    }

    // Upper Right
    FillEdges (data_units [du],
               upperleft, uppercenter, upperright,
               left, right,
               lowerleft, lowercenter, lowerright) ;
    upperleft [LAST][LAST] = data_units [du - 1][0][LAST] ;
    lowerright [0][0] = data_units [du + du_cols][0][LAST] ;
    FilterDataUnit (&upsample_data [offset], rowwidth,
                    h_sampling, v_sampling,
                    upperleft, uppercenter, upperright,
                    data_units [du - 1], data_units [du], right,
                    data_units [du + du_cols  - 1], data_units [du + du_cols], lowerright) ;
    ++ du ;
    offset += h_sampling * JPEGSAMPLEWIDTH ;

    ASSERT (offset == rowwidth) ;
    offset += rowwidth * (JPEGSAMPLEWIDTH * v_sampling - 1) ;

    for (unsigned int durows = 1 ;  durows < du_rows - 1 ; ++ durows)
    {
      ASSERT (offset == rowwidth * v_sampling * durows * JPEGSAMPLEWIDTH) ;
      // Left Edge
      FillEdges (data_units [du],
                 upperleft, uppercenter, upperright,
                 left, right,
                 lowerleft, lowercenter, lowerright) ;
      upperleft [LAST][LAST] = data_units [du - du_cols][LAST][0] ;
      lowerleft [0][LAST] = data_units [du + du_cols][0][0] ;
      FilterDataUnit (&upsample_data [offset], rowwidth,
                      h_sampling, v_sampling,
                      upperleft, data_units [du - du_cols], data_units [du - du_cols + 1],
                      left, data_units [du], data_units [du + 1],
                      lowerleft, data_units [du + du_cols], data_units [du + du_cols + 1]) ;
      ++ du ;
      offset += h_sampling * JPEGSAMPLEWIDTH ;
      for (unsigned int ducols = 1 ; ducols < du_cols - 1 ; ++ ducols)
      {
        FilterDataUnit (&upsample_data [offset], rowwidth,
                        h_sampling, v_sampling,
                        data_units [du - du_cols - 1], data_units [du - du_cols], data_units [du - du_cols + 1],
                        data_units [du - 1], data_units [du], data_units [du + 1],
                        data_units [du + du_cols - 1], data_units [du + du_cols], data_units [du + du_cols + 1]) ;
        ++ du ;
        offset += h_sampling * JPEGSAMPLEWIDTH ;
      }
      // Right Edge
      FillEdges (data_units [du],
                 upperleft, uppercenter, upperright,
                 left, right,
                 lowerleft, lowercenter, lowerright) ;
      upperright [LAST][0] = data_units [du - du_cols][LAST][LAST] ;
      lowerright [0][0] = data_units [du + du_cols][0][LAST] ;
      FilterDataUnit (&upsample_data [offset], rowwidth,
                      h_sampling, v_sampling,
                      data_units [du - du_cols - 1], data_units [du - du_cols], upperright,
                      data_units [du - 1], data_units [du], right,
                      data_units [du + du_cols - 1], data_units [du + du_cols], lowerright) ;
      ++ du ;
      offset += h_sampling * JPEGSAMPLEWIDTH ;
      offset += rowwidth * (JPEGSAMPLEWIDTH * v_sampling - 1) ;
      ASSERT (offset % JPEGSAMPLEWIDTH == 0 && offset % rowwidth == 0) ;
    }

    // Bottom Left Corner
    FillEdges (data_units [du],
               upperleft, uppercenter, upperright,
               left, right,
               lowerleft, lowercenter, lowerright) ;
    upperleft [LAST][LAST] = data_units [du - du_cols][LAST][0] ;
    lowerright [0][0] = data_units [du + 1][0][0] ;
    FilterDataUnit (&upsample_data [offset], rowwidth,
                    h_sampling, v_sampling,
                    upperleft, data_units [du - du_cols], data_units [du - du_cols + 1],
                    left, data_units [du], data_units [du + 1],
                    lowerleft, lowercenter, lowerright) ;
    ++ du ;
    offset += h_sampling * JPEGSAMPLEWIDTH ;
BILLSELLSPOOP
    for (unsigned int ii = 1 ; ii < du_cols - 1 ; ++ ii)
    {
      FillEdges (data_units [du],
                 upperleft, uppercenter, upperright,
                 left, right,
                 lowerleft, lowercenter, lowerright) ;
      lowerleft [0][LAST] = data_units [du - 1][LAST][LAST] ;
      lowerright [0][0] = data_units [du + 1][0][0] ;
      FilterDataUnit (&upsample_data [offset], rowwidth,
                      h_sampling, v_sampling,
                      data_units [du - du_cols - 1], data_units [du - du_cols], data_units [du - du_cols + 1],
                      data_units [du - 1], data_units [du], data_units [du + 1],
                      lowerleft, lowercenter, lowerright) ;
      ++ du ;
      offset += h_sampling * JPEGSAMPLEWIDTH ;
    }
ENDBILLSELLSPOOP
    FillEdges (data_units [du],
               upperleft, uppercenter, upperright,
               left, right,
               lowerleft, lowercenter, lowerright) ;
    lowerleft [0][LAST] = data_units [du - 1][LAST][LAST] ;
    upperright [LAST][0] = data_units [du - du_cols][LAST][LAST] ;
    FilterDataUnit (&upsample_data [offset], rowwidth,
                    h_sampling, v_sampling,
                    data_units [du - du_cols - 1], data_units [du - du_cols], upperright,
                    data_units [du - 1], data_units [du], right,
                    lowerleft, lowercenter, lowerright) ;
    ++ du ;
    offset += h_sampling * JPEGSAMPLEWIDTH ;
  }
  return ;
}


} // End Namespace ColosseumPrivate
