unit GraphicCompression; {$TYPEDADDRESS OFF} // The contents of this file are subject to the Mozilla Public License // Version 1.1 (the "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ // // Software distributed under the License is distributed on an "AS IS" basis, // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the // specific language governing rights and limitations under the License. // // The original code is GraphicColor.pas, released November 1, 1999. // // The initial developer of the original code is Dipl. Ing. Mike Lischke (Pleißa, Germany, www.delphi-gems.com), // // Portions created by Dipl. Ing. Mike Lischke are Copyright // (C) 1999-2003 Dipl. Ing. Mike Lischke. All Rights Reserved. //---------------------------------------------------------------------------------------------------------------------- // This file is part of the image library GraphicEx. // // 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) // //---------------------------------------------------------------------------------------------------------------------- interface {$I GraphicConfiguration.inc} uses Windows, Classes, SysUtils, Graphics, JPG, // JPEG compression support MZLib; // general inflate/deflate and LZ77 compression support type // abstract decoder class to define the base functionality of an encoder/decoder TDecoder = class public procedure Decode(var Source, Dest: Pointer; PackedSize, UnpackedSize: Integer); virtual; abstract; procedure DecodeEnd; virtual; procedure DecodeInit; virtual; procedure Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); virtual; abstract; procedure EncodeInit; virtual; procedure EncodeEnd; 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) TTargaRLEDecoder = class(TDecoder) private FColorDepth: Cardinal; public constructor Create(ColorDepth: Cardinal); procedure Decode(var Source, Dest: Pointer; PackedSize, UnpackedSize: Integer); override; procedure Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); override; end; {$ifdef UseLZW} // Lempel-Ziff-Welch encoder/decoder class // TIFF LZW compression / decompression is a bit different to the common LZW code TTIFFLZWDecoder = class(TDecoder) public procedure Decode(var Source, Dest: Pointer; PackedSize, UnpackedSize: Integer); override; procedure Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); override; end; {$endif} // UseLZW TPackbitsRLEDecoder = class(TDecoder) public procedure Decode(var Source, Dest: Pointer; PackedSize, UnpackedSize: Integer); override; procedure Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); override; end; TPCXRLEDecoder = class(TDecoder) public procedure Decode(var Source, Dest: Pointer; PackedSize, UnpackedSize: Integer); override; procedure Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); override; end; TSGIRLEDecoder = class(TDecoder) private FSampleSize: Byte; // 8 or 16 bits public constructor Create(SampleSize: Byte); procedure Decode(var Source, Dest: Pointer; PackedSize, UnpackedSize: Integer); override; procedure Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); override; end; TCUTRLEDecoder = class(TDecoder) public procedure Decode(var Source, Dest: Pointer; PackedSize, UnpackedSize: Integer); override; procedure Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); override; end; TPSPRLEDecoder = class(TDecoder) public procedure Decode(var Source, Dest: Pointer; PackedSize, UnpackedSize: Integer); override; procedure Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); override; end; {$ifdef UseLZW} // 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. TGIFLZWDecoder = class(TDecoder) private FInitialCodeSize: Byte; public constructor Create(InitialCodeSize: Byte); procedure Decode(var Source, Dest: Pointer; PackedSize, UnpackedSize: Integer); override; procedure Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); override; end; {$endif} // UseLZW TRLADecoder = class(TDecoder) public procedure Decode(var Source, Dest: Pointer; PackedSize, UnpackedSize: Integer); override; procedure Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); override; end; TStateEntry = record NewState: array[Boolean] of Cardinal; RunLength: Integer; end; TStateArray = array of TStateEntry; TCCITTDecoder = class(TDecoder) 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; TCCITTFax3Decoder = class(TCCITTDecoder) public procedure Decode(var Source, Dest: Pointer; PackedSize, UnpackedSize: Integer); override; procedure Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); override; end; TCCITTMHDecoder = class(TCCITTDecoder) // modified Huffman RLE public procedure Decode(var Source, Dest: Pointer; PackedSize, UnpackedSize: Integer); override; procedure Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); override; end; TLZ77Decoder = class(TDecoder) 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); override; procedure DecodeEnd; override; procedure DecodeInit; override; procedure Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); override; property AvailableInput: Integer read GetAvailableInput; property AvailableOutput: Integer read GetAvailableOutput; property ZLibResult: Integer read FZLibResult; end; TTIFFJPEGDecoder = class; TJPEGGeneral = packed record case byte of 0: (common: jpeg_common_struct); 1: (d: jpeg_decompress_struct); 2: (c: jpeg_compress_struct); end; PJPEGState = ^TJPEGState; TJPEGState = record General: TJPEGGeneral; // must be the first member here because we pass TJPEGState as // compress, decompress or common struct around to be able // to access our internal data Error: jpeg_error_mgr; // libjpeg error manager DestinationManager: jpeg_destination_mgr; // data dest for compression SourceManager: jpeg_source_mgr; // data source for decompression HSampling, // luminance sampling factors VSampling: Word; BytesPerLine: Cardinal; // decompressed bytes per scanline RawBuffer: Pointer; // source data RawBufferSize: Cardinal; // pointers to intermediate buffers when processing downsampled data DownSampleBuffer: array[0..MAX_COMPONENTS - 1] of JSAMPARRAY; ScanCount, // number of 'scanlines' accumulated SamplesPerClump: Integer; JPEGTables: Pointer; // JPEGTables tag value, or nil JTLength: Cardinal; // number of bytes JPEGTables JPEGQuality, // compression quality level JPEGTablesMode: Integer; // what to put in JPEGTables end; TTIFFJPEGDecoder = class(TDecoder) private FState: TJPEGState; FImageProperties: Pointer; // anonymously declared because I cannot take GraphicEx.pas in the uses clause above public constructor Create(Properties: Pointer); procedure Decode(var Source, Dest: Pointer; PackedSize, UnpackedSize: Integer); override; procedure DecodeInit; override; procedure DecodeEnd; override; procedure Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); override; end; TThunderDecoder = class(TDecoder) private FWidth: Cardinal; // width of a scanline in pixels public constructor Create(Width: Cardinal); procedure Decode(var Source, Dest: Pointer; PackedSize, UnpackedSize: Integer); override; procedure Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); override; end; TPCDDecoder = class(TDecoder) private FStream: TStream; // decoder must read some data public constructor Create(Stream: TStream); procedure Decode(var Source, Dest: Pointer; PackedSize, UnpackedSize: Integer); override; procedure Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); override; end; //---------------------------------------------------------------------------------------------------------------------- implementation uses Math, GraphicEx, GraphicStrings, GraphicColor; const // LZW encoding and decoding support NoLZWCode = 4096; type EGraphicCompression = class(Exception); //---------------------------------------------------------------------------------------------------------------------- procedure CompressionError(ErrorString: String); overload; begin raise EGraphicCompression.Create(ErrorString); end; //----------------- 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; //---------------------------------------------------------------------------------------------------------------------- procedure TDecoder.EncodeEnd; // called after all compression has been done begin end; //---------------------------------------------------------------------------------------------------------------------- procedure TDecoder.EncodeInit; // called before any compression 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: Cardinal; 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; //---------------------------------------------------------------------------------------------------------------------- function GetPixel(P: PByte; BPP: Byte): Cardinal; // Retrieves a pixel value from a Buffer. The actual size and order of the bytes is not important // since we are only using the value for comparisons with other pixels. begin Result := P^; Inc(P); Dec(BPP); while BPP > 0 do begin Result := Result shl 8; Result := Result or P^; Inc(P); Dec(BPP); end; end; //---------------------------------------------------------------------------------------------------------------------- function CountDiffPixels(P: PByte; BPP: Byte; Count: Integer): Integer; // counts pixels in Buffer until two identical adjacent ones found var N: Integer; Pixel, NextPixel: Cardinal; begin N := 0; NextPixel := 0; // shut up compiler if Count = 1 then Result := Count else begin Pixel := GetPixel(P, BPP); while Count > 1 do begin Inc(P, BPP); NextPixel := GetPixel(P, BPP); if NextPixel = Pixel then Break; Pixel := NextPixel; Inc(N); Dec(Count); end; if NextPixel = Pixel then Result := N else Result := N + 1; end; end; //---------------------------------------------------------------------------------------------------------------------- function CountSamePixels(P: PByte; BPP: Byte; Count: Integer): Integer; var Pixel, NextPixel: Cardinal; begin Result := 1; Pixel := GetPixel(P, BPP); Dec(Count); while Count > 0 do begin Inc(P, BPP); NextPixel := GetPixel(P, BPP); if NextPixel <> Pixel then Break; Inc(Result); Dec(Count); end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TTargaRLEDecoder.Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); // Encodes "Count" bytes pointed to by Source into the Buffer supplied with Target and returns the // number of bytes stored in Target. BPP denotes bytes per pixel color depth. // Note: The target Buffer must provide enough space to hold the compressed data. Using a size of // twice the size of the input Buffer is sufficent. var DiffCount, // pixel count until two identical SameCount: Integer; // number of identical adjacent pixels SourcePtr, TargetPtr: PByte; BPP: Integer; begin BytesStored := 0; SourcePtr := Source; TargetPtr := Dest; BytesStored := 0; // +1 for 15 bits to get the correct 2 bytes per pixel BPP := (FColorDepth + 1) div 8; while Count > 0 do begin DiffCount := CountDiffPixels(SourcePtr, BPP, Count); SameCount := CountSamePixels(SourcePtr, BPP, Count); if DiffCount > 128 then DiffCount := 128; if SameCount > 128 then SameCount := 128; if DiffCount > 0 then begin // create a raw packet TargetPtr^ := DiffCount - 1; Inc(TargetPtr); Dec(Count, DiffCount); Inc(BytesStored, (DiffCount * BPP) + 1); while DiffCount > 0 do begin TargetPtr^ := SourcePtr^; Inc(SourcePtr); Inc(TargetPtr); if BPP > 1 then begin TargetPtr^ := SourcePtr^; Inc(SourcePtr); Inc(TargetPtr); end; if BPP > 2 then begin TargetPtr^ := SourcePtr^; Inc(SourcePtr); Inc(TargetPtr); end; if BPP > 3 then begin TargetPtr^ := SourcePtr^; Inc(SourcePtr); Inc(TargetPtr); end; Dec(DiffCount); end; end; if SameCount > 1 then begin // create a RLE packet TargetPtr^ := (SameCount - 1) or $80; Inc(TargetPtr); Dec(Count, SameCount); Inc(BytesStored, BPP + 1); Inc(SourcePtr, (SameCount - 1) * BPP); TargetPtr^ := SourcePtr^; Inc(SourcePtr); Inc(TargetPtr); if BPP > 1 then begin TargetPtr^ := SourcePtr^; Inc(SourcePtr); Inc(TargetPtr); end; if BPP > 2 then begin TargetPtr^ := SourcePtr^; Inc(SourcePtr); Inc(TargetPtr); end; if BPP > 3 then begin TargetPtr^ := SourcePtr^; Inc(SourcePtr); Inc(TargetPtr); end; end; end; end; //----------------- TTIFFLZWDecoder ------------------------------------------------------------------------------------ {$ifdef UseLZW} 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: Cardinal; CodeMask: Cardinal; FreeCode: Cardinal; OldCode: Cardinal; Prefix: array[0..4095] of Cardinal; // LZW prefix Suffix, // LZW suffix Stack: array [0..4095] of Byte; // stack StackPointer: PByte; 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; //---------------------------------------------------------------------------------------------------------------------- procedure TTIFFLZWDecoder.Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); begin end; {$endif} // UseLZW //----------------- 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; //---------------------------------------------------------------------------------------------------------------------- procedure TPackbitsRLEDecoder.Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); begin 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; //---------------------------------------------------------------------------------------------------------------------- procedure TPCXRLEDecoder.Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); begin 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 := 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; //---------------------------------------------------------------------------------------------------------------------- procedure TSGIRLEDecoder.Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); begin 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; //---------------------------------------------------------------------------------------------------------------------- procedure TCUTRLEDecoder.Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); begin 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; //---------------------------------------------------------------------------------------------------------------------- procedure TPSPRLEDecoder.Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); begin end; //----------------- TGIFLZWDecoder ------------------------------------------------------------------------------------- {$ifdef UseLZW} constructor TGIFLZWDecoder.Create(InitialCodeSize: Byte); begin FInitialCodeSize := InitialCodeSize; end; //---------------------------------------------------------------------------------------------------------------------- procedure TGIFLZWDecoder.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: Cardinal; CodeMask: Cardinal; FreeCode: Cardinal; OldCode: Cardinal; Prefix: array[0..4095] of Cardinal; // LZW prefix Suffix, // LZW suffix Stack: array [0..4095] of Byte; // stack StackPointer: PByte; Target: PByte; FirstChar: Byte; // Buffer for decoded byte ClearCode, EOICode: Word; begin Target := Dest; 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 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 (UnpackedSize > 0) and (PackedSize > 0) do begin // 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]; 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); Target^ := StackPointer^; Inc(Target); Dec(UnpackedSize); until StackPointer = @Stack; end; Inc(SourcePtr); Dec(PackedSize); end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TGIFLZWDecoder.Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); begin end; {$endif} //----------------- 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; //---------------------------------------------------------------------------------------------------------------------- procedure TRLADecoder.Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); begin 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 := 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 begin NewState := FBlackStates[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.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 = 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] XLAT // (Only) Delphi 6 needs XLATB here. 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; until (FPackedSize = 0) or (integer(FTarget) - integer(Dest) >= UnpackedSize); end; //---------------------------------------------------------------------------------------------------------------------- procedure TCCITTFax3Decoder.Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); begin 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] XLAT // (Only) Delphi 6 needs XLATB here. 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; //---------------------------------------------------------------------------------------------------------------------- procedure TCCITTMHDecoder.Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); begin 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(gesLZ77Error); end; //---------------------------------------------------------------------------------------------------------------------- procedure TLZ77Decoder.DecodeInit; begin if InflateInit(FStream) < 0 then CompressionError(gesLZ77Error); end; //---------------------------------------------------------------------------------------------------------------------- procedure TLZ77Decoder.Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); begin end; //---------------------------------------------------------------------------------------------------------------------- function TLZ77Decoder.GetAvailableInput: Integer; begin Result := FStream.AvailableInput; end; //---------------------------------------------------------------------------------------------------------------------- function TLZ77Decoder.GetAvailableOutput: Integer; begin Result := FStream.AvailableOutput; end; //----------------- TTIFFJPEGDecoder --------------------------------------------------------------------------------------- // Libjpeg interface layer needed to provide access from the JPEG coder class. // This routine is invoked only for warning messages, since error_exit does its own thing // and trace_level is never set > 0. procedure Internaljpeg_output_message(cinfo: j_common_ptr); var Buffer: array[0..JMSG_LENGTH_MAX] of AnsiChar; State: PJPEGState; Msg: string; begin State := Pointer(cinfo); State.Error.format_message(@State.General.common, Buffer); Msg := Copy(String(PAnsiChar(@Buffer)), 1, JMSG_LENGTH_MAX); MessageBox(0, PChar(Msg), PChar(gesWarning), MB_OK or MB_ICONWARNING); end; //---------------------------------------------------------------------------------------------------------------------- { procedure Internaljpeg_create_compress(var State: TJPEGState); begin // initialize JPEG error handling State.General.Common.err := @State.Error; State.Error.output_message := Internaljpeg_output_message; jpeg_createCompress(@State.General.c, JPEG_LIB_VERSION, SizeOf(State.General.c)); end; } //---------------------------------------------------------------------------------------------------------------------- // JPEG library source data manager. These routines supply compressed data to libjpeg. procedure std_init_source(cinfo: j_decompress_ptr); var State: PJPEGState; begin State := Pointer(cinfo); State.SourceManager.next_input_byte := State.RawBuffer; State.SourceManager.bytes_in_buffer := State.RawBufferSize; end; //---------------------------------------------------------------------------------------------------------------------- procedure std_fill_input_buffer(cinfo: j_decompress_ptr); const Dummy_EOI: array[0..1] of JOCTET = ($FF, JPEG_EOI); var State: PJPEGState; begin State := Pointer(cinfo); // Should never get here since entire strip/tile is read into memory before the // decompressor is called, and thus was supplied by init_source. MessageBox(0, PChar(gesJPEGEOI), PChar(gesWarning), MB_OK or MB_ICONWARNING); // insert a fake EOI marker State.SourceManager.next_input_byte := @Dummy_EOI; State.SourceManager.bytes_in_buffer := 2; end; //---------------------------------------------------------------------------------------------------------------------- procedure std_skip_input_data(cinfo: j_decompress_ptr; num_bytes: Integer); var State: PJPEGState; begin State := Pointer(cinfo); if num_bytes > 0 then begin if num_bytes > State.SourceManager.bytes_in_buffer then begin // oops, buffer overrun std_fill_input_buffer(cinfo); end else begin Inc(State.SourceManager.next_input_byte, num_bytes); Dec(State.SourceManager.bytes_in_buffer, num_bytes); end; end; end; //---------------------------------------------------------------------------------------------------------------------- procedure std_term_source(cinfo: j_decompress_ptr); // No work necessary here. begin end; //---------------------------------------------------------------------------------------------------------------------- procedure Internaljpeg_data_src(var State: TJPEGState); begin with State do begin // set data source manager General.d.src := @SourceManager; // fill in fields in our data source manager SourceManager.init_source := @std_init_source; SourceManager.fill_input_buffer := @std_fill_input_buffer; SourceManager.skip_input_data := @std_skip_input_data; SourceManager.resync_to_restart := @jpeg_resync_to_restart; SourceManager.term_source := @std_term_source; SourceManager.bytes_in_buffer := 0; // for safety SourceManager.next_input_byte := nil; end; end; //---------------------------------------------------------------------------------------------------------------------- // Alternate source manager for reading from JPEGTables. // We can share all the code except for the init routine. procedure tables_init_source(cinfo: j_decompress_ptr); var State: PJPEGState; begin State := Pointer(cinfo); State.SourceManager.next_input_byte := State.JPEGTables; State.SourceManager.bytes_in_buffer := State.JTLength; end; //---------------------------------------------------------------------------------------------------------------------- procedure Internaljpeg_tables_src(var State: TJPEGState); begin Internaljpeg_data_src(State); State.SourceManager.init_source := @tables_init_source; end; //---------------------------------------------------------------------------------------------------------------------- constructor TTIFFJPEGDecoder.Create(Properties: Pointer); begin FImageProperties := Properties; with PImageProperties(Properties)^ do begin if Assigned(JPEGTables) then begin FState.JPEGTables := @JPEGTables[0]; FState.JTLength := Length(JPEGTables); end; // no else branch, rely on class initialization end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TTIFFJPEGDecoder.Decode(var Source, Dest: Pointer; PackedSize, UnpackedSize: Integer); type PCompInfoArray = ^TCompInfoArray; TCompInfoArray = array[0..MAX_COMPONENTS - 1] of jpeg_component_info; const // also defined in GraphicEx, but not publicitly PLANARCONFIG_CONTIG = 1; PLANARCONFIG_SEPARATE = 2; var I, J: Integer; SegmentWidth, SegmentHeight: Cardinal; Temp: Integer; Target: PByte; begin // Reset decoder state from any previous strip/tile, in case application didn't read the whole strip. jpeg_abort(@FState.General.Common); FState.RawBuffer := Source; FState.RawBufferSize := PackedSize; // Read the header for this strip/tile. jpeg_read_header(@FState.General, True); with PImageProperties(FImageProperties)^ do begin // Check image parameters and set decompression parameters. if ioTiled in Options then begin // tiled images currently not supported SegmentWidth := TileWidth; SegmentHeight := Height; BytesPerLine := 0; //TIFFTileRowSize(tif); end else begin SegmentWidth := Width; SegmentHeight := Height - CurrentRow; // I assume here that all strips are equally sized if SegmentHeight > RowsPerStrip[0] then SegmentHeight := RowsPerStrip[0]; end; FState.BytesPerLine := BytesPerLine; if (PlanarConfig = PLANARCONFIG_SEPARATE) and (CurrentStrip = StripCount) then begin // For PC 2, scale down the expected strip/tile size to match a downsampled component SegmentWidth := (SegmentWidth + Cardinal(FState.HSampling - 1)) div FState.HSampling; SegmentHeight := (SegmentHeight + Cardinal(FState.VSampling - 1)) div FState.VSampling; end; if (FState.General.d.image_width <> SegmentWidth) or (FState.General.d.image_height <> SegmentHeight) then CompressionError(gesJPEGStripSize); Temp := 1; if PlanarConfig = PLANARCONFIG_CONTIG then Temp := SamplesPerPixel; if FState.General.d.num_components <> Temp then CompressionError(gesJPEGComponentCount); if FState.General.d.data_precision <> BitsPerSample then CompressionError(gesJPEGDataPrecision); if PlanarConfig = PLANARCONFIG_CONTIG then begin // component 0 should have expected sampling factors if (FState.General.d.comp_info.h_samp_factor <> FState.HSampling) or (FState.General.d.comp_info.v_samp_factor <> FState.VSampling) then CompressionError(gesJPEGSamplingFactors); // rest should have sampling factors 1,1 for I := 1 to FState.General.d.num_components - 1 do with PCompInfoArray(FState.General.d.comp_info)[I] do begin if (h_samp_factor <> 1) or (v_samp_factor <> 1) then CompressionError(gesJPEGSamplingFactors); end; end else begin // PC 2's single component should have sampling factors 1,1 if (FState.General.d.comp_info.h_samp_factor <> 1) or (FState.General.d.comp_info.v_samp_factor <> 1) then CompressionError(gesJPEGSamplingFactors); end; // Since libjpeg can convert YCbCr data to RGB (actually BGR) on the fly I let do // it this conversion instead handling it by the color manager. if ColorScheme = csYCbCr then FState.General.d.jpeg_color_space := JCS_YCbCr else FState.General.d.jpeg_color_space := JCS_UNKNOWN; FState.General.d.out_color_space := JCS_RGB; FState.General.d.raw_data_out := False; // Start JPEG decompressor jpeg_start_decompress(@FState.General); try Target := Dest; // data is expected to be read in multiples of a scanline J := Cardinal(UnpackedSize) div FState.BytesPerLine; if (Cardinal(UnpackedSize) mod FState.BytesPerLine) <> 0 then CompressionError(gesJPEGFractionalLine); while J > 0 do begin // jpeg_read_scanlines needs as target an array of pointers, but since we read only one lin // at a time we can simply pass the address of the pointer to the data if jpeg_read_scanlines(@FState.General.d, @Target, 1) <> 1 then Exit; Inc(Target, FState.BytesPerLine); Dec(J); end; finally jpeg_finish_decompress(@FState.General.d); end end; end; //---------------------------------------------------------------------------------------------------------------------- procedure TTIFFJPEGDecoder.DecodeEnd; begin // release libjpeg resources jpeg_destroy(@FState.General.Common); end; //---------------------------------------------------------------------------------------------------------------------- procedure TTIFFJPEGDecoder.DecodeInit; begin // initialize JPEG error handling FState.Error := jpeg_std_error; FState.General.d.common.err := @FState.Error; FState.Error.output_message := Internaljpeg_output_message; // let JPEG library init the core structure before setting our own stuff jpeg_createDecompress(@FState.General.d, JPEG_LIB_VERSION, SizeOf(FState.General.d)); with PImageProperties(FImageProperties)^ do begin if {(ColorScheme = csYCbCr) and} Assigned(YCbCrSubsampling) then begin FState.HSampling := YCbCrSubsampling[0]; FState.VSampling := YCbCrSubsampling[1]; end else begin // TIFF 6.0 forbids subsampling of all other color spaces FState.HSampling := 1; FState.VSampling := 1; end; end; // default values for codec-specific fields with FState do begin // Default IJG quality JPEGQuality := 75; end; if Assigned(FState.JPEGTables) then begin Internaljpeg_tables_src(FState); if jpeg_read_header(@FState.General, False) <> JPEG_HEADER_TABLES_ONLY then CompressionError(gesJPEGBogusTableField); end; Internaljpeg_data_src(FState); end; //---------------------------------------------------------------------------------------------------------------------- procedure TTIFFJPEGDecoder.Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); begin 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; N, Delta: Integer; NPixels: Cardinal; LastPixel: Integer; //--------------- 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; //---------------------------------------------------------------------------------------------------------------------- procedure TThunderDecoder.Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); begin end; //----------------- TPCDDecoder ---------------------------------------------------------------------------------------- constructor TPCDDecoder.Create(Stream: TStream); 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 = record Length: Byte; Sequence: Cardinal; Key: Byte; Mask: Integer; end; PQuantumArray = ^TQuantumArray; TQuantumArray = array[0..3 * 256 - 1] of Byte; var Luma, Chroma1, Chroma2: PByte; // 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: PByte; 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.ReadBuffer(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^ := RangeLimit[ClampByte(Q^ + R.Key)] else Q^ := RangeLimit[ClampByte(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; //---------------------------------------------------------------------------------------------------------------------- procedure TPCDDecoder.Encode(Source, Dest: Pointer; Count: Cardinal; var BytesStored: Cardinal); begin end; //---------------------------------------------------------------------------------------------------------------------- end.