unit jpegdecodercomponent;

interface

Uses
  jpeghuffmandecoder, jpegdecoderdataunit, jpgpvt, bitmapimage,
  jpeginputstream ;

Type

  TJpegDecoderComponent = Class
    Private
      // Jfif/Frame component ID
      component_id : Cardinal ; // Could Use type here

      // Sampling Frequencies
      horizontal_frequency : JPEGSAMPLINGFREQUENCY ;
      vertical_frequency : JPEGSAMPLINGFREQUENCY;

      // These values are the numnber of samples to take for each data
      // point. They come from the sampling frequencies and the maximum
      // sampling frequencies of all the components in the image.
      // sampling frequencies of all the components in the image.
      v_sampling : Cardinal ;
      h_sampling : Cardinal ;

      // Last encoded DC value.
      last_dc_value : Integer ;

      // Entropy tables used by the component.
      ac_table, dc_table : TJpegHuffmanDecoder ;

      // Quantization table used by the component
      quantization_table : TJpegDecoderQuantizationTable ;

      // End of band Run - Progressive Specific
      eob_run : Cardinal ;

      // Non-interleaved dimensions.
      noninterleaved_rows, noninterleaved_cols : Cardinal ;

      du_rows, du_cols : Cardinal ;
      data_units : Array of DecoderDataUnit ;
      upsample_data : Array of JPEGSAMPLE ;
      coefficient_blocks : Array of JpegCoefficientBlock ;
      image_height, image_width : Cardinal ;

      Procedure upSample1To1 ;
      Procedure blockFilterImage ;
      Procedure triangleFilterImage ;

    Public
      Constructor Create ;

      // We have made the color conversions static because RGB
      // conversion requires the data from three components.
      // Grayscale conversion is static strictly for consistency
      // with RGB.
      Class Procedure convertRgb (c1, c2, c3 : TJpegDecoderComponent ;
                                  image : TBitmapImage) ;
      Class Procedure convertGrayscale (cc : TJpegDecoderComponent ;
                                        image : TBitmapImage) ;

      Property horizontalFrequency : JPEGSAMPLINGFREQUENCY
                                   read horizontal_frequency
                                   write horizontal_frequency ;
      Property verticalFrequency : JPEGSAMPLINGFREQUENCY
                                   read vertical_frequency
                                   write vertical_frequency ;

      Procedure setQuantizationTable (table : TJpegDecoderQuantizationTable) ;
      Procedure allocateComponentBuffers (width, height : Cardinal ;
                                          maxhoriz, maxvert : JPEGSAMPLINGFREQUENCY ;
                                          isprogressive : Boolean) ;
      Procedure freeComponentBuffers () ;
      Procedure setHuffmanTables (dc, ac : TJpegHuffmanDecoder) ;
      Procedure upsampleImage (usefilter : Boolean) ;

      Procedure checkAcTable ;
      Procedure checkDcTable ;
      Procedure checkQuantizationTable ;

      Procedure decodeSequential (inputstream : TJpegInputStream ; mcurow, mcucol : Cardinal) ;
      Property noninterleavedRows : Cardinal Read noninterleaved_rows ;
      Property noninterleavedCols : Cardinal Read noninterleaved_cols ;
      Procedure resetDcDifference ;

      Procedure decodeDcFirst (inputstream : TJpegInputStream ;
                                row, col, ssa : Cardinal) ;
      Procedure decodeDcRefine (inputstream : TJpegInputStream ;
                                row, col, ssa : Cardinal) ;

      Procedure decodeAcFirst (inputstream : TJpegInputStream ;
                                row, col, sss, sse, ssa : Cardinal) ;

      Procedure decodeAcRefine (inputstream : TJpegInputStream ;
                                row, col, sss, sse, ssa : Cardinal) ;

      Procedure progressiveInverseDct ;
  End ;


implementation

uses jfif, jpegdecoder ;

Procedure RefineAcCoefficient (inputstream : TJpegInputStream ;
                              ssa : Cardinal ;
                              var value : JpegCoefficient) ;
  Begin
  // Section G.1.2.3
  if (value > 0) Then
    Begin
    if (inputstream.nextBit <> 0) Then
      Inc (value, (1 Shl ssa)) ;
    End
  else if (value < 0) Then
    Begin
    if (inputstream.nextBit <> 0) Then
      inc (value, (-1 Shl ssa)) ;
    End ;
  End ;


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

Function Extend (vv, tt : Integer) : Integer ;
  Var
    vt : Integer ;
  Begin
  // 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.
  vt := 1 Shl (tt - 1) ;
  if (vv < vt) Then
    Begin
    vt := (-1 Shl tt) + 1 ;
    Result := vv + vt ;
    End
  else
    Begin
    Result := vv ;
    End ;
  End ;

//
//  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.
//
//
Function TriangleFilter (xpos, ypos : Integer ;
                        ul, ur, ll, lr : JPEGSAMPLE ;
                        hperiod, vperiod : Cardinal) : JPEGSAMPLE ;
  Begin
  result :=  ((ul * (2 * hperiod - xpos) + ur * xpos) * (2 * vperiod - ypos)
                + (ll * (2 * hperiod - xpos) + lr * xpos) * ypos
                + 4 * hperiod * vperiod - 1)  Div (4 * hperiod * vperiod) ;
  End ;



    
//
//  Description:
//
//    Class default constructor
//
Constructor TJpegDecoderComponent.Create ;
  Begin
  Inherited Create ;
  component_id := 0 ;
  horizontal_frequency := 1 ;
  vertical_frequency := 1 ;
  v_sampling := 0 ;
  h_sampling := 0 ;
  last_dc_value := 0 ;
  ac_table := Nil ;
  dc_table := Nil ;
  quantization_table := Nil ;
  eob_run := 0 ;
  noninterleaved_rows := 0 ;
  noninterleaved_cols := 0 ;
  End ;


//
//  Description:
//
//    This function associates a quantization table with the component.
//
//  Parameters:
//    table:  The quantization table
//
Procedure TJpegDecoderComponent.setQuantizationTable (table : TJpegDecoderQuantizationTable) ;
  Begin
  quantization_table := table ;
  End ;

