{$IFDEF FPC}
{$DEFINE NOT_USE_KOL_ERR}
{$MODE Delphi}
{$ASMMODE intel}
{$GOTO ON}
{$ENDIF}

{$IFDEF NO_GIF_OPTIMIZE}
  {$UNDEF ASM_GIF}
  {$UNDEF GIF_MMX}
{$ELSE}
  {$DEFINE ASM_GIF}
  {$DEFINE GIF_MMX}
{$ENDIF}

//{$DEFINE GIF_SAFE} // to check array boundaries while LZW decoding GIF
                     // (this prevents Access Violation exceptions and other
                     // possible errors)

//{$IFDEF GIF_SAFE}
//  {$DEFINE NO_GIF_MMX}
//{$ENDIF}

{$IFDEF NO_GIF_MMX}
  {$UNDEF GIF_MMX}
{$ENDIF}

{$IFNDEF GIF_LOG}
{$O+}
{$ENDIF GIF_LOG}
{$W-}

unit KOLGraphicCompression;

// This file is part of the image library GraphicEx (www.lischke-online.de/Graphics.html).
//
// GraphicCompression contains various encoder/decoder classes used to handle compressed
// data in the various image classes.
//
// Currently supported methods are:
// - LZW (Lempel-Ziff-Welch)
//   + TIF
//   + GIF
// - RLE (run length encoding)
//   + TGA,
//   + PCX,
//   + packbits
//   + SGI
//   + CUT
//   + RLA
//   + PSP
// - CCITT
//   + raw G3 (fax T.4)
//   + modified G3 (CCITT RLE)
//   + modified G3 w/ word alignment (CCITT RLEW)
// - LZ77
// - Thunderscan
// - JPEG
// - PCD Huffmann encoding (photo CD)
//
// (c) Copyright 1999, 2000  Dipl. Ing. Mike Lischke (public@lischke-online.de). All rights reserved.
//
// This package is freeware for non-commercial use only.
// Contact author for licenses (shareware@lischke-online.de) and see License.txt which comes with the package.

//////////////////////////////////////////////////
// Converted to KOL by Dimaxx (dimaxx@atnet.ru) //
//////////////////////////////////////////////////

interface

{$ALIGN OFF}
{$I KOLDEF.INC}

uses Windows, KOL, {$IFDEF NOT_USE_KOL_ERR}sysutils {$ELSE}Err {$ENDIF}, Errors,
  MZLib {$IFDEF GIF_MMX}, Mmx {$ENDIF}; // general inflate/deflate and LZ77 compression support

type
  // abstract decoder class to define the base functionality of an encoder/decoder
  PDecoder = ^TDecoder;
  TDecoder = {$IFDEF NOCLASSES} object(TObj) {$ELSE} class {$ENDIF}
  public
    procedure Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer); virtual; abstract;
    procedure DecodeEnd; virtual;
    procedure DecodeInit; virtual;
  end;

  // generally, there should be no need to cover the decoder classes by conditional symbols
  // because the image classes which use the decoder classes are already covered and if they
  // aren't compiled then the decoders are also not compiled (more precisely: not linked)
  PTargaRLEDecoder = ^TTargaRLEDecoder;
  TTargaRLEDecoder = {$IFDEF NOCLASSES} object(TDecoder) {$ELSE} class(TDecoder) {$ENDIF}
  private
    FColorDepth: cardinal;
  public
    constructor Create(ColorDepth: cardinal);
    procedure Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
      {$IFDEF NOCLASSES} virtual; {$ELSE} override; {$ENDIF}
  end;

  // Lempel-Ziff-Welch encoder/decoder class
  // TIFF LZW compression / decompression is a bit different to the common LZW code
  PTiffLzwDecoder = ^TTIFFLZWDecoder;
  TTIFFLZWDecoder = {$IFDEF NOCLASSES} object(TDecoder) {$ELSE} class(TDecoder) {$ENDIF}
  public
    procedure Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
      {$IFDEF NOCLASSES} virtual; {$ELSE} override; {$ENDIF}
  end;

  PPackBitsRLEDecoder = ^TPackbitsRLEDecoder;
  TPackbitsRLEDecoder = {$IFDEF NOCLASSES} object(TDecoder) {$ELSE} class(TDecoder) {$ENDIF}
  public
    procedure Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
      {$IFDEF NOCLASSES} virtual; {$ELSE} override; {$ENDIF}
  end;

  PPcxRLEDecoder = ^TPCXRLEDecoder;
  TPCXRLEDecoder = {$IFDEF NOCLASSES} object(TDecoder) {$ELSE} class(TDecoder) {$ENDIF}
  public
    procedure Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
      {$IFDEF NOCLASSES} virtual; {$ELSE} override; {$ENDIF}
  end;

  PSGIRLEDecoder = ^TSGIRLEDecoder;
  TSGIRLEDecoder = {$IFDEF NOCLASSES} object(TDecoder) {$ELSE} class(TDecoder) {$ENDIF}
  private
    FSampleSize: byte; // 8 or 16 bits
  public
    constructor Create(SampleSize: byte);
    procedure Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
      {$IFDEF NOCLASSES} virtual; {$ELSE} override; {$ENDIF}
  end;

  PCUTRLEDecoder = ^TCUTRLEDecoder;
  TCUTRLEDecoder = {$IFDEF NOCLASSES} object(TDecoder) {$ELSE} class(TDecoder) {$ENDIF}
  public
    procedure Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
      {$IFDEF NOCLASSES} virtual; {$ELSE} override; {$ENDIF}
  end;

  PPSPRLEDecoder = ^TPSPRLEDecoder;
  TPSPRLEDecoder = {$IFDEF NOCLASSES} object(TDecoder) {$ELSE} class(TDecoder) {$ENDIF}
  public
    procedure Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
      {$IFDEF NOCLASSES} virtual; {$ELSE} override; {$ENDIF}
  end;

  // Note: We need a different LZW decoder class for GIF because the bit order is reversed compared to that
  //       of TIFF and the code size increment is handled slightly different.
  PGIFLZWDecoder = ^TGIFLZWDecoder;
  TGIFLZWDecoder = {$IFDEF NOCLASSES} object(TDecoder) {$ELSE} class(TDecoder) {$ENDIF}
  private
    FInitialCodeSize: byte;
    FLineWidth: Integer;
    FCorrupted: Boolean;
  public
    constructor Create(InitialCodeSize: byte; linewidth: Integer);
    procedure Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
      {$IFDEF NOCLASSES} virtual; {$ELSE} override; {$ENDIF}
    procedure CxDecode( var Source, Dest: PByte; PackedSize,UnpackedSize: Integer );
    property GIFCorrupted: Boolean read FCorrupted;
  end;

  PRLADecoder = ^TRLADecoder;
  TRLADecoder = {$IFDEF NOCLASSES} object(TDecoder) {$ELSE} class(TDecoder) {$ENDIF}
  public
    procedure Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
      {$IFDEF NOCLASSES} virtual; {$ELSE} override; {$ENDIF}
  end;

  TStateEntry = packed record
    NewState: array[Boolean] of cardinal;
    RunLength: integer;
  end;
  TStateArray = array of TStateEntry;

  TCCITTDecoder = {$IFDEF NOCLASSES} object(TDecoder) {$ELSE} class(TDecoder) {$ENDIF}
  private
    FOptions: integer;  // determines some options how to proceed
                        // Bit 0: if set then two-dimensional encoding was used, otherwise one-dimensional
                        // Bit 1: if set then data is uncompressed
                        // Bit 2: if set then fill bits are used before EOL codes so that EOL codes always end at
                        //        at a byte boundary (not used in this context)
    FIsWhite,           // alternating flag used while coding
    FSwapBits: boolean; // True if the order of all bits in a byte must be swapped
    FWhiteStates,
    FBlackStates: TStateArray;
    FWidth: cardinal;   // need to know how line length for modified huffman encoding
                        // coding/encoding variables
    FBitsLeft,FMask,FBits: byte;
    FPackedSize,FRestWidth: cardinal;
    FSource,FTarget: PByte;
    FFreeTargetBits: byte;
    FWordAligned: boolean;
    procedure MakeStates;
  protected
    function FillRun(RunLength: cardinal): boolean;
    function FindBlackCode: integer;
    function FindWhiteCode: integer;
    function NextBit: boolean;
  public
    constructor Create(Options: integer; SwapBits,WordAligned: boolean; Width: cardinal);
  end;

  PCCITTFax3Decoder = ^TCCITTFax3Decoder;
  TCCITTFax3Decoder = {$IFDEF NOCLASSES} object(TCCITTDecoder)
                      {$ELSE} class(TCCITTDecoder) {$ENDIF}
  public
    procedure Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
      {$IFDEF NOCLASSES} virtual; {$ELSE} override; {$ENDIF}
  end;

  PCCITTFax4Decoder = ^TCCITTFax4Decoder;
  TCCITTFax4Decoder = {$IFDEF NOCLASSES} object(TCCITTDecoder)
                      {$ELSE} class(TCCITTDecoder) {$ENDIF}
  public
    procedure Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
      {$IFDEF NOCLASSES} virtual; {$ELSE} override; {$ENDIF}
  end;

  PCCITTMHDecoder = ^TCCITTMHDecoder;
  TCCITTMHDecoder = {$IFDEF NOCLASSES} object(TCCITTDecoder)
                      {$ELSE} class(TCCITTDecoder) {$ENDIF} // modified Huffman RLE
  public
    procedure Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
      {$IFDEF NOCLASSES} virtual; {$ELSE} override; {$ENDIF}
  end;

  PLZ77Decoder = ^TLZ77Decoder;
  TLZ77Decoder = {$IFDEF NOCLASSES} object(TDecoder) {$ELSE} class(TDecoder) {$ENDIF}
  private
    FStream: TZState;
    FZLibResult,         // contains the return code of the last ZLib operation
    FFlushMode: integer; // one of flush constants declard in ZLib.pas
                         // this is usually Z_FINISH for PSP and Z_PARTIAL_FLUSH for PNG
    FAutoReset: boolean; // TIF, PSP and PNG share this decoder, TIF needs a reset for each
                         // decoder run
    function GetAvailableInput: integer;
    function GetAvailableOutput: integer;
  public
    constructor Create(FlushMode: integer; AutoReset: boolean);
    procedure Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
      {$IFDEF NOCLASSES} virtual; {$ELSE} override; {$ENDIF}
    procedure DecodeEnd;
      {$IFDEF NOCLASSES} virtual; {$ELSE} override; {$ENDIF}
    procedure DecodeInit;
      {$IFDEF NOCLASSES} virtual; {$ELSE} override; {$ENDIF}
    property AvailableInput: integer read GetAvailableInput;
    property AvailableOutput: integer read GetAvailableOutput;
    property ZLibResult: integer read FZLibResult;
  end;

  TThunderDecoder = {$IFDEF NOCLASSES} object(TDecoder) {$ELSE} class(TDecoder) {$ENDIF}
  private
    FWidth: cardinal; // width of a scanline in pixels
  public
    constructor Create(Width: cardinal);
    procedure Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
      {$IFDEF NOCLASSES} virtual; {$ELSE} override; {$ENDIF}
  end;

  PPCDDecoder = ^TPCDDecoder;
  TPCDDecoder = {$IFDEF NOCLASSES} object(TDecoder) {$ELSE} class(TDecoder) {$ENDIF}
  private
    FStream: PStream;  // decoder must read some data
  public
    constructor Create(Stream: PStream);
    procedure Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
      {$IFDEF NOCLASSES} virtual; {$ELSE} override; {$ENDIF}
  end;

