kolmck/Addons/KOLGraphicCompression.pas

2168 lines
74 KiB
ObjectPascal

{$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,
KolZLibBzip {$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: TZStreamRec;
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.next_in := Source;
FStream.avail_in := PackedSize;
if FAutoReset then FZLibResult:=InflateReset(FStream);
if FZLibResult=Z_OK then
begin
FStream.next_out:=Dest;
FStream.avail_out:=UnpackedSize;
FZLibResult:=Inflate(FStream,FFlushMode);
// advance pointers so used input can be calculated
Source:=FStream.next_in;
Dest:=FStream.next_out;
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.avail_in;
end;
//----------------------------------------------------------------------------------------------------------------------
function TLZ77Decoder.GetAvailableOutput: integer;
begin
Result:=FStream.avail_out;
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.