//
//  Description:
//
//    This function determines the dimensions for the component and allocates
//    the storage to hold the component's data.
//
//  Parameters:
//
//    width : Image width in pixels
//    height : Image height in pixels
//    maxhoriz : Maximum horizontal sampling frequency for all components.
//    maxvert : Maximum vertical sampling frequency for all components.
//
Procedure TJpegDecoderComponent.allocateComponentBuffers (width, height : Cardinal ;
                                                          maxhoriz, maxvert : JPEGSAMPLINGFREQUENCY ;
                                                          isprogressive : Boolean) ;
  Begin
  if (Length (data_units) = 0) Then
    Begin
    image_height := height ;
    image_width := width ;

    // Determine sampling for the component. This is the amount of
    // stretching needed for the component.
    v_sampling := maxvert Div vertical_frequency ;
    h_sampling := maxhoriz Div horizontal_frequency ;

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

    du_rows := McusRequired (image_height, maxvert) * vertical_frequency ;
    du_cols := McusRequired (image_width , maxhoriz) * horizontal_frequency ;

    SetLength (data_units, du_rows * du_cols) ;
    End ;

  if isprogressive And (Length (coefficient_blocks) = 0) Then
    Begin
    SetLength (coefficient_blocks, du_rows * du_cols) ;
    End ;
  End ;

//
//  Description:
//
//    This function frees the memory allocated by the component
//    during the decompression process.
//
Procedure TJpegDecoderComponent.freeComponentBuffers ;
  Begin
  data_units := Nil ;
  coefficient_blocks := Nil ;
  upsample_data := Nil ;
  End ;

//
//  Description:
//
//    This function asigned Huffman tables to the component.
//
//  Parameters:
//    dc:  The DC Huffman table
//    ac:  The AC Huffman table
//
Procedure TJpegDecoderComponent.setHuffmanTables (dc, ac : TJpegHuffmanDecoder) ;
  Begin
  dc_table := dc ;
  ac_table := ac ;
  End ;

//
//  Description:
//
//    This function ensures that this component has a defined
//    AC table assigned to it. If not, it throws an exception.
//
Procedure TJpegDecoderComponent.checkAcTable ;
  Begin
  // If this occurs then we have a programming error.
  if (Not ac_table.tableDefined) Then
    Raise EJpegBadStream.Create ('AC Table Not Defined') ;
  End ;

//
//  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.
//
Procedure TJpegDecoderComponent.checkDcTable ;
  Begin
  // This condition could be caused by a corrupt JPEG stream.
  if (Not dc_table.tableDefined) Then
    Raise EJpegBadStream.Create ('DC Table Not Defined') ;
  End ;

//
//  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.
//
Procedure TJpegDecoderComponent.checkQuantizationTable ;
  Begin
  if (quantization_table = Nil) Then
    Raise EJpegError.Create ('INTERNAL ERROR - Quantization Table Not Assigned') ;
  if (Not quantization_table.tableDefined) Then
    Raise EJpegBadStream.Create ('Quantization Table Not Defined') ;
  End ;

//
//  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.
//
Procedure TJpegDecoderComponent.decodeSequential (inputstream : TJpegInputStream ;
                                                  mcurow, mcucol : Cardinal) ;
  var
    data : JpegCoefficientBlock ;
    ii, kk : Cardinal ;
    count : Cardinal ; // called T in F.2.2.1
    bits, value, diff : Integer ;
    dc : Integer ;
    rs, ssss, rrrr : Word ;
  Begin
  for ii := low (data) to high (data) Do
    data [ii] := 0 ;

  // Decode the DC differce value.
  // Section F.2.2.1
  count := dc_table.decode (inputstream) ;
  bits := inputstream.getBits (count) ;
  diff := Extend (bits, count) ;

  // Create the DC value from the difference and the previous DC value.
  dc := diff + last_dc_value ;
  last_dc_value := dc ;
  data [Low (COEFFICIENTINDEX)] := dc ;

  // Decode the AC coefficients.
  // Section F.2.2.2 Figure F.13
  kk := Low (COEFFICIENTINDEX) + 1 ;
  while kk < High (COEFFICIENTINDEX) Do
    Begin
    rs := ac_table.decode (inputstream) ;
    ssss := (rs And $F) ;
    rrrr := (rs Shr $4) ;

    if (ssss = 0) Then
      Begin
      // 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) Then
        Inc (kk, JPEGSAMPLESIZE) ;
      Inc (kk, 15) ; // Actually 16 since one more gets added by the loop.
      End
    else
      Begin
      // If ssss is non-zero then rrrr gives the number of zero coefficients
      // to skip.

      Inc (kk, rrrr) ;

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

//
//  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.
//
Procedure TJpegDecoderComponent.upsampleImage (usefilter : Boolean) ;
  var
    rowwidth, imagesize : Cardinal ;
  Begin
  rowwidth := du_cols * h_sampling * JPEGSAMPLEWIDTH ;
  imagesize := rowwidth * du_rows * v_sampling * JPEGSAMPLEWIDTH ;

  if (imagesize = 0) Then
    Exit ;  // No data for this component yet.

  if Length (upsample_data) = 0 Then
    SetLength (upsample_data, imagesize) ;

  // Simple case where component does not need to be upsampled.
  if (v_sampling = 1) And (h_sampling = 1) Then
    upSample1To1
  else
    Begin
    // The triangle filter only workd for 2:1 and 4:1 sampling.
    if usefilter
        And ((v_sampling Mod 2 = 0) Or (v_sampling = 1))
        And ((h_sampling Mod 2 = 0) Or (h_sampling = 1)) Then
      triangleFilterImage
    else
      blockFilterImage ;
    End ;
  End ;

//
//  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
//
Class Procedure TJpegDecoderComponent.convertGrayscale (cc : TJpegDecoderComponent ;
                                                        image : TBitmapImage) ;
  Var
    ii, jj, pixel : Integer ;
    rowstart, offset : Cardinal ;
  Begin
  rowstart := 0 ;
  pixel := 0 ;
  For ii := 1 To image.Height Do
    Begin
    offset := rowstart ;
    for jj := 1 To image.Width Do
      Begin
      Image.Pixels [pixel].red   := cc.upsample_data [offset] ;
      Image.Pixels [pixel].green := cc.upsample_data [offset] ;
      Image.Pixels [pixel].blue  := cc.upsample_data [offset] ;
      Image.Pixels [pixel].alpha := $FF ;
      Inc (offset) ;
      Inc (pixel) ;
      End ;
    Inc (rowstart, cc.du_cols * cc.h_sampling * JPEGSAMPLEWIDTH) ;
    End ;
  End ;