//----------------------------------------------------------------------------------------------------------------------

implementation

uses {$IFDEF NOT_USE_KOL_ERR}math, {$ELSE}KOLMath, {$ENDIF} KOLGraphicEx, KOLGraphicColor;

const GIFBufSize = {$IFDEF GIF_SAFE} 16384 {$ELSE} 4096 {$ENDIF};
const // LZW encoding and decoding support
  NoLZWCode = 4096;

{$IFDEF GIF_MMX}
var mmxSupported: Boolean;
{$ENDIF}

//----------------------------------------------------------------------------------------------------------------------

{$IFDEF NOT_USE_KOL_ERR}
procedure CompressionError(Code: integer);
var E: Exception;
begin
  E:=Exception.Create(int2str(Code));
  //E.ErrorCode:=Code;
  raise E;
end;
{$ELSE}
procedure CompressionError(Code: integer);
var E: Exception;
begin
  E:=Exception.Create(e_Custom,ErrorMsg[Code]);
  E.ErrorCode:=Code;
  raise E;
end;
{$ENDIF}

//----------------- TDecoder (generic decoder class) -------------------------------------------------------------------

procedure TDecoder.DecodeEnd;
// called after all decompression has been done
begin
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TDecoder.DecodeInit;
// called before any decompression can start
begin
end;

//----------------- TTargaRLEDecoder -----------------------------------------------------------------------------------

constructor TTargaRLEDecoder.Create(ColorDepth: cardinal);
begin
  FColorDepth:=ColorDepth;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TTargaRLEDecoder.Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
type
  PCardinalArray = ^TCardinalArray;
  TCardinalArray = array[0..MaxInt div 4-1] of Cardinal;
var I: integer;
    SourcePtr,TargetPtr: PByte;
    RunLength,Sourcecardinal: cardinal;
begin
  TargetPtr:=Dest;
  SourcePtr:=Source;
  // unrolled decoder loop to speed up process
  case FColorDepth of
    8: while UnpackedSize>0 do
         begin
           RunLength:=1+(SourcePtr^ and $7F);
           if SourcePtr^>$7F then
             begin
               Inc(SourcePtr);
               FillChar(TargetPtr^,RunLength,SourcePtr^);
               Inc(TargetPtr,RunLength);
               Inc(SourcePtr);
             end
           else
             begin
               Inc(SourcePtr);
               Move(SourcePtr^,TargetPtr^,RunLength);
               Inc(SourcePtr,RunLength);
               Inc(TargetPtr,RunLength);
             end;
           Dec(UnpackedSize, RunLength);
         end;
    15,
    16: while UnpackedSize>0 do
          begin
            RunLength:=1+(SourcePtr^ and $7F);
            if SourcePtr^>$7F then
              begin
                Inc(SourcePtr);
                for I:=0 to RunLength-1 do
                  begin
                    TargetPtr^:=SourcePtr^;
                    Inc(SourcePtr);
                    Inc(TargetPtr);
                    TargetPtr^:=SourcePtr^;
                    Dec(SourcePtr);
                    Inc(TargetPtr);
                  end;
                Inc(SourcePtr,2);
              end
            else
              begin
                Inc(SourcePtr);
                Move(SourcePtr^,TargetPtr^,2*RunLength);
                Inc(SourcePtr,2*RunLength);
                Inc(TargetPtr,2*RunLength);
              end;
            Dec(UnpackedSize,RunLength);
          end;
    24: while UnpackedSize>0 do
          begin
            RunLength:=1+(SourcePtr^ and $7F);
            if SourcePtr^>$7F then
              begin
                Inc(SourcePtr);
                for I:=0 to RunLength-1 do
                  begin
                    TargetPtr^:=SourcePtr^;
                    Inc(SourcePtr);
                    Inc(TargetPtr);
                    TargetPtr^:=SourcePtr^;
                    Inc(SourcePtr);
                    Inc(TargetPtr);
                    TargetPtr^:=SourcePtr^;
                    Dec(SourcePtr,2);
                    Inc(TargetPtr);
                  end;
                Inc(SourcePtr,3);
              end
            else
              begin
                Inc(SourcePtr);
                Move(SourcePtr^,TargetPtr^,3*RunLength);
                Inc(SourcePtr,3*RunLength);
                Inc(TargetPtr,3*RunLength);
              end;
            Dec(UnpackedSize, RunLength);
          end;
    32: while UnpackedSize>0 do
          begin
            RunLength:=1+(SourcePtr^ and $7F);
            if SourcePtr^>$7F then
              begin
                Inc(SourcePtr);
                SourceCardinal:=PCardinalArray(SourcePtr)[0];
                for I:=0 to RunLength-1 do
                PCardinalArray(TargetPtr)[I]:=SourceCardinal;
                Inc(TargetPtr,4*RunLength);
                Inc(SourcePtr,4);
              end
            else
              begin
                Inc(SourcePtr);
                Move(SourcePtr^,TargetPtr^,4*RunLength);
                Inc(SourcePtr,4*RunLength);
                Inc(TargetPtr,4*RunLength);
              end;
            Dec(UnpackedSize,RunLength);
          end;
  end;
  Source:=SourcePtr;
end;

//----------------- TTIFFLZWDecoder ------------------------------------------------------------------------------------

procedure TTIFFLZWDecoder.Decode(var Source, Dest: pointer; PackedSize, UnpackedSize: integer);
var I: integer;
    Data,                               // current data
    Bits,                               // counter for bit management
    Code: cardinal;                     // current code value
    SourcePtr: PByte;
    InCode: cardinal;                   // Buffer for passed code
    CodeSize,CodeMask,FreeCode,OldCode: cardinal;
    Prefix: array[0..4095] of cardinal; // LZW prefix
    Suffix,                             // LZW suffix
    Stack: array[0..4095] of byte;      // stack
    Stackpointer,Target: PByte;
    FirstChar: byte;                    // Buffer for decoded byte
    ClearCode,EOICode: word;
begin
  Target:=Dest;
  SourcePtr:=Source;
  // initialize parameter
  ClearCode:=1 shl 8;
  EOICode:=ClearCode+1;
  FreeCode:=ClearCode+2;
  OldCode:=NoLZWCode;
  CodeSize:=9;
  CodeMask:=(1 shl CodeSize)-1;
  // init code table
  for I:=0 to ClearCode-1 do
    begin
      Prefix[I]:=NoLZWCode;
      Suffix[I]:=I;
    end;
  // initialize stack
  Stackpointer:=@Stack;
  FirstChar:=0;
  Data:=0;
  Bits:=0;
  while (PackedSize>0) and (UnpackedSize>0) do
    begin
      // read code from bit stream
      Inc(Data,cardinal(SourcePtr^) shl (24-Bits));
      Inc(Bits,8);
      while Bits>=CodeSize do
        begin
          // current code
          Code:=(Data and ($FFFFFFFF-CodeMask)) shr (32-CodeSize);
          // mask it
          Data:=Data shl CodeSize;
          Dec(Bits,CodeSize);
          if Code=EOICode then Exit;
          // handling of clear codes
          if Code=ClearCode then
            begin
              // reset of all variables
              CodeSize:=9;
              CodeMask:=(1 shl CodeSize)-1;
              FreeCode:=ClearCode+2;
              OldCode:=NoLZWCode;
              Continue;
            end;
          // check whether it is a valid, already registered code
          if Code>FreeCode then Break;
          // handling for the first LZW code: print and keep it
          if OldCode=NoLZWCode then
            begin
              FirstChar:=Suffix[Code];
              Target^:=FirstChar;
              Inc(Target);
              Dec(UnpackedSize);
              OldCode:=Code;
              Continue;
            end;
          // keep the passed LZW code
          InCode:=Code;
          // the first LZW code is always smaller than FFirstCode
          if Code=FreeCode then
            begin
              Stackpointer^:=FirstChar;
              Inc(StackPointer);
              Code:=OldCode;
            end;
          // loop to put decoded bytes onto the stack
          while Code>ClearCode do
            begin
              Stackpointer^:=Suffix[Code];
              Inc(StackPointer);
              Code:=Prefix[Code];
            end;
          // place new code into code table
          FirstChar:=Suffix[Code];
          Stackpointer^:=FirstChar;
          Inc(StackPointer);
          Prefix[FreeCode]:=OldCode;
          Suffix[FreeCode]:=FirstChar;
          if FreeCode<4096 then Inc(FreeCode);
          // increase code size if necessary
          if (FreeCode=CodeMask) and (CodeSize<12) then
            begin
              Inc(CodeSize);
              CodeMask:=(1 shl CodeSize)-1;
            end;
          // put decoded bytes (from the stack) into the target Buffer
          OldCode:=InCode;
          repeat
            Dec(StackPointer);
            Target^:=StackPointer^;
            Inc(Target);
            Dec(UnpackedSize);
          until cardinal(Stackpointer)<=cardinal(@Stack);
        end;
    Inc(SourcePtr);
    Dec(PackedSize);
  end;