//
//  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
//
Class Procedure TJpegDecoderComponent.convertRgb (c1, c2, c3 : TJpegDecoderComponent ;
                                                 image : TBitmapImage) ;
  Var
    yvalue, cbvalue, crvalue : JPEGSAMPLE ;
    rowstart, pixel, offset : Cardinal ;
    ii, jj : Cardinal ;
  Begin
  if (Length (c1.upsample_data) = 0)
      Or (Length (c2.upsample_data) = 0)
      Or (Length (c3.upsample_data) = 0) Then
    Exit ; // If we get here then do not yet have data for all components.

  rowstart := 0 ;
  pixel := 0 ;
  for ii := 1 To image.Height Do
    Begin
    offset := rowstart ;
    for jj := 1 To image.Width Do
      Begin
      yvalue := c1.upsample_data [offset] ;
      cbvalue := c2.upsample_data [offset] ;
      crvalue := c3.upsample_data [offset] ;
      image.Pixels [pixel].red := YCbCrToR (yvalue, cbvalue, crvalue) ;
      image.Pixels [pixel].green := YCbCrToG (yvalue, cbvalue, crvalue) ;
      image.Pixels [pixel].blue := YCbCrToB (yvalue, cbvalue, crvalue) ;
      image.Pixels [pixel].alpha := $FF ;
      Inc (offset) ;
      Inc (pixel) ;
      End ;
    Inc (rowstart, c1.du_cols * c1.h_sampling * JPEGSAMPLEWIDTH) ;
    End ;
  End ;

//
//  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
//
Procedure TJpegDecoderComponent.decodeDcFirst (inputstream : TJpegInputStream ;
                                              row, col, ssa : Cardinal) ;
  Var
    count : Cardinal ; // called T in F.2.2.1
    bits, diff, value : Integer ;
  Begin
  // 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
  count := dc_table.decode (inputstream) ;
  bits := inputstream.getBits (count) ;
  diff := Extend (bits, count) ;
  value := diff + last_dc_value ;
  last_dc_value := value ;
  coefficient_blocks [row * du_cols + col][Low (COEFFICIENTINDEX)] := (value Shl ssa) ;
  End ;

//
//  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
//
Procedure TJpegDecoderComponent.decodeDcRefine (inputstream : TJpegInputStream ;
                                                row, col, ssa : Cardinal) ;
  Begin
  // Reversing G.1.2.1
  If (inputstream.nextBit <> 0) Then
    Begin
    coefficient_blocks [row * du_cols + col][Low (COEFFICIENTINDEX)] :=
      coefficient_blocks [row * du_cols + col][Low (COEFFICIENTINDEX)] Or (1 Shl ssa) ;
    End ;
  End ;

//
//  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
//
Procedure TJpegDecoderComponent.decodeAcFirst (inputstream : TJpegInputStream ;
                                               row, col, sss, sse, ssa : Cardinal) ;
  Var
    kk : Cardinal ;
    rs : Word ;
    ssss, rrrr : Byte ;
    bits, value : Integer ;
  Begin
  If eob_run > 0 Then
    Begin
    // If a previous call created a nonzero EOB run then we decrement the
    // counter and return.
    Dec (eob_run) ;
    End
  Else
    Begin
    kk := sss ;
    While kk <= sse Do
      Begin
      // Decode the next value in the input stream.
      rs := ac_table.decode (inputstream) ;
      ssss := (rs And $F) ;
      rrrr := (rs Shr $4) ;
      if ssss = 0 Then
        Begin
        If rrrr = 15 Then
          Begin
          // A zero value ssss with rrrr = 15 means to skip
          // 16 zero coefficients.
          Inc (kk, 16) ;
          End
        Else
          Begin
          // 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 Then
            Begin
            bits := inputstream.getBits (rrrr) ;
            eob_run := (1 Shl rrrr) + bits - 1 ;
            End ;
          Exit ;
          End ;
        End
      Else
        Begin
        // When ssss != 0, rrrr gives the number of zero elements to skip
        // before the next non-zero coefficient.
        Inc (kk, rrrr) ;
        // Extend the value and store.
        bits := inputstream.getBits (ssss) ;
        value := Extend (bits, ssss) ;
        coefficient_blocks [row * du_cols + col][JpegZigZagInputOrder [kk]] := (value Shl ssa) ;
        Inc (kk) ;
        End ;
      End ;
    End ;
  End ;