end;

//----------------- TPackbitsRLEDecoder --------------------------------------------------------------------------------

procedure TPackbitsRLEDecoder.Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
// decodes a simple run-length encoded strip of size PackedSize
var SourcePtr,TargetPtr: PByte;
    N: integer;
begin
  TargetPtr:=Dest;
  SourcePtr:=Source;
  while (UnpackedSize>0) and (PackedSize>0) do
    begin
      N:=ShortInt(SourcePtr^);
      Inc(SourcePtr);
      Dec(PackedSize);
      if N<0 then // replicate next Byte -N+1 times
        begin
          if N=-128 then Continue; // nop
          N:=-N+1;
          if N>UnpackedSize then N:=UnpackedSize;
          FillChar(TargetPtr^,N,SourcePtr^);
          Inc(SourcePtr);
          Dec(PackedSize);
          Inc(TargetPtr,N);
          Dec(UnpackedSize,N);
        end
      else
        begin // copy next N+1 bytes literally
          Inc(N);
          if N>UnpackedSize then N:=UnpackedSize;
          if N>PackedSize then N:=PackedSize;
          Move(SourcePtr^,TargetPtr^,N);
          Inc(TargetPtr,N);
          Inc(SourcePtr,N);
          Dec(PackedSize,N);
          Dec(UnpackedSize,N);
        end;
    end;
end;

//----------------- TPCXRLEDecoder -------------------------------------------------------------------------------------

procedure TPCXRLEDecoder.Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
var Count: integer;
    SourcePtr,TargetPtr: PByte;
begin
  SourcePtr:=Source;
  TargetPtr:=Dest;
  while UnpackedSize>0 do
    begin
      if (SourcePtr^ and $C0)=$C0 then
        begin
          // RLE-Code
          Count:=SourcePtr^ and $3F;
          Inc(SourcePtr);
          if UnpackedSize<Count then Count:=UnpackedSize;
          FillChar(TargetPtr^,Count,SourcePtr^);
          Inc(SourcePtr);
          Inc(TargetPtr,Count);
          Dec(UnpackedSize,Count);
        end
      else
        begin
          // not compressed
          TargetPtr^:=SourcePtr^;
          Inc(SourcePtr);
          Inc(TargetPtr);
          Dec(UnpackedSize);
        end;
    end;
end;

//----------------- TSGIRLEDecoder -------------------------------------------------------------------------------------

constructor TSGIRLEDecoder.Create(SampleSize: byte);
begin
  FSampleSize:=SampleSize;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TSGIRLEDecoder.Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
var Source8,Target8: PByte;
    Source16,Target16: PWord;
    Pixel: byte;
    Pixel16: word;
    RunLength: cardinal;
begin
  if FSampleSize=8 then
    begin
      Source8:=Source;
      Target8:=Dest;
      while True do
        begin
          Pixel:=Source8^;
          Inc(Source8);
          RunLength:=Pixel and $7F;
          if RunLength=0 then Break;
          if (Pixel and $80)<>0 then
            begin
              Move(Source8^,Target8^,RunLength);
              Inc(Target8,RunLength);
              Inc(Source8,RunLength);
            end
          else
            begin
              Pixel:=Source8^;
              Inc(Source8);
              FillChar(Target8^,RunLength,Pixel);
              Inc(Target8,RunLength);
            end;
        end;
    end
  else
    begin
      // 16 bits per sample
      Source16:=Source;
      Target16:=Dest;
      while True do
        begin
          // SGI images are stored in big endian style, swap this one repeater value for it
          Pixel16:=System.Swap(Source16^);
          Inc(Source16);
          RunLength:=Pixel16 and $7F;
          if RunLength=0 then Break;
          if (Pixel16 and $80)<>0 then
            begin
              Move(Source16^,Target16^,2*RunLength);
              Inc(Source16^,RunLength);
              Inc(Target16^,RunLength);
            end
          else
            begin
              Pixel16:=Source16^;
              Inc(Source16);
              while RunLength>0 do
                begin
                  Target16^:=Pixel16;
                  Inc(Target16);
                  Dec(RunLength);
                end;
            end;
        end;
    end;
end;

//----------------- TCUTRLE --------------------------------------------------------------------------------------------

procedure TCUTRLEDecoder.Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
var TargetPtr: PByte;
    Pixel: byte;
    RunLength: cardinal;
begin
  TargetPtr:=Dest;
  // skip first two bytes per row (I don't know their meaning)
  Inc(PByte(Source),2);
  while True do
    begin
      Pixel:=PByte(Source)^;
      Inc(PByte(Source));
      if Pixel=0 then Break;
      RunLength:=Pixel and $7F;
      if (Pixel and $80)=0 then
        begin
          Move(Source^,TargetPtr^,RunLength);
          Inc(TargetPtr,RunLength);
          Inc(PByte(Source),RunLength);
        end
      else
        begin
          Pixel:=PByte(Source)^;
          Inc(PByte(Source));
          FillChar(TargetPtr^,RunLength,Pixel);
          Inc(TargetPtr,RunLength);
        end;
    end;
end;

//----------------- TPSPRLEDecoder -------------------------------------------------------------------------------------

procedure TPSPRLEDecoder.Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
var SourcePtr,TargetPtr: PByte;
    RunLength: cardinal;
begin
  SourcePtr:=Source;
  TargetPtr:=Dest;
  while PackedSize>0 do
    begin
      RunLength:=SourcePtr^;
      Inc(SourcePtr);
      Dec(PackedSize);
      if RunLength<128 then
        begin
          Move(SourcePtr^,TargetPtr^,RunLength);
          Inc(TargetPtr,RunLength);
          Inc(SourcePtr,RunLength);
          Dec(PackedSize,RunLength);
        end
      else
        begin
          Dec(RunLength,128);
          FillChar(TargetPtr^,RunLength,SourcePtr^);
          Inc(SourcePtr);
          Inc(TargetPtr,RunLength);
          Dec(PackedSize);
        end;
    end;
end;

//----------------- TGIFLZWDecoder -------------------------------------------------------------------------------------

constructor TGIFLZWDecoder.Create(InitialCodeSize: byte; linewidth: Integer);
begin
  FInitialCodeSize:=InitialCodeSize;
  FLineWidth := linewidth;
end;
//----------------------------------------------------------------------------------------------------------------------