//
//  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
//
Procedure TJpegDecoderComponent.decodeAcRefine (inputstream : TJpegInputStream ;
                                               row, col, sss, sse, ssa : Cardinal) ;
  var
    rs : Word ;
    ssss, rrrr : Byte ;
    ii, kk : Cardinal ;
    offset : Cardinal ;
    bits : Integer ;
    newvalue : Integer ;
    zerocount : Cardinal ;
  Begin
  offset := row * du_cols + col ;
  // kk is incremented within the loop.
  kk := sss ;
  while kk <= sse Do
    Begin
    if (eob_run <> 0) Then
      Begin
      // 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.

      While kk <= sse Do
        Begin
        if (coefficient_blocks [offset][JpegZigZagInputOrder [kk]] <> 0) Then
          Begin
          RefineAcCoefficient (inputstream,
                               ssa,
                               coefficient_blocks [offset][JpegZigZagInputOrder [kk]]) ;
          End ;
        inc (kk) ;
        End ;
      Dec (eob_run) ;
      End
    else
      Begin
      rs := ac_table.decode (inputstream) ;
      ssss := (rs And $F) ;
      rrrr := (rs Shr $4) ;
      if (ssss = 0) Then
        Begin
        // ssss == 0 means that we either have an EOB run or we need to
        // 16 non-zero coefficients.

        if (rrrr = 15) Then
          Begin
          // ssss == 0 and rrrr == 15 => Skip over 16 zero coefficients
          ii := 0 ;
          while (kk <= sse) And (ii < 16) Do
            Begin
            if coefficient_blocks [offset][JpegZigZagInputOrder [kk]] <> 0 Then
              Begin
              RefineAcCoefficient (inputstream,
                                   ssa,
                                   coefficient_blocks [offset][JpegZigZagInputOrder [kk]]) ;
              End
            else
              Inc (ii) ;
            inc (kk) ;
            End ;
          End
        else
          Begin
          // We are reading an EOB run.
          if (rrrr = 0) Then
            eob_run := 1
          else
            Begin
            bits := inputstream.getBits (rrrr) ;
            eob_run := (1 Shl rrrr) + bits ;
            End
          End
        End
      else if ssss = 1 Then
        Begin
        // 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.
        newvalue := inputstream.nextBit ;

        // Skip the zero coefficients.
        zerocount := 0 ;
        while (kk < JPEGSAMPLESIZE)
               And ((zerocount < rrrr)
                    Or (coefficient_blocks [offset][JpegZigZagInputOrder [kk]] <> 0)) Do
          Begin
          if (kk >  sse) Then
            Raise EJpegBadStream.Create ('Error in progressive scan') ;

          if (coefficient_blocks [row * du_cols + col][JpegZigZagInputOrder [kk]] <> 0) Then
            Begin
            RefineAcCoefficient (inputstream,
                                 ssa,
                                 coefficient_blocks [offset][JpegZigZagInputOrder [kk]]) ;
            End
          Else
            Inc (zerocount) ;
          Inc (kk) ;
          End ;

        If (kk >  sse) Then
          Raise EJpegBadStream.Create ('Error in progressive scan') ;
        If newvalue <> 0 Then
          coefficient_blocks [offset][JpegZigZagInputOrder [kk]] := (1 Shl ssa)
        else
          coefficient_blocks [offset][JpegZigZagInputOrder [kk]] := (-1 Shl ssa) ;
        Inc (kk) ;
        End
      else
        Begin
        // 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.
        Raise EJpegBadStream.Create ('Invalid value in input stream') ;
        End ;
      End ;
    End ;
  End ;

//
//  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.
//
Procedure TJpegDecoderComponent.progressiveInverseDct ;
  var
    ii : Integer ;
  Begin
  // If UpdateImage gets called before the image is completely
  // decoded the these values may be 0.
  if (Length (data_units) = 0) And (Length (coefficient_blocks) = 0) Then
    Exit ;

  for ii := 0 To du_cols * du_rows - 1 Do
    inverseDCT (coefficient_blocks [ii], quantization_table, data_units [ii]) ;
  End ;

//  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.
Procedure TJpegDecoderComponent.upSample1To1 ;
  Var
    output : Cardinal ;
    startdu, du : Cardinal ;
    durow, ducol : Cardinal ;
    ii, jj : Integer ;
  Begin
  output := 0 ;
  startdu := 0 ;
  for durow := 1 to du_rows Do
    Begin
    for ii := Low (DATAUNITINDEX) To High (DATAUNITINDEX) Do
      Begin
      du := startdu ;
	    for ducol := 1 To du_cols Do
        begin
        for jj := Low (DATAUNITINDEX) to High (DATAUNITINDEX) do
          Begin
          upsample_data [output] := data_units [du][ii, jj] ;
          Inc (output) ;
          End ;
        Inc (du) ;
    	  End ;
      End ;
    Inc (startdu, du_cols) ;
    End ;
  End ;

//  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.
//
Procedure TJpegDecoderComponent.blockFilterImage ;
  Var
    output : Cardinal ;
    startdu, du : Cardinal ;
    durow, ducol : Cardinal ;
    ii, jj, vv : Integer ;

  Procedure FilterRow (row : Cardinal) ;
    Var
      jj : Cardinal ;
    Begin
    for jj := 1 To h_sampling Do
      Begin
      upsample_data [output] := data_units [du][jj, row] ;
      Inc (output) ;
      End ;
    End ;

  Begin
  output := 0 ;
  startdu := 0 ;
  for durow := 0 To du_rows - 1 Do
    Begin
    for ii := 1 To JPEGSAMPLEWIDTH Do
      Begin
      for vv := 1 To v_sampling Do
        Begin
        du := startdu ;
  	    for ducol := 1 To du_cols Do
          Begin
          For jj := Low (DATAUNITINDEX) To High (DATAUNITINDEX) Do
            FilterRow (jj) ;
          Inc (du) ;
          End ;
        End ;
     End ;
    Inc (startdu, du_cols) ;
    End ;
  End ;