procedure doGifLog( usz, psz, data: Integer );
begin
  LogFileOutput( GetStartDir + 'gif_log.txt',
    Int2Str( usz ) + ' '#9 + Int2Str( psz ) + ' '#9 + Int2Str( data ) );
end;

procedure doGifLog2( data: Byte );
begin
  LogFileOutput( GetStartDir + 'gif_log.txt',
    '<= ' + Int2Str( data ) );
end;

{
  Converted from C++ library CxImage (conversion by Vladimir Kladov)
}
{$O-}
procedure TGIFLZWDecoder.CxDecode(var Source, Dest: PByte; PackedSize,UnpackedSize: integer);
type short = SmallInt;
const MAX_CODES = 4095;
      code_mask: array[0..16] of word =
        ( $0000, $0001, $0003, $0007,
          $000F, $001F, $003F, $007F,
          $00FF, $01FF, $03FF, $07FF,
          $0FFF, $1FFF, $3FFF, $7FFF, $FFFF );
var sp, bufptr: PByte;
    buf: PByte;
    code, fc, oc, bufcnt: short;
    c, size: short;
    ret: Integer;
    bad_code_count: Integer;

    curr_size, top_slot, clear, ending,
    slot, newcodes: short;
    {navail_bytes,} nbits_left: short;

    stack, suffix: array[ 0..MAX_CODES ] of Byte;
    prefix: array[ 0..MAX_CODES ] of WORD;

    src, dst: PByte;
    b1: Byte;

    function out_line( from: PByte; count: Integer ): Integer;
    begin
      Result := UnpackedSize - count;
      if Result < 0 then count := UnpackedSize;
      move( from^, dst^, count );
      inc( dst, count );
      dec( UnpackedSize, count );
    end;

    function get_next_code: short;
    var	ret: DWORD;
    begin
	if (nbits_left = 0) then
        begin
		b1 := src^; inc( src ); dec( PackedSize );
		nbits_left := 8;
        end;

	if (PackedSize<0) then
        begin
          Result := ending; // prevent deadlocks (thanks to Mike Melnikov)
          Exit;
        end;

	ret := b1 shr (8 - nbits_left);

	while (curr_size > nbits_left) do
        begin
		b1 := src^; inc( src ); dec( PackedSize );
		ret := ret or ( b1 shl nbits_left );
		inc( nbits_left, 8 );
        end;

	nbits_left := nbits_left - curr_size;
	ret := ret and code_mask[curr_size];
        Result := ret;
    end;

begin
  FillChar( Dest^, UnpackedSize, 0 );

  //* Initialize for decoding a new image... */
  bad_code_count := 0;
  size := FInitialCodeSize;
  if (size < 2) or (9 < size) then
  begin
    //return(BAD_CODE_SIZE);
    FCorrupted := TRUE;
    Exit;
  end;
  // out_line = outline;
  //init_exp(size);
  curr_size := size + 1;
  top_slot  := 1 shl curr_size;
  clear     := 1 shl size;
  ending    := clear + 1;
  slot      := ending + 1;
  newcodes  := slot;
  //navail_bytes := 0;
  nbits_left   := 0;

  FillChar( stack, SizeOf( stack ), 0 );
  FillChar( prefix, SizeOf( prefix ), 0 );
  FillChar( suffix, SizeOf( suffix ), 0 );

  (* Initialize in case they forgot to put in a clear code.
   * (This shouldn't happen, but we'll try and decode it anyway...)
   *)
  oc := 0;
  fc := 0;

  //* Allocate space for the decode buffer */
  buf := AllocMem( FLinewidth + 1 );
  if (buf = nil) then
  begin
    //return(OUT_OF_MEMORY);
    Exit;
  end;

  //* Set up the stack pointer and decode buffer pointer */
  sp := @ stack[ 0 ];
  bufptr := buf;
  bufcnt := FLinewidth;

  (* This is the main loop.  For each code we get we pass through the
   * linked list of prefix codes, pushing the corresponding "character" for
   * each code onto the stack.  When the list reaches a single "character"
   * we push that on the stack too, and then start unstacking each
   * character for output in the correct order.  Special handling is
   * included for the clear code, and the whole thing ends when we get
   * an ending code.
   *)
  src := Source;
  dst := Dest;
  while TRUE do
  begin
    c := get_next_code;
    if c = ending then break;

    //* If the code is a clear code, reinitialize all necessary items.*/
    if (c = clear) then
    begin
            curr_size := size + 1;
            slot      := newcodes;
            top_slot  := 1 shl curr_size;

            (* Continue reading codes until we get a non-clear code
            * (Another unlikely, but possible case...)
            *)
            REPEAT
               c := get_next_code;
            UNTIL c <> clear;

            (* If we get an ending code immediately after a clear code
            * (Yet another unlikely case), then break out of the loop.
            *)
            if c = ending then break;

            (* Finally, if the code is beyond the range of already set codes,
            * (This one had better NOT happen...  I have no idea what will
            * result from this, but I doubt it will look good...) then set it
            * to color zero.
            *)
            if (c >= slot) then c := 0;
            fc := c;
            oc := fc;

            (* And let us not forget to put the char into the buffer... And
            * if, on the off chance, we were exactly one pixel from the end
            * of the line, we have to send the buffer to the out_line()
            * routine...
            *)
            bufptr^ := c; inc( bufptr );
            dec( bufcnt );
            if (bufcnt = 0) then
            begin
              ret := out_line( buf, FLinewidth );
              if (ret <= 0) then
              begin
                FreeMem( buf );
                Exit;
              end;
              bufptr := buf;
              bufcnt := FLinewidth;
            end;
    end
      else
    begin
            (* In this case, it's not a clear code or an ending code, so
            * it must be a code code...  So we can now decode the code into
            * a stack of character codes. (Clear as mud, right?)
            *)
            code := c;

            (* Here we go again with one of those off chances...  If, on the
            * off chance, the code we got is beyond the range of those already
            * set up (Another thing which had better NOT happen...) we trick
            * the decoder into thinking it actually got the last code read.
            * (Hmmn... I'm not sure why this works...  But it does...)
            *)
            if (code >= slot) then
            begin
              if (code > slot) then
                inc( bad_code_count );
              code := oc;
              sp^ := fc; inc( sp );
            end;

            (* Here we scan back along the linked list of prefixes, pushing
            * helpless characters (ie. suffixes) onto the stack as we do so.
            *)
            while code >= newcodes do
            begin
              sp^ := suffix[ code ]; inc( sp );
              code := prefix[ code ];
            end;

            (* Push the last character on the stack, and set up the new
            * prefix and suffix, and if the required slot number is greater
            * than that allowed by the current bit size, increase the bit
            * size.  (NOTE - If we are all full, we *don't* save the new
            * suffix and prefix...  I'm not certain if this is correct...
            * it might be more proper to overwrite the last code...
            *)
            sp^ := code; inc( sp );
            if (slot < top_slot) then
            begin
                    fc := code;
                    suffix[slot] := fc;
                    prefix[slot] := oc; inc( slot );
                    oc := c;
            end;
            if (slot >= top_slot) then
            begin
                    if (curr_size < 12) then
                    begin
                            top_slot := top_slot shl 1;
                            inc( curr_size );
                    end;
            end;

            (* Now that we've pushed the decoded string (in reverse order)
            * onto the stack, lets pop it off and put it into our decode
            * buffer...  And when the decode buffer is full, write another
            * line...
            *)
            while (sp <> @ stack[ 0 ]) do
            begin
                    dec( sp );
                    bufptr^ := sp^; inc( bufptr );
                    dec( bufcnt );
                    if bufcnt = 0 then
                    begin
                            ret := out_line( buf, FLinewidth );
                            if ret <= 0 then
                            begin
                              FreeMem( buf );
                              Exit;
                            end;
                            bufptr := buf;
                            bufcnt := FLinewidth;
                    end;
            end;

    end;

  end;

  if (bufcnt <> FLinewidth) then
    {ret :=} out_line( buf, FLinewidth - bufcnt );
  FreeMem( buf );

  if bad_code_count > 0 then
    FCorrupted := TRUE;

end;

procedure TGIFLZWDecoder.Decode(var Source, Dest: pointer; PackedSize,UnpackedSize: integer);
var I: integer;
    Data,                               // current data
    Bits                               // counter for bit management
    {$IFNDEF ASM_GIF}
    ,Code{$ENDIF}: cardinal;                     // current code value

    SourcePtr: PByte;
    InCode: cardinal;                   // Buffer for passed code
    CodeSize,CodeMask,FreeCode,OldCode: cardinal;
    Prefix: array[0..GIFBufSize-1] of cardinal; // LZW prefix
    Suffix,                             // LZW suffix
    Stack: array[0..GIFBufSize-1] of byte;      // stack
    StackPointer,Target: PByte;
    FirstChar: byte;                    // Buffer for decoded byte
    ClearCode,EOICode: {$IFDEF ASM_GIF} cardinal {$ELSE} word {$ENDIF};
    {$IFDEF ASM_GIF}
    initial_code_size: Byte;
    {$ENDIF}
    {$IFDEF GIF_SAFE}
    Bad: Boolean;
    {$ENDIF}
begin
  {$IFDEF GIF_SAFE}
  Bad := FALSE;
  {$ENDIF}
  Target:=Dest; if Target <> nil then;
  SourcePtr:=Source;
  // initialize parameter
  CodeSize:=FInitialCodeSize+1;
  ClearCode:=1 shl FInitialCodeSize;
  EOICode:=ClearCode+1;
  FreeCode:=ClearCode+2;
  OldCode:=NoLZWCode;
  CodeMask:=(1 shl CodeSize)-1;
  // init code table
  {$IFDEF GIF_SAFE}
  FillChar( Suffix, SizeOf( Suffix ), 0 );
  FillChar( Prefix, SizeOf( Prefix ), 0 );
  //FillChar( Stack, SizeOf( Stack ), 0 );
  {$ENDIF}
  for I:=0 to ClearCode-1 do
    begin
      Prefix[I]:=NoLZWCode;
      Suffix[I]:=I;
    end;
  // initialize stack
  StackPointer:=@Stack; if StackPointer <> nil then;
  FirstChar:=0;
  Data:=0;
  Bits:=0;
  {$IFDEF ASM_GIF}
      initial_code_size := FInitialCodeSize;
      {$IFDEF GIF_MMX}
      if mmxSupported then
        {$I GIF_MMX.inc}
        else
      {$ENDIF} /////////////////////////////////////////////////////////////////////
        {$I GIF_ASM.inc}
      {$IFDEF GIF_SAFE}
        if Bad then FCorrupted := TRUE;
        {$IFDEF GIF_TRY_CX}
        if Bad then
          CxDecode( PByte( Source ), PByte( Dest ), PackedSize, UnpackedSize );
        {$ENDIF}
      {$ENDIF}
  {$ELSE}
  while (UnpackedSize>0) and (PackedSize>0) do
    begin
      {$IFDEF GIF_LOG}
      doGifLog( UnpackedSize, PackedSize, Data );
      {$ENDIF}
      // read code from bit stream
      Inc(Data,SourcePtr^ shl Bits);
      Inc(Bits,8);
      while Bits>=CodeSize do
        begin
          // current code
          Code:=Data and CodeMask;
          // prepare next run
          Data:=Data shr CodeSize;
          Dec(Bits,CodeSize);
          // decoding finished?
          if Code=EOICode then Break;
          // handling of clear codes
          if Code=ClearCode then
            begin
              // reset of all variables
              CodeSize:=FInitialCodeSize+1;
              CodeMask:=(1 shl CodeSize)-1;
              FreeCode:=ClearCode+2;
              OldCode:=NoLZWCode;
              Continue;
            end;
          // check whether it is a valid, already registered code
          if Code>FreeCode then Break;
          // handling for the first LZW code: print and keep it
          if OldCode=NoLZWCode then
            begin
              FirstChar:=Suffix[Code];
              {$IFDEF GIF_LOG}
              doGifLog2( FirstChar );
              {$ENDIF}
              Target^:=FirstChar;
              Inc(Target);
              Dec(UnpackedSize);
              OldCode:=Code;
              Continue;
            end;
          // keep the passed LZW code
          InCode:=Code;
          // the first LZW code is always smaller than FFirstCode
          if Code=FreeCode then
            begin
              StackPointer^:=FirstChar;
              Inc(StackPointer);
              Code:=OldCode;
            end;
          // loop to put decoded bytes onto the stack
          while Code>ClearCode do
            begin
              StackPointer^:=Suffix[Code];
              Inc(StackPointer);
              Code:=Prefix[Code];
            end;
          // place new code into code table
          FirstChar:=Suffix[Code];
          Stackpointer^:=FirstChar;
          Inc(Stackpointer);
          Prefix[FreeCode]:=OldCode;
          Suffix[FreeCode]:=FirstChar;
          // increase code size if necessary
          if (FreeCode=CodeMask) and (CodeSize<12) then
            begin
              Inc(CodeSize);
              CodeMask:=(1 shl CodeSize)-1;
            end;
          if FreeCode<4095 then Inc(FreeCode);
          // put decoded bytes (from the stack) into the target Buffer
          OldCode:=InCode;
          repeat
            Dec(StackPointer);
            {$IFDEF GIF_LOG}
            doGifLog2( StackPointer^ );
            {$ENDIF}
            Target^:=StackPointer^;
            Inc(Target);
            Dec(UnpackedSize);
          until StackPointer=@Stack;
        end;
      Inc(SourcePtr);
      Dec(PackedSize);
    end;
  {$ENDIF}
end;

//----------------- TRLADecoder ----------------------------------------------------------------------------------------

procedure TRLADecoder.Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
// decodes a simple run-length encoded strip of size PackedSize
// this is very similar to TPackbitsRLEDecoder
var SourcePtr,TargetPtr: PByte;
    N: smallint;
begin
  TargetPtr:=Dest;
  SourcePtr:=Source;
  while PackedSize>0 do
    begin
      N:=ShortInt(SourcePtr^);
      Inc(SourcePtr);
      Dec(PackedSize);
      if N>=0 then // replicate next Byte N+1 times
        begin
          FillChar(TargetPtr^,N+1,SourcePtr^);
          Inc(TargetPtr,N+1);
          Inc(SourcePtr);
          Dec(PackedSize);
        end
      else
        begin // copy next -N bytes literally
          Move(SourcePtr^,TargetPtr^,-N);
          Inc(TargetPtr,-N);
          Inc(SourcePtr,-N);
          Inc(PackedSize,N);
        end;
      end;
end;

//----------------- TCCITTDecoder --------------------------------------------------------------------------------------

constructor TCCITTDecoder.Create(Options: integer; SwapBits,WordAligned: boolean; Width: cardinal);
begin
  FOptions:=Options;
  FSwapBits:=SwapBits;
  FWidth:=Width;
  FWordAligned:=WordAligned;
  MakeStates;
end;

//----------------------------------------------------------------------------------------------------------------------

const
  // 256 bytes to make bit reversing easy,
  // this is actually not much more than writing bit manipulation code, but much faster
  ReverseTable: array[0..255] of byte = (
    $00,$80,$40,$C0,$20,$A0,$60,$E0,$10,$90,$50,$D0,$30,$B0,$70,$F0,
    $08,$88,$48,$C8,$28,$A8,$68,$E8,$18,$98,$58,$D8,$38,$B8,$78,$F8,
    $04,$84,$44,$C4,$24,$A4,$64,$E4,$14,$94,$54,$D4,$34,$B4,$74,$F4,
    $0C,$8C,$4C,$CC,$2C,$AC,$6C,$EC,$1C,$9C,$5C,$DC,$3C,$BC,$7C,$FC,
    $02,$82,$42,$C2,$22,$A2,$62,$E2,$12,$92,$52,$D2,$32,$B2,$72,$F2,
    $0A,$8A,$4A,$CA,$2A,$AA,$6A,$EA,$1A,$9A,$5A,$DA,$3A,$BA,$7A,$FA,
    $06,$86,$46,$C6,$26,$A6,$66,$E6,$16,$96,$56,$D6,$36,$B6,$76,$F6,
    $0E,$8E,$4E,$CE,$2E,$AE,$6E,$EE,$1E,$9E,$5E,$DE,$3E,$BE,$7E,$FE,
    $01,$81,$41,$C1,$21,$A1,$61,$E1,$11,$91,$51,$D1,$31,$B1,$71,$F1,
    $09,$89,$49,$C9,$29,$A9,$69,$E9,$19,$99,$59,$D9,$39,$B9,$79,$F9,
    $05,$85,$45,$C5,$25,$A5,$65,$E5,$15,$95,$55,$D5,$35,$B5,$75,$F5,
    $0D,$8D,$4D,$CD,$2D,$AD,$6D,$ED,$1D,$9D,$5D,$DD,$3D,$BD,$7D,$FD,
    $03,$83,$43,$C3,$23,$A3,$63,$E3,$13,$93,$53,$D3,$33,$B3,$73,$F3,
    $0B,$8B,$4B,$CB,$2B,$AB,$6B,$EB,$1B,$9B,$5B,$DB,$3B,$BB,$7B,$FB,
    $07,$87,$47,$C7,$27,$A7,$67,$E7,$17,$97,$57,$D7,$37,$B7,$77,$F7,
    $0F,$8F,$4F,$CF,$2F,$AF,$6F,$EF,$1F,$9F,$5F,$DF,$3F,$BF,$7F,$FF);

  G3_EOL     = -1;
  G3_INVALID = -2;

//----------------------------------------------------------------------------------------------------------------------

function TCCITTDecoder.FillRun(RunLength: cardinal): boolean;
// fills a number of bits with 1s (for black, white only increments pointers),
// returns True if the line has been filled entirely, otherwise False
var Run: cardinal;
begin
  Run:=KOL.Min(FFreeTargetBits,RunLength);
  // fill remaining bits in the current byte
  if Run in [1..7] then
    begin
      Dec(FFreeTargetBits,Run);
      if not FIsWhite then FTarget^:=FTarget^ or (((1 shl Run)-1) shl FFreeTargetBits);
      if FFreeTargetBits=0 then
        begin
          Inc(FTarget);
          FFreeTargetBits:=8;
        end;
      Run:=RunLength-Run;
    end
  else Run:=RunLength;
  // fill entire bytes whenever possible
  if Run>0 then
    begin
      if not FIsWhite then FillChar(FTarget^,Run div 8,$FF);
      Inc(FTarget,Run div 8);
      Run:=Run mod 8;
    end;
  // finally fill remaining bits
  if Run>0 then
    begin
      FFreeTargetBits:=8-Run;
      if not FIsWhite then FTarget^:=((1 shl Run)-1) shl FFreeTargetBits;
    end;
  // this will throw an exception if the sum of the run lengths for a row is not
  // exactly the row size (the documentation speaks of an unrecoverable error)
  if cardinal(RunLength)>FRestWidth then RunLength:=FRestWidth;
  Dec(FRestWidth,RunLength);
  Result:=FRestWidth=0;
end;

//----------------------------------------------------------------------------------------------------------------------

function TCCITTDecoder.FindBlackCode: integer;
// Executes the state machine to find the run length for the next bit combination.
// Returns the run length of the found code.
var State,NewState: cardinal;
    Bit: boolean;
begin
  State:=0;
  Result:=0;
  repeat
    // advance to next byte in the input Buffer if necessary
    if FBitsLeft=0 then
      begin
        if FPackedSize=0 then Break;
        FBits:=FSource^;
        Inc(FSource);
        Dec(FPackedSize);
        FMask:=$80;
        FBitsLeft:=8;
      end;
    Bit:=(FBits and FMask)<>0;
    // advance the state machine
    NewState:=FBlackStates[State].NewState[Bit];
    if NewState=0 then
      begin
        Inc(Result,FBlackStates[State].RunLength);
        if FBlackStates[State].RunLength<64 then Break else NewState:=FBlackStates[0].NewState[Bit];
      end;
    State:=NewState;
    // address next bit
    FMask:=FMask shr 1;
    if FBitsLeft>0 then Dec(FBitsLeft);
  until False;
end;

//----------------------------------------------------------------------------------------------------------------------

function TCCITTDecoder.FindWhiteCode: integer;
// Executes the state machine to find the run length for the next bit combination.
// Returns the run length of the found code.
var State,NewState: cardinal;
    Bit: boolean;
begin
  State:=0;
  Result:=0;
  repeat
    // advance to next byte in the input Buffer if necessary
    if FBitsLeft=0 then
      begin
        if FPackedSize=0 then Break;
        FBits:=FSource^;
        Inc(FSource);
        Dec(FPackedSize);
        FMask:=$80;
        FBitsLeft:=8;
      end;
    Bit:=(FBits and FMask)<>0;
    // advance the state machine
    NewState:=FWhiteStates[State].NewState[Bit];
    if NewState=0 then
      begin
        // a code has been found
        Inc(Result,FWhiteStates[State].RunLength);
        // if we found a terminating code then exit loop, otherwise continue
        if FWhiteStates[State].RunLength<64 then Break else
          begin
            // found a make up code, continue state machine with current bit (rather than reading the next one)
            NewState:=FWhiteStates[0].NewState[Bit];
          end;
      end;
    State:=NewState;
    // address next bit
    FMask:=FMask shr 1;
    if FBitsLeft>0 then Dec(FBitsLeft);
  until False;
end;

//----------------------------------------------------------------------------------------------------------------------

function TCCITTDecoder.NextBit: boolean;
// Reads the current bit and returns True if it is set, otherwise False.
// This method is only used in the process to synchronize the bit stream in descentants.
begin
  // advance to next byte in the input Buffer if necessary
  if (FBitsLeft=0) and (FPackedSize>0) then
    begin
      FBits:=FSource^;
      Inc(FSource);
      Dec(FPackedSize);
      FMask:=$80;
      FBitsLeft:=8;
    end;
  Result:=(FBits and FMask)<>0;
  // address next bit
  FMask:=FMask shr 1;
  if FBitsLeft>0 then Dec(FBitsLeft);
end;

//----------------------------------------------------------------------------------------------------------------------

type
  TCodeEntry = packed record
    Code,Len: cardinal;
  end;

const // CCITT code tables
  WhiteCodes: array[0..103] of TCodeEntry = (
    (Code: $0035; Len: 8),(Code: $0007; Len: 6),(Code: $0007; Len: 4),
    (Code: $0008; Len: 4),(Code: $000B; Len: 4),(Code: $000C; Len: 4),
    (Code: $000E; Len: 4),(Code: $000F; Len: 4),(Code: $0013; Len: 5),
    (Code: $0014; Len: 5),(Code: $0007; Len: 5),(Code: $0008; Len: 5),
    (Code: $0008; Len: 6),(Code: $0003; Len: 6),(Code: $0034; Len: 6),
    (Code: $0035; Len: 6),(Code: $002A; Len: 6),(Code: $002B; Len: 6),
    (Code: $0027; Len: 7),(Code: $000C; Len: 7),(Code: $0008; Len: 7),
    (Code: $0017; Len: 7),(Code: $0003; Len: 7),(Code: $0004; Len: 7),
    (Code: $0028; Len: 7),(Code: $002B; Len: 7),(Code: $0013; Len: 7),
    (Code: $0024; Len: 7),(Code: $0018; Len: 7),(Code: $0002; Len: 8),
    (Code: $0003; Len: 8),(Code: $001A; Len: 8),(Code: $001B; Len: 8),
    (Code: $0012; Len: 8),(Code: $0013; Len: 8),(Code: $0014; Len: 8),
    (Code: $0015; Len: 8),(Code: $0016; Len: 8),(Code: $0017; Len: 8),
    (Code: $0028; Len: 8),(Code: $0029; Len: 8),(Code: $002A; Len: 8),
    (Code: $002B; Len: 8),(Code: $002C; Len: 8),(Code: $002D; Len: 8),
    (Code: $0004; Len: 8),(Code: $0005; Len: 8),(Code: $000A; Len: 8),
    (Code: $000B; Len: 8),(Code: $0052; Len: 8),(Code: $0053; Len: 8),
    (Code: $0054; Len: 8),(Code: $0055; Len: 8),(Code: $0024; Len: 8),
    (Code: $0025; Len: 8),(Code: $0058; Len: 8),(Code: $0059; Len: 8),
    (Code: $005A; Len: 8),(Code: $005B; Len: 8),(Code: $004A; Len: 8),
    (Code: $004B; Len: 8),(Code: $0032; Len: 8),(Code: $0033; Len: 8),
    (Code: $0034; Len: 8),(Code: $001B; Len: 5),(Code: $0012; Len: 5),
    (Code: $0017; Len: 6),(Code: $0037; Len: 7),(Code: $0036; Len: 8),
    (Code: $0037; Len: 8),(Code: $0064; Len: 8),(Code: $0065; Len: 8),
    (Code: $0068; Len: 8),(Code: $0067; Len: 8),(Code: $00CC; Len: 9),
    (Code: $00CD; Len: 9),(Code: $00D2; Len: 9),(Code: $00D3; Len: 9),
    (Code: $00D4; Len: 9),(Code: $00D5; Len: 9),(Code: $00D6; Len: 9),
    (Code: $00D7; Len: 9),(Code: $00D8; Len: 9),(Code: $00D9; Len: 9),
    (Code: $00DA; Len: 9),(Code: $00DB; Len: 9),(Code: $0098; Len: 9),
    (Code: $0099; Len: 9),(Code: $009A; Len: 9),(Code: $0018; Len: 6),
    (Code: $009B; Len: 9),(Code: $0008; Len: 11),(Code: $000C; Len: 11),
    (Code: $000D; Len: 11),(Code: $0012; Len: 12),(Code: $0013; Len: 12),
    (Code: $0014; Len: 12),(Code: $0015; Len: 12),(Code: $0016; Len: 12),
    (Code: $0017; Len: 12),(Code: $001C; Len: 12),(Code: $001D; Len: 12),
    (Code: $001E; Len: 12),(Code: $001F; Len: 12));
    // EOL codes are added "manually"

  BlackCodes: array[0..103] of TCodeEntry = (
    (Code: $0037; Len: 10),(Code: $0002; Len: 3),(Code: $0003; Len: 2),
    (Code: $0002; Len: 2),(Code: $0003; Len: 3),(Code: $0003; Len: 4),
    (Code: $0002; Len: 4),(Code: $0003; Len: 5),(Code: $0005; Len: 6),
    (Code: $0004; Len: 6),(Code: $0004; Len: 7),(Code: $0005; Len: 7),
    (Code: $0007; Len: 7),(Code: $0004; Len: 8),(Code: $0007; Len: 8),
    (Code: $0018; Len: 9),(Code: $0017; Len: 10),(Code: $0018; Len: 10),
    (Code: $0008; Len: 10),(Code: $0067; Len: 11),(Code: $0068; Len: 11),
    (Code: $006C; Len: 11),(Code: $0037; Len: 11),(Code: $0028; Len: 11),
    (Code: $0017; Len: 11),(Code: $0018; Len: 11),(Code: $00CA; Len: 12),
    (Code: $00CB; Len: 12),(Code: $00CC; Len: 12),(Code: $00CD; Len: 12),
    (Code: $0068; Len: 12),(Code: $0069; Len: 12),(Code: $006A; Len: 12),
    (Code: $006B; Len: 12),(Code: $00D2; Len: 12),(Code: $00D3; Len: 12),
    (Code: $00D4; Len: 12),(Code: $00D5; Len: 12),(Code: $00D6; Len: 12),
    (Code: $00D7; Len: 12),(Code: $006C; Len: 12),(Code: $006D; Len: 12),
    (Code: $00DA; Len: 12),(Code: $00DB; Len: 12),(Code: $0054; Len: 12),
    (Code: $0055; Len: 12),(Code: $0056; Len: 12),(Code: $0057; Len: 12),
    (Code: $0064; Len: 12),(Code: $0065; Len: 12),(Code: $0052; Len: 12),
    (Code: $0053; Len: 12),(Code: $0024; Len: 12),(Code: $0037; Len: 12),
    (Code: $0038; Len: 12),(Code: $0027; Len: 12),(Code: $0028; Len: 12),
    (Code: $0058; Len: 12),(Code: $0059; Len: 12),(Code: $002B; Len: 12),
    (Code: $002C; Len: 12),(Code: $005A; Len: 12),(Code: $0066; Len: 12),
    (Code: $0067; Len: 12),(Code: $000F; Len: 10),(Code: $00C8; Len: 12),
    (Code: $00C9; Len: 12),(Code: $005B; Len: 12),(Code: $0033; Len: 12),
    (Code: $0034; Len: 12),(Code: $0035; Len: 12),(Code: $006C; Len: 13),
    (Code: $006D; Len: 13),(Code: $004A; Len: 13),(Code: $004B; Len: 13),
    (Code: $004C; Len: 13),(Code: $004D; Len: 13),(Code: $0072; Len: 13),
    (Code: $0073; Len: 13),(Code: $0074; Len: 13),(Code: $0075; Len: 13),
    (Code: $0076; Len: 13),(Code: $0077; Len: 13),(Code: $0052; Len: 13),
    (Code: $0053; Len: 13),(Code: $0054; Len: 13),(Code: $0055; Len: 13),
    (Code: $005A; Len: 13),(Code: $005B; Len: 13),(Code: $0064; Len: 13),
    (Code: $0065; Len: 13),(Code: $0008; Len: 11),(Code: $000C; Len: 11),
    (Code: $000D; Len: 11),(Code: $0012; Len: 12),(Code: $0013; Len: 12),
    (Code: $0014; Len: 12),(Code: $0015; Len: 12),(Code: $0016; Len: 12),
    (Code: $0017; Len: 12),(Code: $001C; Len: 12),(Code: $001D; Len: 12),
    (Code: $001E; Len: 12),(Code: $001F; Len: 12));
    // EOL codes are added "manually"

procedure TCCITTDecoder.MakeStates;
// creates state arrays for white and black codes
// These state arrays are so designed that they have at each state (starting with state 0) a new state index
// into the same array according to the bit for which the state is current.

  //--------------- local functions -------------------------------------------
  procedure AddCode(var Target: TStateArray; Bits: cardinal; BitLen,RL: integer);
  // interprets the given string as a sequence of bits and makes a state chain from it
  var State,NewState: integer;
      Bit: boolean;
  begin
    // start state
    State:=0;
    // prepare bit combination (bits are given right align, but must be scanned from left)
    Bits:=Bits shl (32-BitLen);
    while BitLen>0 do
      begin
        // determine next state according to the bit string
        asm
          SHL  [Bits],1
          SETC [Bit]
        end;
        NewState:=Target[State].NewState[Bit];
        // Is it a not yet assigned state?
        if NewState=0 then
          begin
            // if yes then create a new state at the end of the array
            NewState:=Length(Target);
            Target[State].NewState[Bit]:=NewState;
            SetLength(Target,Length(Target)+1);
          end;
        State:=NewState;
        Dec(BitLen);
      end;
    // at this point State indicates the final state where we must store the run length for the
    // particular bit combination
    Target[State].RunLength:=RL;
  end;
  //--------------- end local functions ---------------------------------------
var I: integer;
begin
  // set an initial entry in each state array
  SetLength(FWhiteStates,1);
  SetLength(FBlackStates,1);
  // with codes
  for I:=0 to 63 do with WhiteCodes[I] do AddCode(FWhiteStates,Code,Len,I);
  for I:=64 to 103 do with WhiteCodes[I] do AddCode(FWhiteStates,Code,Len,(I-63)*64);
  AddCode(FWhiteStates,1,12,G3_EOL);
  AddCode(FWhiteStates,1,9,G3_INVALID);
  AddCode(FWhiteStates,1,10,G3_INVALID);
  AddCode(FWhiteStates,1,11,G3_INVALID);
  AddCode(FWhiteStates,0,12,G3_INVALID);
  // black codes
  for I:=0 to 63 do with BlackCodes[I] do AddCode(FBlackStates,Code,Len,I);
  for I:=64 to 103 do with BlackCodes[I] do AddCode(FBlackStates,Code,Len,(I-63)*64);
  AddCode(FBlackStates,1,12,G3_EOL);
  AddCode(FBlackStates,1,9,G3_INVALID);
  AddCode(FBlackStates,1,10,G3_INVALID);
  AddCode(FBlackStates,1,11,G3_INVALID);
  AddCode(FBlackStates,0,12,G3_INVALID);
end;

//----------------- TCCITTFax3Decoder ----------------------------------------------------------------------------------

procedure TCCITTFax3Decoder.Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
var
  RunLength: integer;
  EOLCount: integer;
  //--------------- local functions -------------------------------------------
  procedure SynchBOL;
  // synch bit stream to next line start
  var Count: integer;
  begin
    // if no EOL codes have been read so far then do it now
    if EOLCount=0 then
      begin
        // advance until 11 consecutive 0 bits have been found
        Count:=0;
        while (Count<11) and (FPackedSize>0) do
          begin
            if NextBit then Count:=0 else Inc(Count);
          end;
      end;
    // read 8 bit until at least one set bit is found
    repeat
      Count:=0;
      while (Count<8) and (FPackedSize>0) do
        begin
          if NextBit then Count:=9 else Inc(Count);
        end;
    until (Count>8) or (FPackedSize=0);
    // here we are already beyond the set bit and can restart scanning
    EOLCount:=0;
  end;
  //---------------------------------------------------------------------------
  procedure AdjustEOL;
  begin
    FIsWhite:=False;
    if FFreeTargetBits in [1..7] then Inc(FTarget);
    FFreeTargetBits:=8;
    FRestWidth:=FWidth;
  end;
  //--------------- end local functions ---------------------------------------
begin
  // make all bits white
  FillChar(Dest^,UnpackedSize,0);
  // swap all bits here, in order to avoid frequent tests in the main loop
  if FSwapBits then
    asm
           PUSH EBX
           LEA  EBX,ReverseTable
           MOV  ECX,[PackedSize]
           MOV  EDX,[Source]
           MOV  EDX,[EDX]
      @@1:
           MOV  AL,[EDX]
           XLATB //<<<XLAT
           MOV  [EDX],AL
           INC  EDX
           DEC  ECX
           JNZ  @@1
           POP  EBX
    end;
  // setup initial states
  // a row always starts with a (possibly zero-length) white run
  FSource:=Source;
  FBitsLeft:=0;
  FPackedSize:=PackedSize;
  // target preparation
  FTarget:=Dest;
  FRestWidth:=FWidth;
  FFreeTargetBits:=8;
  EOLCount:=0;
  // main loop
  repeat
    // synchronize to start of next line
    SynchBOL;
    // a line always starts with a white run
    FIsWhite:=True;
    // decode one line
    repeat
      if FIsWhite then RunLength:=FindWhiteCode else RunLength:=FindBlackCode;
      if RunLength>=0 then
        begin
          if FillRun(RunLength) then Break;
          FIsWhite:=not FIsWhite;
        end
      else
        if RunLength=G3_EOL then Inc(EOLCount) else Break;
    until (RunLength=G3_EOL) or (FPackedSize=0);
    AdjustEOL;
  {$IFDEF FPC}
  until (FPackedSize=0) or (PChar(FTarget)-PChar(Dest)>=UnpackedSize);
  {$ELSE}
  until (FPackedSize=0) or (FTarget-PChar(Dest)>=UnpackedSize);
  {$ENDIF}
end;

//----------------- TCCITTFax4Decoder ----------------------------------------------------------------------------------

procedure TCCITTFax4Decoder.Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
var
  RunLength: integer;
  EOLCount: integer;
  //--------------- local functions -------------------------------------------
  procedure SynchBOL;
  // synch bit stream to next line start
  var Count: integer;
  begin
    // if no EOL codes have been read so far then do it now
    if EOLCount=0 then
      begin
        // advance until 11 consecutive 0 bits have been found
        Count:=0;
        while (Count<11) and (FPackedSize>0) do
          begin
            if NextBit then Count:=0 else Inc(Count);
          end;
      end;
    // read 8 bit until at least one set bit is found
    repeat
      Count:=0;
      while (Count<8) and (FPackedSize>0) do
        begin
          if NextBit then Count:=9 else Inc(Count);
        end;
    until (Count>8) or (FPackedSize=0);
    // here we are already beyond the set bit and can restart scanning
    EOLCount:=0;
  end;
  //---------------------------------------------------------------------------
  procedure AdjustEOL;
  begin
    FIsWhite:=False;
    if FFreeTargetBits in [1..7] then Inc(FTarget);
    FFreeTargetBits:=8;
    FRestWidth:=FWidth;
  end;
  //--------------- end local functions ---------------------------------------
begin
  // make all bits white
  FillChar(Dest^,UnpackedSize,0);
  // swap all bits here, in order to avoid frequent tests in the main loop
  if FSwapBits then
    asm
           PUSH EBX
           LEA  EBX,ReverseTable
           MOV  ECX,[PackedSize]
           MOV  EDX,[Source]
           MOV  EDX,[EDX]
      @@1:
           MOV  AL,[EDX]
           XLATB //<<<XLAT
           MOV  [EDX],AL
           INC  EDX
           DEC  ECX
           JNZ  @@1
           POP  EBX
    end;
  // setup initial states
  // a row always starts with a (possibly zero-length) white run
  FSource:=Source;
  FBitsLeft:=0;
  FPackedSize:=PackedSize;
  // target preparation
  FTarget:=Dest;
  FRestWidth:=FWidth;
  FFreeTargetBits:=8;
  EOLCount:=0;
  // main loop
  repeat
    // synchronize to start of next line
    SynchBOL;
    // a line always starts with a white run
    FIsWhite:=True;
    // decode one line
    repeat
      if FIsWhite then RunLength:=FindWhiteCode else RunLength:=FindBlackCode;
      if RunLength>=0 then
        begin
          if FillRun(RunLength) then Break;
          FIsWhite:=not FIsWhite;
        end
      else
        if RunLength=G3_EOL then Inc(EOLCount) else Break;
    until (RunLength=G3_EOL) or (FPackedSize=0);
    AdjustEOL;
  {$IFDEF FPC}
  until (FPackedSize=0) or (PChar(FTarget)-PChar(Dest)>=UnpackedSize);
  {$ELSE}
  until (FPackedSize=0) or (FTarget-PChar(Dest)>=UnpackedSize);
  {$ENDIF}
end;

//----------------- TCCITTMHDecoder ------------------------------------------------------------------------------------

procedure TCCITTMHDecoder.Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
var RunLength: integer;
  //--------------- local functions -------------------------------------------
  procedure AdjustEOL;
  begin
    FIsWhite:=False;
    if FFreeTargetBits in [1..7] then Inc(FTarget);
    FFreeTargetBits:=8;
    FRestWidth:=FWidth;
    if FBitsLeft<8 then FBitsLeft:=0; // discard remaining bits
    if FWordAligned and Odd(cardinal(FTarget)) then Inc(FTarget);
  end;
  //--------------- end local functions ---------------------------------------
begin
  // make all bits white
  FillChar(Dest^, UnpackedSize,0);
  // swap all bits here, in order to avoid frequent tests in the main loop
  if FSwapBits then
    asm
           PUSH EBX
           LEA  EBX,ReverseTable
           MOV  ECX,[PackedSize]
           MOV  EDX,[Source]
           MOV  EDX,[EDX]
      @@1:
           MOV  AL,[EDX]
           XLATB //<<<XLATB
           MOV  [EDX],AL
           INC  EDX
           DEC  ECX
           JNZ  @@1
           POP  EBX
    end;

  // setup initial states
  // a row always starts with a (possibly zero-length) white run
  FIsWhite:=True;
  FSource:=Source;
  FBitsLeft:=0;
  FPackedSize:=PackedSize;
  // target preparation
  FTarget:=Dest;
  FRestWidth:=FWidth;
  FFreeTargetBits:=8;
  // main loop
  repeat
    if FIsWhite then RunLength:=FindWhiteCode else RunLength:=FindBlackCode;
    if RunLength>0 then
      if FillRun(RunLength) then AdjustEOL;
    FIsWhite:=not FIsWhite;
  until FPackedSize=0;
end;

//----------------- TLZ77Decoder ---------------------------------------------------------------------------------------

constructor TLZ77Decoder.Create(FlushMode: integer; AutoReset: boolean);
begin
  FillChar(FStream,sizeof(FStream),0);
  FFlushMode:=FlushMode;
  FAutoReset:=AutoReset;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TLZ77Decoder.Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
begin
  FStream.NextInput:=Source;
  FStream.AvailableInput:=PackedSize;
  if FAutoReset then FZLibResult:=InflateReset(FStream);
  if FZLibResult=Z_OK then
    begin
      FStream.NextOutput:=Dest;
      FStream.AvailableOutput:=UnpackedSize;
      FZLibResult:=Inflate(FStream,FFlushMode);
      // advance pointers so used input can be calculated
      Source:=FStream.NextInput;
      Dest:=FStream.NextOutput;
    end;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TLZ77Decoder.DecodeEnd;
begin
  if InflateEnd(FStream)<0 then CompressionError(20{gesLZ77Error});
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TLZ77Decoder.DecodeInit;
begin
  if InflateInit(FStream)<0 then CompressionError(20{gesLZ77Error});
end;

//----------------------------------------------------------------------------------------------------------------------

function TLZ77Decoder.GetAvailableInput: integer;
begin
  Result:=FStream.AvailableInput;
end;

//----------------------------------------------------------------------------------------------------------------------

function TLZ77Decoder.GetAvailableOutput: integer;
begin
  Result:=FStream.AvailableOutput;
end;

//----------------- TThunderDecoder ------------------------------------------------------------------------------------

// ThunderScan uses an encoding scheme designed for 4-bit pixel values.  Data is encoded in bytes, with
// each byte split into a 2-bit code word and a 6-bit data value.  The encoding gives raw data, runs of
// pixels, or pixel values encoded as a delta from the previous pixel value.  For the latter, either 2-bit
// or 3-bit delta values are used, with the deltas packed into a single byte.

const
  THUNDER_DATA       = $3F;   // mask for 6-bit data
  THUNDER_CODE       = $C0;   // mask for 2-bit code word
  // code values
  THUNDER_RUN        = 0;     // run of pixels w/ encoded count
  THUNDER_2BITDELTAS = $40;   // 3 pixels w/ encoded 2-bit deltas
  DELTA2_SKIP        = 2;     // skip code for 2-bit deltas
  THUNDER_3BITDELTAS = $80;   // 2 pixels w/ encoded 3-bit deltas
  DELTA3_SKIP        = 4;     // skip code for 3-bit deltas
  THUNDER_RAW        = $C0;   // raw data encoded

  TwoBitDeltas: array[0..3] of integer = (0,1,0,-1);
  ThreeBitDeltas: array[0..7] of integer = (0,1,2,3,0,-3,-2,-1);

constructor TThunderDecoder.Create(Width: cardinal);
begin
  FWidth:=Width;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TThunderDecoder.Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
var SourcePtr,TargetPtr: PByte;
    LastPixel,N,Delta: integer;
    NPixels: cardinal;
    //--------------- local function --------------------------------------------
    procedure SetPixel(Delta: integer);
    begin
      Lastpixel:=Delta and $0F;
      if Odd(NPixels) then
        begin
          TargetPtr^:=TargetPtr^ or LastPixel;
          Inc(TargetPtr);
        end
      else TargetPtr^:=LastPixel shl 4;
      Inc(NPixels);
    end;
    //--------------- end local function ----------------------------------------
begin
  SourcePtr:=Source;
  TargetPtr:=Dest;
  while UnpackedSize>0 do
    begin
      LastPixel:=0;
      NPixels:=0;
      // Usually Width represents the byte number of a strip, but the thunder
      // algo is only defined for 4 bits per pixel formats where 2 pixels take up
      // one byte.
      while (PackedSize>0) and (NPixels<2*FWidth) do
        begin
          N:=SourcePtr^;
          Inc(SourcePtr);
          Dec(PackedSize);
          case N and THUNDER_CODE of
            THUNDER_RUN:
              // pixel run, replicate the last pixel n times, where n is the lower-order 6 bits
              begin
                if Odd(NPixels) then
                  begin
                    TargetPtr^:=TargetPtr^ or Lastpixel;
                    Lastpixel:=TargetPtr^;
                    Inc(TargetPtr);
                    Inc(NPixels);
                    Dec(N);
                  end
                else LastPixel:=LastPixel or LastPixel shl 4;
                Inc(NPixels, N);
                while N>0 do
                  begin
                    TargetPtr^:=LastPixel;
                    Inc(TargetPtr);
                    Dec(N, 2);
                  end;
                if N = -1 then
                  begin
                    Dec(TargetPtr);
                    TargetPtr^:=TargetPtr^ and $F0;
                  end;
                LastPixel:=LastPixel and $0F;
              end;
            THUNDER_2BITDELTAS: // 2-bit deltas
              begin
                Delta:=(N shr 4) and 3;
                if Delta<>DELTA2_SKIP then SetPixel(LastPixel+TwoBitDeltas[Delta]);
                Delta:=(N shr 2) and 3;
                if Delta<>DELTA2_SKIP then SetPixel(LastPixel+TwoBitDeltas[Delta]);
                Delta:=N and 3;
                if Delta<>DELTA2_SKIP then SetPixel(LastPixel+TwoBitDeltas[Delta]);
              end;
            THUNDER_3BITDELTAS: // 3-bit deltas
              begin
                Delta:=(N shr 3) and 7;
                if Delta<>DELTA3_SKIP then SetPixel(LastPixel+ThreeBitDeltas[Delta]);
                Delta:=N and 7;
                if Delta<>DELTA3_SKIP then SetPixel(LastPixel+ThreeBitDeltas[Delta]);
              end;
            THUNDER_RAW: // raw data
              SetPixel(N);
          end;
        end;
      Dec(UnpackedSize,FWidth);
    end;
end;

//----------------- TPCDDecoder ----------------------------------------------------------------------------------------

constructor TPCDDecoder.Create(Stream: PStream);
begin
  FStream:=Stream;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TPCDDecoder.Decode(var Source,Dest: pointer; PackedSize,UnpackedSize: integer);
// recovers the Huffman encoded luminance and chrominance deltas
// Note: This decoder leaves a bit the way like the other decoders work.
//       Source points to an array of 3 pointers, one for luminance (Y, Luma), one for blue
//       chrominance (Cb, Chroma1) and one for red chrominance (Cr, Chroma2). These pointers
//       point to source and target at the same time (in place decoding).
//       PackedSize contains the width of the current subimage and UnpackedSize its height.
//       Dest is not used and can be nil.
type
  PPointerArray = ^TPointerArray;
  TPointerArray = array[0..2] of pointer;
  PPCDTable = ^TPCDTable;
  TPCDTable = packed record
    Length: byte;
    Sequence: cardinal;
    Key: byte;
    Mask: integer;
  end;
  PQuantumArray = ^TQuantumArray;
  TQuantumArray = array[0..3*256-1] of byte;
var
  Luma,Chroma1,Chroma2: PChar; // hold the actual pointers, PChar to easy pointer maths
  Width,Height: cardinal;
  PCDTable: array[0..2] of PPCDTable;
  I,J,K: cardinal;
  R: PPCDTable;
  RangeLimit: PQuantumArray;
  P,Q,Buffer: PChar;
  Accumulator,Bits,Length,Plane,Row: cardinal;
  PCDLength: array[0..2] of cardinal;

  //--------------- local function --------------------------------------------
  procedure PCDGetBits(N: cardinal);
  begin
    Accumulator:=Accumulator shl N;
    Dec(Bits,N);
    while Bits<=24 do
      begin
        if P>=(Buffer+$800) then
          begin
            FStream.Read(Buffer^,$800);
            P:=Buffer;
          end;
        Accumulator:=Accumulator or (cardinal(P^) shl (24-Bits));
        Inc(Bits,8);
        Inc(P);
      end;
  end;
  //--------------- end local function ----------------------------------------
var Limit: cardinal;
begin
  // place the used source values into local variables with proper names to make
  // their usage clearer
  Luma:=PPointerArray(Source)[0];
  Chroma1:=PPointerArray(Source)[1];
  Chroma2:=PPointerArray(Source)[2];
  Width:=PackedSize;
  Height:=UnpackedSize;
  // initialize Huffman tables
  ZeroMemory(@PCDTable,sizeof(PCDTable));
  GetMem(Buffer,$800);
  try
    Accumulator:=0;
    Bits:=32;
    P:=Buffer+$800;
    Limit:=1;
    if Width>1536 then Limit:=3;
    for I:=0 to Limit-1 do
      begin
        PCDGetBits(8);
        Length:=(Accumulator and $FF)+1;
        GetMem(PCDTable[I],Length*sizeof(TPCDTable));
        R:=PCDTable[I];
        for J:=0 to Length-1 do
          begin
            PCDGetBits(8);
            R.Length:=(Accumulator and $FF)+1;
            if R.Length>16 then
              begin
                if Assigned(Buffer) then FreeMem(Buffer);
                for K:=0 to 2 do
                if Assigned(PCDTable[K]) then FreeMem(PCDTable[K]);
                Exit;
              end;
            PCDGetBits(16);
            R.Sequence:=(Accumulator and $FFFF) shl 16;
            PCDGetBits(8);
            R.Key:=Accumulator and $FF;
            asm
              // R.Mask:=not ((1 shl (32-R.Length))-1);
              // asm implementation to avoid overflow errors and for faster execution
              MOV EDX,[R]
              MOV CL,32
              SUB CL,[EDX].TPCDTable.Length
              MOV EAX,1
              SHL EAX,CL
              DEC EAX
              NOT EAX
              MOV [EDX].TPCDTable.Mask,EAX
            end;
            Inc(R);
          end;
        PCDLength[I]:=Length;
      end;
    // initialize range limits
    GetMem(RangeLimit,3*256);
    try
      for I:=0 to 255 do
        begin
          RangeLimit[I]:=0;
          RangeLimit[I+256]:=I;
          RangeLimit[I+2*256]:=255;
        end;
      Inc(PByte(RangeLimit),255);
      // search for sync byte
      PCDGetBits(16);
      PCDGetBits(16);
      while (Accumulator and $00FFF000)<>$00FFF000 do PCDGetBits(8);
      while (Accumulator and $FFFFFF00)<>$FFFFFE00 do PCDGetBits(1);
      // recover the Huffman encoded luminance and chrominance deltas
      Length:=0;
      Plane:=0;
      Q:=Luma;
      repeat
        if (Accumulator and $FFFFFF00)=$FFFFFE00 then
          begin
            // determine plane and row number
            PCDGetBits(16);
            Row:=(Accumulator shr 9) and $1FFF;
            if Row=Height then Break;
            PCDGetBits(8);
            Plane:=Accumulator shr 30;
            PCDGetBits(16);
            case Plane of
              0: Q:=Luma+Row*Width;
              2: begin
                   Q:=Chroma1+(Row shr 1)*Width;
                   Dec(Plane);
                 end;
              3: begin
                   Q:=Chroma2+(Row shr 1)*Width;
                   Dec(Plane);
                 end;
            else Abort; // invalid/corrupt image
            end;
            Length:=PCDLength[Plane];
            Continue;
          end;
        // decode luminance or chrominance deltas
        R:=PCDTable[Plane];
        I:=0;
        while (I<Length) and ((Accumulator and R.Mask)<>R.Sequence) do
          begin
            Inc(I);
            Inc(R);
          end;
        if R=nil then
          begin
            // corrupt PCD image, skipping to sync byte
            while (Accumulator and $00FFF000)<>$00FFF000 do PCDGetBits(8);
            while (Accumulator and $FFFFFF00)<>$FFFFFE00 do PCDGetBits(1);
            Continue;
          end;
        if R.Key<128 then Q^:=Char(RangeLimit[ClampByte(Byte(Q^)+R.Key)]) else Q^:=Char(RangeLimit[ClampByte(Byte(Q^)+R.Key-256)]);
        Inc(Q);
        PCDGetBits(R.Length);
      until False;
    finally
      for I:=0 to 2 do if Assigned(PCDTable[I]) then FreeMem(PCDTable[I]);
      Dec(PByte(RangeLimit), 255);
      if Assigned(RangeLimit) then FreeMem(RangeLimit);
    end;
  finally
    if Assigned(Buffer) then FreeMem(Buffer);
  end;
end;

{$IFDEF GIF_MMX}
initialization
  mmxSupported := GetCPUType >= [ cpuMMX ];
{$ENDIF}

end.