Procedure TJpegDecoderComponent.triangleFilterImage ;
  Var
    // These are dummy data units that are used to fill in round the edges.
    upperleft, uppercenter, upperright,
    left, right,
    lowerleft, lowercenter, lowerright : DecoderDataUnit ;
    rowwidth : Cardinal ;

  //    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.
  Procedure extendRightColumn ;
    Var
      row, col, durow : Cardinal ;
    Begin
    if (image_width Mod (h_sampling * JPEGSAMPLEWIDTH) <> 0) Then
      Begin
      col := (image_width Mod (h_sampling * JPEGSAMPLEWIDTH) + h_sampling - 1) Div h_sampling + 1 ;

      for durow := 0 To du_rows - 1 Do
        Begin
        for row := Low (DATAUNITINDEX) To High (DATAUNITINDEX) Do
          Begin
          data_units [du_cols * durow][row, col] :=
              data_units [du_cols * durow][row, col - 1] ;
          End ;
        End ;
      // Handle the lower right corner.
      if (image_height Mod (v_sampling * JPEGSAMPLEWIDTH) <> 0) Then
        Begin
        row := image_height Mod JPEGSAMPLEWIDTH ;
        data_units [du_cols * du_rows - 1][row, col] :=
          data_units [du_cols * du_rows - 1][row - 1, col - 1] ;
        End ;
      End ;
    End ;
  //    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.
  Procedure extendBottomRow ;
    Var
      row : Cardinal ;
      du, Col : Cardinal ;
    Begin
    // If the image height does not fill the data unit height, extend the last row.
    if (image_height Mod (v_sampling * JPEGSAMPLEWIDTH) <> 0) Then
      Begin
      row := (image_height Mod (v_sampling * JPEGSAMPLEWIDTH) + v_sampling - 1)
                          Div v_sampling + 1 ;

      for du := du_cols * (du_rows - 1) To du_cols * du_rows - 1 Do
        Begin
        for col := 1 To JPEGSAMPLEWIDTH - 1 Do
          data_units [du][row, col] := data_units [du][row - 1, col] ;
        End ;
      End ;
    End ;

  //  Description:
  //
  //    This function triangle filters a data unit.
  //
  //  Parameters:
  //
  //    start: The index of the location in upsample_data 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.
  //
  Procedure FilterDataUnit (
                  start : Cardinal ;
                  rowwidth, hperiod, vperiod : Cardinal ;
                  upperleft, uppercenter, upperright,
                  left, center, right,
                  lowerleft, lowercenter, lowerright : DecoderDataUnit) ;
    var
      offset : Cardinal ;
      mcurow, mcucol : Cardinal ;
    //
    //  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.
    //
    Procedure FilterCorner (startx, starty : Cardinal ;
                            start : Cardinal ;
                            rowwidth : Cardinal ;
                            ul, ur, ll, lr : Integer ;
                            hperiod, vperiod : Cardinal) ;
      Var
        xx, yy : Cardinal ;
        rowoffset, offset : cardinal ;
        ii, jj : Integer ;
      Begin
      yy := starty ;
      rowoffset := 0 ;
      for ii := 1 To vperiod Div 2 Do
        Begin
        xx := startx ;
        offset := rowoffset ;
        for jj := 1 To hperiod Div 2 Do
          Begin
          upsample_data [start + offset] := TriangleFilter (xx, yy,
                                                   ul, ur,
                                                   ll, lr,
                                                   hperiod, vperiod) ;
          Inc (offset) ;
          Inc (xx, 2) ;
          End ;
        Inc (rowoffset, rowwidth) ;
        Inc (yy, 2) ;
        End ;
      End ;
    //
    //  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.
    //
    Procedure FilterTopOrBottom (starty : Cardinal ;
                                 start: Cardinal ;
                                 rowwidth : Cardinal ;
                                 ul, ur, ll, lr : Integer ;
                                 hperiod, vperiod : Cardinal) ;
      var
        offset, rowoffset : Cardinal ;
        xx, yy : Cardinal ;
        ii, jj : Cardinal ;
      Begin
      rowoffset := 0 ;
      yy := starty ;
      for ii := 1 To vperiod Div 2 Do
        Begin
        xx := 1 ;
        offset := rowoffset ;
        for jj := 1 To hperiod Do
          Begin
          upsample_data [start + offset] := TriangleFilter (xx, yy,
                                                   ul, ur,
                                                   ll, lr,
                                                   hperiod, vperiod) ;
          Inc (offset) ;
          Inc (xx, 2) ;
          End ;
        Inc (rowoffset, rowwidth) ;
        Inc (yy, 2) ;
        End ;
      End ;
    //
    //  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 first 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 right side of a data
    //            unit, this value is hperiod + 1. At the left 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.
    //
    Procedure FilterSide (startx : Cardinal ;
                          start : Cardinal ;
                          rowwidth : Cardinal ;
                          ul, ur, ll, lr : Integer ;
                          hperiod, vperiod : Cardinal) ;
      Var
        offset, rowoffset : Cardinal ;
        xx, yy : Cardinal ;
        ii, jj : Cardinal ;
      Begin
      rowoffset := 0 ;
      yy := 1 ;
      for ii := 1  To vperiod Do
        Begin
        offset := rowoffset ;
        xx := startx ;
        for jj := 1 To hperiod Div 2 Do
          Begin
          upsample_data [start + offset] := TriangleFilter (xx, yy,
                                                   ul, ur,
                                                   ll, lr,
                                                   hperiod, vperiod) ;
          Inc (offset) ;
          Inc (xx, 2) ;
          End ;
        Inc (rowoffset, rowwidth) ;
        Inc (yy, 2) ;
        End ;
      End ;

    //
    //  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.
    //
    Procedure FilterMiddle (start : Cardinal ;
                            rowwidth : Cardinal ;
                            ul, ur, ll, lr : Integer ;
                            hperiod, vperiod : Cardinal) ;
      var
        ii, jj : Cardinal ;
        xx, yy : Cardinal ;
        offset, rowoffset : Cardinal ;
      Begin
      rowoffset := 0 ;
      yy := 1 ;
      for ii := 1 To vperiod Do
        Begin
        offset := rowoffset ;
        xx := 1 ;
        for jj := 1 To hperiod Do
          Begin
          upsample_data [start + offset] := TriangleFilter (xx, yy,
                                                   ul, ur,
                                                   ll, lr,
                                                   hperiod, vperiod) ;
          Inc (offset) ;
          Inc (xx, 2) ;
          End ;
        Inc (rowoffset, rowwidth) ;
        Inc (yy, 2) ;
        End ;
      End ;

    Begin
    offset := 0 ;
    // Upper Right Corner
    FilterCorner (hperiod + 1, vperiod + 1,
                  start + offset, rowwidth,
                  upperleft [High (DATAUNITINDEX), High (DATAUNITINDEX)], uppercenter [High (DATAUNITINDEX), Low (DATAUNITINDEX)],
                  left [Low (DATAUNITINDEX), High (DATAUNITINDEX)], center [Low (DATAUNITINDEX), Low (DATAUNITINDEX)],
                  hperiod, vperiod) ;
    Inc (offset, hperiod Div 2) ;
    // Top Edges
    for mcucol := Low (DATAUNITINDEX) To High (DATAUNITINDEX) - 1 Do
      Begin
      FilterTopOrBottom (vperiod + 1,
                         start + offset, rowwidth,

                         uppercenter [High (DATAUNITINDEX), mcucol],
                         uppercenter [High (DATAUNITINDEX), mcucol + 1],
                         center [Low (DATAUNITINDEX), mcucol], center [Low (DATAUNITINDEX), mcucol + 1],
                         hperiod, vperiod) ;
      Inc (offset, hperiod) ;
      End ;
    // Upper Left Corner
    FilterCorner (1, vperiod + 1,
                  start + offset, rowwidth,
                  uppercenter [High (DATAUNITINDEX), High (DATAUNITINDEX)], upperright [High (DATAUNITINDEX), Low (DATAUNITINDEX)],
                  center [Low (DATAUNITINDEX), High (DATAUNITINDEX)], right [Low (DATAUNITINDEX), Low (DATAUNITINDEX)],
                  hperiod, vperiod) ;
    Inc (offset, (hperiod + vperiod * rowwidth) Div 2 - JPEGSAMPLEWIDTH * hperiod) ;

    for mcurow := Low (DATAUNITINDEX) To High (DATAUNITINDEX) - 1 Do
      Begin
      // Left Edge
      FilterSide (hperiod + 1,
                  start + offset, rowwidth,
                  left [mcurow, High (DATAUNITINDEX)], center [mcurow, Low (DATAUNITINDEX)],
                  left [mcurow + 1, High (DATAUNITINDEX)], center [mcurow + 1, Low (DATAUNITINDEX)],
                  hperiod, vperiod) ;
      Inc (offset, hperiod Div 2) ;

      // Middle
      for mcucol := Low (DATAUNITINDEX) To  High (DATAUNITINDEX) - 1 Do
        Begin
        FilterMiddle (start + offset, rowwidth,
                      center [mcurow, mcucol], center [mcurow, mcucol + 1],
                      center [mcurow + 1, mcucol], center [mcurow + 1, mcucol + 1],
                      hperiod, vperiod) ;
        Inc (offset, hperiod) ;
        End ;
      // Right Edge
      FilterSide (1,
                  start + offset, rowwidth,
                  center [mcurow][High (DATAUNITINDEX)], right [mcurow][1],
                  center [mcurow + 1][High (DATAUNITINDEX)], right [mcurow + 1][1],
                  hperiod, vperiod) ;

      Inc (offset, (hperiod Div 2 + vperiod * rowwidth) - JPEGSAMPLEWIDTH * hperiod) ;
      End ;

    // Bottom Left Corner
    FilterCorner (hperiod + 1, 1,
                  start + offset, rowwidth,
                  left [High (DATAUNITINDEX), High (DATAUNITINDEX)], center [High (DATAUNITINDEX), Low (DATAUNITINDEX)],
                  lowerleft [Low (DATAUNITINDEX), High (DATAUNITINDEX)], lowercenter [Low (DATAUNITINDEX), Low (DATAUNITINDEX)],
                  hperiod, vperiod) ;
    Inc (offset, hperiod Div 2) ;

    // Bottom Edge
    for mcurow := Low (DATAUNITINDEX) To High (DATAUNITINDEX) - 1 Do
      Begin
      FilterTopOrBottom (1,
                         start + offset, rowwidth,
                         center [High (DATAUNITINDEX), mcurow], center [High (DATAUNITINDEX), mcurow + 1],
                         lowercenter [Low (DATAUNITINDEX), mcurow], lowercenter [Low (DATAUNITINDEX), mcurow + 1],
                         hperiod, vperiod) ;
      Inc (offset, hperiod) ;
      End ;
    // Bottom Right Corner
    FilterCorner (1, 1,
                  start + offset, rowwidth,
                  center [High (DATAUNITINDEX), High (DATAUNITINDEX)], right [High (DATAUNITINDEX), Low (DATAUNITINDEX)],
                  lowercenter [Low (DATAUNITINDEX), High (DATAUNITINDEX)], lowerright [Low (DATAUNITINDEX), Low (DATAUNITINDEX)],
                  hperiod, vperiod) ;
    End ;


  //
  //  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.
  //
  Procedure FillEdges (center : DecoderDataUnit ;
                       var upperleft, uppercenter, upperright,
                       left, right,
                       lowerleft, lowercenter, lowerright : DecoderDataUnit) ;
    Var
      ii : Integer ;
    Begin
    upperleft [High (DATAUNITINDEX), High (DATAUNITINDEX)] := center [Low (DATAUNITINDEX), Low (DATAUNITINDEX)] ;
    lowerleft [Low (DATAUNITINDEX), High (DATAUNITINDEX)] := center [High (DATAUNITINDEX), Low (DATAUNITINDEX)] ;
    upperright [High (DATAUNITINDEX), Low (DATAUNITINDEX)] := center [Low (DATAUNITINDEX), High (DATAUNITINDEX)] ;
    lowerright [Low (DATAUNITINDEX), Low (DATAUNITINDEX)] := center [High (DATAUNITINDEX), High (DATAUNITINDEX)] ;
    for ii := Low (DATAUNITINDEX) To High (DATAUNITINDEX) Do
      Begin
      left [ii, High (DATAUNITINDEX)] := center [ii, Low (DATAUNITINDEX)] ;
      uppercenter [High (DATAUNITINDEX), ii] := center [Low (DATAUNITINDEX), ii] ;
      right [ii, Low (DATAUNITINDEX)] := center [ii, High (DATAUNITINDEX)] ;
      lowercenter [Low (DATAUNITINDEX), ii] := center [High (DATAUNITINDEX), ii] ;
      End ;
    End ;

  Procedure FilterSingleDataUnitImage ;
    Begin
    FillEdges (data_units [0],
               upperleft, uppercenter, upperright,
               left, right,
               lowerleft, lowercenter, lowerright) ;
    FilterDataUnit (upsample_data [0], rowwidth,
                    h_sampling, v_sampling,
                    upperleft, uppercenter, upperright,
                    left, data_units [0], right,
                    lowerleft, lowercenter, lowerright) ;
    End ;

  Procedure FilterSingleDataUnitRowImage ;
    var
      offset : Cardinal ;
      du : Cardinal ;
    Begin
    // Special case where the component has just one row of data units.
    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 [Low (DATAUNITINDEX), Low (DATAUNITINDEX)] := data_units [1][Low (DATAUNITINDEX), Low (DATAUNITINDEX)] ;
    lowerright [Low (DATAUNITINDEX), Low (DATAUNITINDEX)] := data_units [1][High (DATAUNITINDEX), Low (DATAUNITINDEX)] ;

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

    for du := 1 To du_cols - 2 Do
      Begin
      FillEdges (data_units [du],
             upperleft, uppercenter, upperright,
             left, right,
             lowerleft, lowercenter, lowerright) ;
      upperleft [High (DATAUNITINDEX), High (DATAUNITINDEX)] := data_units [du - 1][Low (DATAUNITINDEX), High (DATAUNITINDEX)] ;
      lowerleft [Low (DATAUNITINDEX), High (DATAUNITINDEX)] := data_units [du - 1][High (DATAUNITINDEX), High (DATAUNITINDEX)] ;
      upperright [High (DATAUNITINDEX), Low (DATAUNITINDEX)] := data_units [du + 1][Low (DATAUNITINDEX), Low (DATAUNITINDEX)] ;
      lowerright [Low (DATAUNITINDEX), Low (DATAUNITINDEX)] := data_units [du + 1][High (DATAUNITINDEX), Low (DATAUNITINDEX)] ;

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

    FillEdges (data_units [du_cols - 1],
               upperleft, uppercenter, upperright,
               left, right,
               lowerleft, lowercenter, lowerright) ;
    upperleft [High (DATAUNITINDEX), High (DATAUNITINDEX)] := data_units [du_cols - 2][Low (DATAUNITINDEX), High (DATAUNITINDEX)] ;
    lowerleft [Low (DATAUNITINDEX), High (DATAUNITINDEX)] := data_units [du_cols - 2][High (DATAUNITINDEX), High (DATAUNITINDEX)] ;
    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) ;
    End ;

  Procedure FilterSingledataUnitColumnImage ;
    var
      du : Cardinal ;
      offset : Cardinal ;
    Begin
    offset := 0 ;
    FillEdges (data_units [0],
               upperleft, uppercenter, upperright,
               left, right,
               lowerleft, lowercenter, lowerright) ;
    lowerleft  [Low (DATAUNITINDEX), High (DATAUNITINDEX)] := data_units [1][Low (DATAUNITINDEX), Low (DATAUNITINDEX)] ;
    lowerright [Low (DATAUNITINDEX), Low (DATAUNITINDEX)] := data_units [1][Low (DATAUNITINDEX), High (DATAUNITINDEX)] ;
    FilterDataUnit (offset, rowwidth,
                    h_sampling, v_sampling,
                    upperleft, uppercenter, upperright,
                    left, data_units [0], right,
                    lowerleft, data_units [1], lowerright) ;
    Inc (offset, v_sampling * h_sampling * JPEGSAMPLESIZE) ;

    for du := 1 To du_rows - 2 Do
      Begin
      FillEdges (data_units [du],
                 upperleft, uppercenter, upperright,
                 left, right,
                 lowerleft, lowercenter, lowerright) ;
      upperleft  [High (DATAUNITINDEX), High (DATAUNITINDEX)] := data_units [du - 1][High (DATAUNITINDEX), Low (DATAUNITINDEX)] ;
      upperright [High (DATAUNITINDEX), Low (DATAUNITINDEX)] := data_units [du - 1][High (DATAUNITINDEX), High (DATAUNITINDEX)] ;
      lowerleft  [Low (DATAUNITINDEX), High (DATAUNITINDEX)] := data_units [du + 1][Low (DATAUNITINDEX), Low (DATAUNITINDEX)] ;
      lowerright [Low (DATAUNITINDEX), Low (DATAUNITINDEX)] := data_units [du + 1][Low (DATAUNITINDEX), High (DATAUNITINDEX)] ;
      FilterDataUnit (offset, rowwidth,
                      h_sampling, v_sampling,
                      upperleft, data_units [du - 1], upperright,
                      left, data_units [du], right,
                      lowerleft, data_units [du + 1], lowerright) ;
      Inc (offset, v_sampling * h_sampling * JPEGSAMPLESIZE) ;
      End ;

    FillEdges (data_units [du_cols - 1],
               upperleft, uppercenter, upperright,
               left, right,
               lowerleft, lowercenter, lowerright) ;
    upperleft [High (DATAUNITINDEX), High (DATAUNITINDEX)] := data_units [du_cols - 2][High (DATAUNITINDEX), Low (DATAUNITINDEX)] ;
    upperright [High (DATAUNITINDEX), Low (DATAUNITINDEX)] := data_units [du_cols - 2][High (DATAUNITINDEX), High (DATAUNITINDEX)] ;
    FilterDataUnit (offset, rowwidth,
                    h_sampling, v_sampling,
                    upperleft, data_units [du_cols - 1], upperright,
                    left, data_units [du_cols], right,
                    lowerleft, lowercenter, lowerright) ;
    End ;

  Procedure FilterNormalImage ;
    Var
      // Source offset.
      du : Cardinal ;
      ii : Cardinal ;
      durows, ducols : Cardinal ;
      // The position to start writing the pixel data to. (Destination offset)
      offset : Cardinal ;
    Begin
    du := Low (data_units) ;
    offset := 0 ;
    // Upper Left Corner
    FillEdges (data_units [du],
               upperleft, uppercenter, upperright,
               left, right,
               lowerleft, lowercenter, lowerright) ;
    upperright [High (DATAUNITINDEX), Low (DATAUNITINDEX)] :=
        data_units [du + 1][Low (DATAUNITINDEX), Low (DATAUNITINDEX)] ;
    lowerleft [Low (DATAUNITINDEX), High (DATAUNITINDEX)] :=
        data_units [du + du_cols][Low (DATAUNITINDEX), Low (DATAUNITINDEX)] ;

    FilterDataUnit (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]) ;
    Inc (du) ;
    Inc (offset, h_sampling * JPEGSAMPLEWIDTH) ;

    // Top Row
    for ii := 1 To du_cols - 2 Do
      Begin
      FillEdges (data_units [du],
                 upperleft, uppercenter, upperright,
                 left, right,
                 lowerleft, lowercenter, lowerright) ;
      upperleft [High (DATAUNITINDEX), High (DATAUNITINDEX)] :=
          data_units [du - 1][Low (DATAUNITINDEX), High (DATAUNITINDEX)] ;
      upperright [High (DATAUNITINDEX), Low (DATAUNITINDEX)] :=
          data_units [du + 1][Low (DATAUNITINDEX), Low (DATAUNITINDEX)] ;

      FilterDataUnit (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]) ;
      Inc (du) ;
      Inc (offset, h_sampling * JPEGSAMPLEWIDTH) ;
      End ;

    // Upper Right
    FillEdges (data_units [du],
               upperleft, uppercenter, upperright,
               left, right,
               lowerleft, lowercenter, lowerright) ;
    upperleft [High (DATAUNITINDEX), High (DATAUNITINDEX)] :=
        data_units [du - 1][Low (DATAUNITINDEX), High (DATAUNITINDEX)] ;
    lowerright [Low (DATAUNITINDEX), Low (DATAUNITINDEX)] :=
        data_units [du + du_cols][Low (DATAUNITINDEX), High (DATAUNITINDEX)] ;
    FilterDataUnit (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) ;
    Inc (du) ;
    Inc (offset, h_sampling * JPEGSAMPLEWIDTH) ;
    Inc (offset, rowwidth * (JPEGSAMPLEWIDTH * v_sampling - 1)) ;

    for durows := 1 To du_rows - 2 Do
      Begin
      // Left Edge
      FillEdges (data_units [du],
                 upperleft, uppercenter, upperright,
                 left, right,
                 lowerleft, lowercenter, lowerright) ;
      upperleft [High (DATAUNITINDEX), High (DATAUNITINDEX)] :=
          data_units [du - du_cols][High (DATAUNITINDEX), Low (DATAUNITINDEX)] ;
      lowerleft [Low (DATAUNITINDEX), High (DATAUNITINDEX)] :=
          data_units [du + du_cols][Low (DATAUNITINDEX), Low (DATAUNITINDEX)] ;
      FilterDataUnit (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]) ;
      Inc (du) ;
      Inc (offset, h_sampling * JPEGSAMPLEWIDTH) ;
      for ducols := 1 To du_cols - 2 Do
        Begin
        FilterDataUnit (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]) ;
        Inc (du) ;
        Inc (offset, h_sampling * JPEGSAMPLEWIDTH) ;
        End ;
      // Right Edge
      FillEdges (data_units [du],
                 upperleft, uppercenter, upperright,
                 left, right,
                 lowerleft, lowercenter, lowerright) ;
      upperright [High (DATAUNITINDEX), Low (DATAUNITINDEX)] :=
          data_units [du - du_cols][High (DATAUNITINDEX), High (DATAUNITINDEX)] ;
      lowerright [Low (DATAUNITINDEX), Low (DATAUNITINDEX)] :=
          data_units [du + du_cols][Low (DATAUNITINDEX), High (DATAUNITINDEX)] ;
      FilterDataUnit (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) ;
      Inc (du) ;
      Inc (offset, h_sampling * JPEGSAMPLEWIDTH) ;
      Inc (offset, rowwidth * (JPEGSAMPLEWIDTH * v_sampling - 1)) ;
      End ;

    // Bottom Left Corner
    FillEdges (data_units [du],
               upperleft, uppercenter, upperright,
               left, right,
               lowerleft, lowercenter, lowerright) ;
    upperleft [High (DATAUNITINDEX), High (DATAUNITINDEX)] :=
        data_units [du - du_cols][High (DATAUNITINDEX), Low (DATAUNITINDEX)] ;
    lowerright [Low (DATAUNITINDEX), Low (DATAUNITINDEX)] :=
        data_units [du + 1][Low (DATAUNITINDEX), Low (DATAUNITINDEX)] ;
    FilterDataUnit (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) ;
    Inc (du) ;
    Inc (offset, h_sampling * JPEGSAMPLEWIDTH) ;

    for ii := 1 To du_cols - 2 Do
      Begin
      FillEdges (data_units [du],
                 upperleft, uppercenter, upperright,
                 left, right,
                 lowerleft, lowercenter, lowerright) ;
      lowerleft [Low (DATAUNITINDEX), High (DATAUNITINDEX)] :=
          data_units [du - 1][High (DATAUNITINDEX), High (DATAUNITINDEX)] ;
      lowerright [Low (DATAUNITINDEX), Low (DATAUNITINDEX)] :=
          data_units [du + 1][Low (DATAUNITINDEX), Low (DATAUNITINDEX)] ;
      FilterDataUnit (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) ;
      Inc (du) ;
      Inc (offset, h_sampling * JPEGSAMPLEWIDTH) ;
      End ;

    FillEdges (data_units [du],
               upperleft, uppercenter, upperright,
               left, right,
               lowerleft, lowercenter, lowerright) ;
    lowerleft [Low (DATAUNITINDEX), High (DATAUNITINDEX)] :=
        data_units [du - 1][High (DATAUNITINDEX), High (DATAUNITINDEX)] ;
    upperright [High (DATAUNITINDEX), Low (DATAUNITINDEX)] :=
        data_units [du - du_cols][High (DATAUNITINDEX), High (DATAUNITINDEX)] ;
    FilterDataUnit (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) ;
    // The remaining increments are just for debugging.
    Inc (du) ;
    Inc (offset, h_sampling * JPEGSAMPLEWIDTH) ;
    Inc (offset, rowwidth * (JPEGSAMPLEWIDTH * v_sampling - 1)) ;

    End ;

  begin

  rowwidth := du_cols * h_sampling * JPEGSAMPLEWIDTH ;

  extendRightColumn ;
  extendBottomRow ;

  if (du_rows = 1) And (du_cols = 1) Then
    // Special case where the component has just one data unit.
    FilterSingleDataUnitImage
  else if (du_rows = 1) Then
    // Special case of a component with a single row of data units.
    FilterSingledataUnitRowImage
  else if (du_cols = 1) Then
    // Special case of a component with a single column of data units.
    FilterSingledataUnitColumnImage
  else
    // General case. The component has at least 2 rows and two columns of data
    // units.
    FilterNormalImage ;

  End ;

Procedure TJpegDecoderComponent.resetDcDifference ;
  begin
  last_dc_value := 0 ;
  End ;

End.
