Browse Source

* LZW stream based on implementation in fpreadtiff

Michaël Van Canneyt 2 years ago
parent
commit
68b092a466

+ 1 - 0
packages/fcl-base/fpmake.pp

@@ -84,6 +84,7 @@ begin
         end;
     T:=P.Targets.AddUnit('iostream.pp');
     T:=P.Targets.AddUnit('chainstream.pp');
+    T:=P.Targets.AddUnit('lzwstream.pp');
     T:=P.Targets.AddUnit('nullstream.pp');
       T.ResourceStrings:=true;
     T:=P.Targets.AddUnit('maskutils.pp');

+ 446 - 0
packages/fcl-base/src/lzwstream.pp

@@ -0,0 +1,446 @@
+{ **********************************************************************
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2022 by the Free Pascal development team
+
+    LZW stream (reader) based on implementation of fpReadTiff
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+unit LZWStream;
+
+
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils;
+
+Type
+  ELZWStreamError = Class(EStreamError);
+
+  TLZWStreamOption = (zoTIFFCodes);
+  TLZWStreamOptions = Set of TLZWStreamOption;
+
+type
+  TLZWString = packed record
+    Count: integer;
+    Data: PByte;
+    ShortData: array[0..3] of byte;
+  end;
+  TLZWStringArray = Array of TLZWString;
+
+const
+  // TIFF Codes
+  ClearCode = 256; // clear table, start with 9bit codes
+  EoiCode = 257;   // end of input
+  NoCode = $7fff;
+  InitialBitLength = 9;
+  MaxBitLength = 12;
+
+  DefaultBufSize = 1024;
+
+Type
+  { TLZWStream }
+
+  TLZWStream = class(TOwnerStream)
+  Private
+    FSkipCodes : Word;
+    Foptions: TLZWStreamOptions;
+    FCodeBuffer: DWord;
+    FCodeBufferLength: byte;
+    FCurBitLength: byte;
+    FTable: TLZWStringArray;
+    FTableCount: integer;
+    FOldCode: Word;
+    FBigEndian: boolean;
+    FTableMargin: byte;
+  Protected
+    procedure ClearTable;
+    procedure InitializeTable;
+    function CodeToIndex(aCode: word): Integer; inline;
+    function IsInTable(aCode: word): boolean;
+    procedure Error(const Msg: string);
+    procedure AddStringToTable(aCode, AddFirstCharFromCode: integer);
+  Public
+    Constructor Create(aSource : TStream; aOptions : TLZWStreamOptions); virtual;
+    Destructor Destroy; override;
+    Property Options: TLZWStreamOptions Read Foptions Write Foptions;
+    Property TableCount : Integer Read FTableCount;
+  end;
+
+  { TLZWDecompressionStream }
+
+  TLZWDecompressionStream = class(TLZWStream)
+  Private
+    FInBuffer : Array[Byte] of Byte;
+    FInRead : Word;
+    FInSize : Word;
+    FOutBuffer : Array of byte;
+    FOutSize : Cardinal;
+    FOutRead : Cardinal;
+    FFirstRead : Boolean;
+    procedure MoveFromBuffer(Dest: PByte; aCount: Integer);
+  Protected
+    function ReadByte(out aByte: Byte): Boolean;
+    Function BufferCapacity : Integer;
+    Function BufferSize : Integer;
+    Function BytesInBuffer : Integer;
+    function GetNextCode: Word;
+    procedure WriteStringFromCode(aCode: integer; AddFirstChar: boolean = false);
+    Procedure FillInBuffer;
+    Procedure FillOutBuffer;
+  Public
+    Constructor Create(aSource : TStream; aOptions : TLZWStreamOptions; InitialBufsize : Word = DefaultBufSize); overload;
+    function Read(var Buffer; Count: Longint): Longint; override;
+  end;
+
+  TLZWCompressionStream = class(TStream)
+    // Todo
+  end;
+
+implementation
+
+procedure TLZWStream.Error(const Msg: string);
+begin
+  Raise ELZWStreamError.Create(Msg);
+end;
+
+
+procedure TLZWStream.ClearTable;
+var
+  i: Integer;
+begin
+  for i:=0 to TableCount-1 do
+    if FTable[i].Data <> @FTable[i].ShortData then
+      ReAllocMem(FTable[i].Data,0);
+  FTableCount:=0;
+end;
+
+procedure TLZWStream.InitializeTable;
+begin
+  FCurBitLength:=InitialBitLength;
+  ClearTable;
+end;
+
+function TLZWStream.CodeToIndex(aCode: word): Integer;
+begin
+  Result:=aCode-FSkipCodes;
+end;
+
+function TLZWStream.IsInTable(aCode: word): boolean;
+begin
+  Result:=(CodeToIndex(aCode)<TableCount);
+end;
+
+constructor TLZWStream.Create(aSource: TStream; aOptions: TLZWStreamOptions);
+
+
+begin
+  Inherited Create(aSource);
+  FOptions:=aOptions;
+  FSkipCodes:=256;
+  if zoTIFFCodes in Options then
+    FSkipCodes:=FSkipCodes+2;
+  SetLength(FTable,4096-1-FSkipCodes);
+  FCurBitLength:=9;
+  FCodeBufferLength := 0;
+  FCodeBuffer := 0;
+  FTableCount:=0;
+  FOldCode := NoCode;
+end;
+
+destructor TLZWStream.Destroy;
+begin
+  ClearTable;
+  inherited Destroy;
+end;
+
+procedure TLZWStream.AddStringToTable(aCode, AddFirstCharFromCode: integer);
+// add string from code plus first character of string from code as new string
+var
+  s1, s2: TLZWString;
+  p: PByte;
+  NewCount: integer;
+begin
+  // WriteLn('AddStringToTable Code=',aCode,' FCFCode=',AddFirstCharFromCode,' TableCount=',TableCount);
+  //check whether can store more codes or not
+  if TableCount=Length(FTable) then exit;
+  // find string 1
+  if aCode<256 then
+    begin
+    // string is byte
+    s1.ShortData[0] := acode;
+    s1.Data:[email protected];
+    s1.Count:=1;
+    end
+  else if (aCode>=FSkipCodes) then
+    begin
+    // normal string
+    if CodeToIndex(aCode)>=TableCount then
+      Error('LZW code out of bounds');
+    s1:=FTable[CodeToindex(aCode)];
+    end
+  else
+    Error('LZW code out of bounds');
+  // find string 2
+  if AddFirstCharFromCode<256 then begin
+    // string is byte
+    s2.ShortData[0] := AddFirstCharFromCode;
+    s2.Data:[email protected];
+    s2.Count:=1;
+  end else begin
+    // normal string
+    if CodeToIndex(AddFirstCharFromCode)>=TableCount then
+      Error('LZW code out of bounds');
+    s2:=FTable[CodeToIndex(AddFirstCharFromCode)];
+  end;
+  // set new FTable entry
+  NewCount := s1.Count+1;
+  FTable[TableCount].Count:= NewCount;
+  if NewCount > 4 then
+  begin
+    p:=nil;
+    GetMem(p,NewCount);
+  end else
+    p := @FTable[TableCount].ShortData;
+  FTable[TableCount].Data:=p;
+  System.Move(s1.Data^,p^,s1.Count);
+  // add first character from string 2
+  p[s1.Count]:=s2.Data^;
+  // increase TableCount
+  inc(FTableCount);
+  case TableCount+FSkipCodes+FTableMargin of
+  512,1024,2048: begin
+      //check if there is room for a greater code
+      if FCurBitLength<MaxBitLength then
+        inc(FCurBitLength);
+    end;
+  end;
+end;
+
+function TLZWDecompressionStream.BufferCapacity: Integer;
+begin
+  Result:=Length(FOutBuffer);
+end;
+
+function TLZWDecompressionStream.BufferSize: Integer;
+begin
+  Result:=FOutSize;
+end;
+
+function TLZWDecompressionStream.BytesInBuffer: Integer;
+begin
+  Result:=FOutSize-FOutRead;
+end;
+
+
+function TLZWDecompressionStream.GetNextCode : Word;
+
+Var
+  B : Byte;
+
+begin
+  while FCodeBufferLength<FCurBitLength do
+    begin
+    if not ReadByte(B) then
+      Exit(EoiCode);
+    // Writeln('Byte: ',B);
+    If FBigEndian then
+      FCodeBuffer:=(FCodeBuffer shl 8) or B
+    else
+      FCodeBuffer:=FCodeBuffer or (DWord(B) shl FCodeBufferLength);
+    Inc(FCodeBufferLength, 8);
+    end;
+
+  if FBigEndian then
+    begin
+    result := FCodeBuffer shr (FCodeBufferLength-FCurBitLength);
+    Dec(FCodeBufferLength, FCurBitLength);
+    FCodeBuffer := FCodeBuffer and ((1 shl FCodeBufferLength) - 1);
+    end
+  else
+    begin
+    result := FCodeBuffer and ((1 shl FCurBitLength)-1);
+    Dec(FCodeBufferLength, FCurBitLength);
+    FCodeBuffer := FCodeBuffer shr FCurBitLength;
+    end;
+end;
+
+procedure TLZWDecompressionStream.WriteStringFromCode(aCode: integer; AddFirstChar: boolean = false);
+var
+  s: TLZWString;
+  i : Integer;
+
+begin
+  // WriteLn('WriteStringFromCode Code=',aCode,' AddFirstChar=',AddFirstChar);
+  if aCode<256 then
+    begin
+    // write byte
+    s.ShortData[0] := acode;
+    s.Data:[email protected];
+    s.Count:=1;
+    end
+  else if (aCode>=FSkipCodes) then
+    begin
+    // write string
+    if CodeToIndex(aCode)>=TableCount then
+      Error('LZW code out of bounds');
+    s:=FTable[CodeToIndex(aCode)];
+    end
+  else
+    Error('LZW code out of bounds');
+  // Grow buffer
+  if (FOutSize+S.Count+1>BufferCapacity) then
+    SetLength(FOutBuffer,BufferCapacity*2+8);
+  System.Move(s.Data^,FoutBuffer[FOutSize],s.Count);
+  // System.Write(S.Count,':');for i:=0 to s.Count-1 do system.write(' ',HexStr(FoutBuffer[FOutSize+i],2)); // debug
+  inc(FOutSize,s.Count);
+  if AddFirstChar then
+    begin
+    FOutBuffer[FOutSize]:=S.Data^;
+    // System.write('+ ',HexStr(FoutBuffer[FOutSize],2)); // debug
+    inc(FOutSize);
+    end;
+  // writeln(',WriteStringFromCode'); // debug
+end;
+
+
+procedure TLZWDecompressionStream.FillOutBuffer;
+
+Var
+  lCode,FillCapacity : Word;
+
+begin
+  // Determine a number of bytes we want to read
+  FOutSize:=0;
+  FOutRead:=0;
+  FillCapacity:=BufferCapacity-8;
+  if FFirstRead and (zoTIFFCodes in FOptions) then
+    begin
+    FillInBuffer;
+    if (FInSize>0) and (FInBuffer[0]=$80) then
+    begin
+      FBigEndian := true; //endian-ness of LZW is not necessarily consistent with the rest of the file
+      FTableMargin := 1; //keep one free code to be able to write EOI code
+    end else
+    begin
+      FBigEndian := false;
+      FTableMargin := 0;
+    end;
+    FFirstRead:=False;
+    end;
+  repeat
+    lCode:=GetNextCode;
+    // WriteLn('DecompressLZW Code=',lCode);
+    if lCode=EoiCode then
+      break;
+    if lCode=ClearCode then
+      begin
+      InitializeTable;
+      lCode:=GetNextCode;
+      // WriteLn('DecompressLZW after clear Code=',lCode);
+      if lCode=EoiCode then
+        break;
+      if lCode=ClearCode then
+        Error('LZW code out of bounds');
+      WriteStringFromCode(lCode);
+      FOldCode:=lCode;
+      end
+    else
+      begin
+      if IsInTable(lCode) then
+        begin
+        // Writeln(lCode,' in Table (',TableCount,')');
+        WriteStringFromCode(lCode);
+        if FOldCode <> NoCode then
+          AddStringToTable(FOldCode,lCode);
+        FOldCode:=lCode;
+        end
+      else if {(Code=TableCount+258) and} (FOldCode <> NoCode) then
+        begin
+        // Writeln(lCode,' not yet in Table (',TableCount,')');
+        WriteStringFromCode(FOldCode,true);
+        AddStringToTable(FOldCode,FOldCode);
+        FOldCode:=lCode;
+        end
+      else
+        Error('LZW code out of bounds');
+      end;
+  until FOutSize>=FillCapacity;
+end;
+
+constructor TLZWDecompressionStream.Create(aSource: TStream;
+  aOptions: TLZWStreamOptions; InitialBufsize: Word);
+begin
+  inherited Create(aSource, aOptions);
+  SetLength(FOutBuffer,InitialBufsize);
+  FOutSize:=0;
+  FOutRead:=0;
+  FFirstRead:=True;
+end;
+
+procedure TLZWDecompressionStream.MoveFromBuffer(Dest : PByte; aCount : Integer);
+
+begin
+  Move(FOutBuffer[FOutRead],Dest^,aCount);
+  Inc(FOutRead,aCount);
+end;
+
+Procedure TLZWDecompressionStream.FillInBuffer;
+
+begin
+  FInSize:=Source.Read(FInBuffer,SizeOf(FInBuffer));
+  FInRead:=0;
+end;
+
+function TLZWDecompressionStream.ReadByte(out aByte: Byte): Boolean;
+begin
+  if FinRead>=FInSize then
+    FillInBuffer;
+  Result:=FInRead<FInSize;
+  if Result then
+    begin
+    aByte:=FInBuffer[FInRead];
+    inc(FInRead);
+    end;
+end;
+
+function TLZWDecompressionStream.Read(var Buffer; Count: Longint): Longint;
+
+Var
+  Dest : PByte;
+  aMoveSize: Integer;
+
+begin
+  Result:=0;
+  if Count=0 then
+    exit;
+  Dest:=@Buffer;
+  if (BytesInBuffer=0) then
+    FillOutBuffer;
+  While (Count>0) and (BytesInBuffer>0) do
+    begin
+    aMoveSize:=BytesInBuffer;
+    if aMoveSize>Count then
+      aMoveSize:=Count;
+    MoveFromBuffer(Dest,aMoveSize);
+    Inc(Dest,aMoveSize);
+    Inc(Result,aMoveSize);
+    Dec(Count,aMoveSize);
+    if Count>0 then // we need more data
+      FillOutBuffer;
+    end;
+end;
+
+
+end.
+

+ 36 - 0
packages/fcl-base/tests/createlzwtest.pas

@@ -0,0 +1,36 @@
+program createlzwtest;
+{
+  Small program that converts TIFF LZW compressed data to uncompressed data.
+  (assumes the fpreadtiff DecodeLZW is correct)
+  
+}
+
+{$mode objfpc}
+{$h+}
+
+uses sysutils,classes,fpreadtiff;
+
+Var
+  M : TBytesStream;
+  F : TFileStream;
+  B : PByte;
+  aSize : PtrInt;
+
+begin
+  If ParamCount<>2 then
+    begin
+    Writeln('Usage : ',ExtractFileName(ParamStr(0)),' compressedfile uncompressedfile');
+    Halt(1);
+    end;
+  M:=TBytesStream.Create([]);
+  try
+    M.LoadFromFile(Paramstr(1));
+    DecompressLZW(M.Memory,M.Size,B,aSize);
+    F:=TFileStream.Create(ParamStr(2),fmCreate);
+    F.WriteBuffer(B^,aSize);
+  finally
+    F.Free;
+    M.Free;
+  end;
+end.
+

+ 1 - 1
packages/fcl-base/tests/fclbase-unittests.pp

@@ -4,7 +4,7 @@ program fclbase_unittests;
 
 uses
   Classes, consoletestrunner, tests_fptemplate, tchashlist,
-  testexprpars, tcmaskutils, tcinifile, tccsvreadwrite,tcbufferedfilestream, tccsvdocument, utcchainstream;
+  testexprpars, tcmaskutils, tcinifile, tccsvreadwrite,tcbufferedfilestream, tccsvdocument, utcchainstream, utclzw;
 
 var
   Application: TTestRunner;

+ 52 - 0
packages/fcl-base/tests/filec.inc

@@ -0,0 +1,52 @@
+
+Const
+  filec : packed Array[0..824] of byte = (
+     128,  3,224, 80, 56, 36, 22, 13,  7,132, 66, 97, 80,184,100, 54, 16,
+      11,135,  3,192, 96,192, 48, 46, 44,  9,  6, 68, 32,192,176,100,102,
+      45, 31,144, 71,192,128,192, 76,  8,  6, 11,140, 71, 99,160, 96, 44,
+      36,  0,  4,  6,204, 64,224,144,  4, 32, 21, 37,130, 68,226,128,201,
+     108,110, 87, 35,  3,  3,166,242, 25,  8, 16, 20, 14,  2,  3,169, 84,
+     186, 20, 30,143, 76,168, 84, 65,192,136, 16, 54,152,  1,  1, 82,193,
+     160, 56,216,  4,  2,  7,  6, 85,129,192,200, 61, 88,  5,  5,  3, 86,
+     105, 64,216, 56, 18,213, 83,183,212,170, 22,112,  0, 50,181, 53,132,
+       0,  0,244,171, 32, 62,147, 99,130,  2,235, 48, 48,  0, 32, 29,103,
+       7,221,105, 64, 26,228, 12, 11, 94,198,131,193, 32,224, 60, 31, 12,
+      14,158,206,105,119,216, 54, 78,207,118,  2, 94,  1,245,107,100, 12,
+      12,  1,210,232,233, 88,136, 78,146,  6, 11,190, 90,  1,208, 77,116,
+      14,236, 14,212,131,238,219,144,120, 31,120, 15,160,210,128,150, 90,
+      92,106, 11,176,182,  0,178,187, 77,196, 23,125,166,165,230, 96,192,
+      10, 87, 27, 97,128,130,207, 32,123, 88, 23, 80, 28,  1,230,114,224,
+      96, 48, 52, 24, 14,  8,195,107, 54,128, 28, 94, 71,  9,128,  5,232,
+     170,188,220, 15,150,  7,236,164, 66,105, 62,174,190,114, 16,238, 32,
+      74, 91,152,  0,186, 72, 59,168,  5, 41,240, 43, 70,  6,175,224, 67,
+     229,  0,191,206, 99,126,231, 53,104, 74,178,156, 32, 79,235,105, 12,
+      62,109,226,214,130, 47,224, 19,220,131, 41, 32, 24,  6,225, 32,201,
+     139,122,213,193,238,196, 82,250, 33, 49, 58,149,  5,  1,224, 43,190,
+     227,182, 40, 28, 93, 14,160,140,152, 28,251, 59,172,188,118,130,128,
+      76, 67,211, 20,180,171, 20, 61,  8,187,113,130, 18,203,129, 74,116,
+     134,235,129,  9,136, 26,195, 72, 15,155, 88,211,191, 40, 40,  6,252,
+      42,113, 26, 37, 46,169,241, 28, 84,196,173, 79, 16, 31, 33,201,176,
+     155,178,165, 42,136, 51, 13,  2,202,146,180,176,130,172, 74,187,140,
+     130,128,179,200, 29, 14,  1,234, 58,122,235,202, 45,163, 82,  0, 63,
+      12,228,217, 30, 33,113,178,149, 17,168, 45,252, 52,238,200,106,176,
+      16,142,191, 19,220, 73, 48, 61,211,  2,153, 56,187,109,204,190,165,
+      39, 20, 91, 85, 55, 72,138, 84,212,213, 75, 48,204,114,129, 38,243,
+     195,232,224,193,200, 80, 10,181, 53, 42, 10,192,149, 44, 79,116,206,
+     215,169,105, 45, 77,  0, 33, 49,243,192,247,212, 12, 13, 94,255,198,
+      11, 21, 10,151, 48,214, 59, 84,249, 70, 86,124, 22,206,189,182, 28,
+     156,151, 41,114,  2,147, 38,  1,244,154, 10,  0, 39,174,227, 30,234,
+     160, 96, 35,134,130,169,232, 20,108,223,176,214, 77,126,130, 54,239,
+     101,193, 83,161,210, 82,  6,172,198,151, 21, 13, 45, 68, 12, 90,240,
+       6, 89, 40, 18,237, 99,174,212,  4, 86,  7, 50, 32,109,165, 10, 89,
+     112,146, 28,224,129,201,172, 79, 55, 58,245, 91,128,236, 44, 85,147,
+      14,154,219,240,163,150,195, 65,248,219, 40,251,182,118,101,181,135,
+     161,111,195,134,163,225, 72,146,246,165,  1, 72,168, 22,  3,  1, 79,
+      96,  6,  0, 75,147,251, 50,  0, 45, 64, 16,  1, 25,  1,139,194,146,
+       0,220,139,216,  5,  5, 58,234, 66, 94,225, 70,154,  4,216,  2,226,
+     137, 98, 27,153,172,234,242, 15,153,174, 80,170,164,130, 47, 76, 88,
+      15,174, 43, 64, 42,255, 96,160,155, 34,165,152,177,240,140,124,168,
+      83, 72, 60,100,161,100,232,142,236,194, 34,202,224,  0,  5,168, 17,
+     166,238,133, 68,219,252,158,166, 76, 92, 23, 13,195,241, 28, 59,110,
+     169,241, 60,111, 29,199,161,148,116,187,200,114,156,175, 28,181,111,
+     220,183, 53,205,161, 75,179,213,206,116, 29, 10, 13, 19,221,125, 23,
+      77,211,190, 61, 63, 85,199, 32, 32);

+ 186 - 0
packages/fcl-base/tests/filed.inc

@@ -0,0 +1,186 @@
+
+Const
+  filed : packed Array[0..3104] of byte = (
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 11, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,  3, 12,  6, 11,
+      11, 11,  9, 12, 11, 15, 15, 15, 15, 15, 15, 15, 11, 12, 12, 11, 11,
+      11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,  4, 12,  9, 15, 15,  3,
+      11,  9, 12, 12, 12, 12, 12,  6,  5, 15, 15, 15, 15, 15, 15, 15, 15,
+      15,  0,  4, 13, 13, 13,  7,  9,  0, 15, 15, 15, 15, 15, 15, 15, 15,
+      10,  9, 15, 15, 15, 15, 15,  3, 12, 12,  6, 12,  5, 15, 15, 15, 15,
+      15, 15, 15, 11, 12, 12,  6,  4, 12,  6, 14, 10,  9, 11, 11, 11, 11,
+      11, 11, 11, 11, 11, 11,  4, 10, 14,  4, 14, 14, 14, 14, 14, 14, 14,
+      10, 15, 15, 15, 15, 15, 15, 15, 10, 14, 14, 14, 14, 14, 14, 14, 14,
+      14, 14, 14, 14, 14, 14, 14, 14, 14,  8, 15, 15, 13, 14, 14, 14, 14,
+       1,  2, 14, 14, 14, 13,  3, 15, 15, 15, 15, 15, 15, 11,  1,  1,  7,
+      12, 13, 14, 14, 12, 15, 15, 15, 15, 15, 15, 15, 13, 14,  2, 15, 15,
+      15, 15, 15,  6,  2, 14, 14, 14, 13, 15, 15, 15, 15, 15, 15, 15,  4,
+       2, 14, 14, 14,  8,  2, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+      14, 14, 14, 14,  2, 15,  0, 12, 14, 14, 14, 13,  0, 15, 15, 15, 15,
+      15, 15, 15, 15, 15,  0,  7, 14, 14, 12, 15, 15,  4, 14, 14, 12, 15,
+      15, 15, 15, 11,  2, 14, 15, 15, 15,  0,  8, 14,  2, 15, 15,  0, 12,
+      14, 14,  1,  3, 15, 15, 15, 15,  5,  1,  1,  3, 15, 15, 15,  9, 14,
+       7, 15, 15, 15, 15, 15, 15, 15,  8, 14, 14,  5, 15, 15, 15, 15, 15,
+      15,  3, 14, 14, 14, 12, 15, 15, 15, 15, 15, 15, 15, 15,  9, 14,  2,
+      15, 12, 14,  4,  0, 15, 15, 15, 13, 14, 13, 15, 15, 15, 15,  6,  1,
+      13, 15, 15, 15, 13, 14, 14,  2, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 13, 14, 13, 15, 15, 15, 11, 14, 14, 12, 15, 15, 15, 15, 15,
+       6, 14, 15, 15, 15, 15, 13, 14, 13, 15, 15, 15, 15, 12, 14, 14, 13,
+      15, 15, 15, 15, 12, 14, 13, 15, 15, 15, 15, 15,  7, 13, 15, 15, 15,
+      15, 15, 15,  6, 14, 14, 14,  4, 15, 15, 15, 15, 15, 15, 15, 13, 14,
+      14, 14, 11, 15, 15, 15, 15, 15, 15, 15, 11, 14, 13, 15,  2,  7, 15,
+      15, 15, 15, 15, 13, 14, 13, 15, 15, 15, 15, 15,  7, 13, 15, 15, 15,
+       6, 14, 14, 14,  5, 15, 15, 15, 15, 15, 15, 15, 15, 15,  0, 14, 14,
+      11, 15, 15, 15, 11, 14, 14, 12, 15, 15, 15, 15, 15, 15, 12,  5, 15,
+      15, 15, 13, 14, 13, 15, 15, 15, 15,  0, 14, 14,  1, 15, 15, 15, 15,
+      13, 14,  7, 15, 15, 15, 15, 15,  3,  6, 15, 15, 15, 15, 15, 15,  7,
+       8,  8, 14,  2, 15, 15, 15, 15, 15, 15, 15, 13,  1, 14, 14,  1,  3,
+      15, 15, 15, 15, 15, 15,  0, 14, 12, 15, 11,  0, 15, 15, 15, 15, 15,
+      13, 14, 13, 15, 15, 15, 15, 15, 11,  6, 15, 15, 15, 15,  1, 14, 14,
+       4, 15, 15, 15, 15, 15, 15, 15, 15, 15,  4, 14,  2, 15, 15, 15, 15,
+      11, 14, 14, 12, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 13, 14,
+      13, 15, 15, 15, 15, 15, 14, 14, 14, 15, 15, 15, 15, 13, 14,  1,  5,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,  0, 14, 10, 10, 14, 14,
+       5, 15, 15, 15, 15, 15, 15, 13, 13,  4, 14, 14,  8,  0, 15, 15, 15,
+      15, 15, 15, 14, 12, 15, 15, 15, 15, 15, 15, 15, 15, 13, 14, 13, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15,  7, 14, 14,  2, 15, 15, 15,
+      15, 15, 15, 15, 15, 15,  2, 14,  9, 15, 15, 15, 15, 11, 14, 14, 12,
+      15, 15, 15, 15, 13,  9, 15, 15, 15, 15, 15, 13, 14, 13, 15, 15, 15,
+      15, 15, 14, 14, 13, 15, 15, 15, 15,  4, 14, 14,  2,  3, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15,  4, 14,  3,  3, 14, 14,  4, 15, 15, 15,
+      15, 15, 15, 13, 13, 15,  7, 14, 14,  2,  0, 15, 15, 15, 15, 15, 14,
+      12, 15, 15, 15, 15, 15, 15, 15, 15, 13, 14, 13, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15,  3, 14, 14, 14,  5, 15, 15, 15, 15, 15, 15,
+      15,  5, 14,  1, 15, 15, 15, 15, 15, 11, 14, 14, 12, 15, 15, 15, 15,
+      14, 12, 15, 15, 15, 15, 15, 13, 14, 13, 15, 15, 15, 15,  9, 14, 14,
+       6, 15, 15, 15, 15,  0,  8, 14, 14, 14, 12, 15, 15, 15, 15, 15, 15,
+      15, 15, 15,  2,  2, 15, 15,  8, 14,  2, 15, 15, 15, 15, 15, 15, 13,
+      13, 15, 15, 13, 14, 14, 13, 15, 15, 15, 15, 15, 14, 12, 15, 15, 15,
+      15, 15, 15, 15, 15, 13, 14, 13, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15,  8, 14, 14, 10, 15, 15, 15, 15, 15, 15, 15, 10, 14, 12,
+      15, 15, 15, 15, 15, 11, 14, 14,  8, 13, 13, 13,  8, 14,  6, 15, 15,
+      15, 15, 15, 13, 14,  2, 15, 15, 15,  6,  1, 14,  4, 15, 15, 15, 15,
+      15, 15,  3,  1, 14, 14, 14,  8,  3, 15, 15, 15, 15, 15, 15,  3, 14,
+       4, 15, 15, 10, 14, 14,  3, 15, 15, 15, 15, 15, 13, 13, 15, 15,  0,
+       2, 14, 14,  7, 15, 15, 15, 15, 14, 12, 15, 15, 15, 15, 15, 15, 15,
+      15, 13, 14, 13, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 12,
+      14, 14,  8, 15, 15, 15, 15, 15, 15, 15,  8, 14,  5, 15, 15, 15, 15,
+      15, 11, 14, 14,  8, 13, 13, 13,  8, 14,  6, 15, 15, 15, 15, 15, 13,
+      14, 14, 14, 14, 14, 14,  1, 11, 15, 15, 15, 15, 15, 15, 15, 15,  5,
+      13, 14, 14, 14, 14,  9, 15, 15, 15, 15, 15, 10, 14,  5, 15, 15, 11,
+      14, 14, 10, 15, 15, 15, 15, 15, 13, 13, 15, 15, 15,  0,  1, 14, 14,
+      12, 15, 15, 15, 14, 12, 15, 15, 15, 15, 15, 15, 15, 15, 13, 14, 13,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,  5, 14, 14, 14,  3,
+      15, 15, 15, 15, 15,  6, 14, 13, 15, 15, 15, 15, 15, 15, 11, 14, 14,
+      12, 15, 15, 15,  0, 14, 12, 15, 15, 15, 15, 15, 13, 14,  8, 12, 12,
+       1, 14, 14, 11, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,  4,  1, 14,
+      14, 14,  3, 15, 15, 15, 15,  1, 14, 14, 14, 14, 14, 14, 14,  8, 15,
+      15, 15, 15, 15, 13, 13, 15, 15, 15, 15,  3,  1, 14, 14,  9, 15, 15,
+      14, 12, 15, 15, 15, 15, 15, 15, 15, 15, 13, 14, 13, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15,  2, 14, 14,  7, 15, 15, 15, 15,
+      15, 13, 14,  6, 15, 15, 15, 15, 15, 15, 11, 14, 14, 12, 15, 15, 15,
+      15, 10,  9, 15, 15, 15, 15, 15, 13, 14, 13, 15, 15,  6, 14, 14,  8,
+       0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,  5,  2, 14, 14, 13, 15,
+      15, 15,  6, 14,  7, 12, 12, 12, 12, 13, 14, 14,  3, 15, 15, 15, 15,
+      13, 13, 15, 15, 15, 15, 15, 11, 14, 14, 14,  9, 15, 14, 12, 15, 15,
+      15, 15, 15, 15, 15, 15, 13, 14, 13, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15,  9, 14, 14,  1, 15, 15, 15, 15,  0, 14,  8, 15,
+      15, 15, 15, 15, 15, 15, 11, 14, 14, 12, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 13, 14, 13, 15, 15, 15, 13, 14, 14, 10, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15,  0,  8, 14,  1, 15, 15, 15, 13, 14,
+       0, 15, 15, 15, 15,  3, 14, 14, 10, 15, 15, 15, 15, 13, 13, 15, 15,
+      15, 15, 15, 15,  9, 14, 14,  1,  3, 14, 12, 15, 15, 15, 15, 15, 15,
+      15, 15, 13, 14, 13, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15,  0, 14, 14, 14,  6, 15, 15, 15,  4, 14, 12, 15, 15, 15, 15, 15,
+      15, 15, 11, 14, 14, 12, 15, 15, 15, 15, 15, 15, 15,  0,  5, 15, 15,
+      13, 14, 13, 15, 15, 15,  5,  1, 14, 14, 11, 15, 15, 15,  4,  4, 15,
+      15, 15, 15, 15, 15, 10, 14, 14, 15, 15,  5, 14, 13, 15, 15, 15, 15,
+      15, 15,  8, 14,  8, 15, 15, 15, 15, 13, 13, 15, 15, 15, 15, 15, 15,
+      15, 12, 14, 14,  1, 14, 12, 15, 15, 15, 15, 15, 15, 15, 15, 13, 14,
+      13, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 13, 14,
+      14, 13, 15, 15, 15,  2, 14,  5, 15, 15, 15, 15, 15, 15, 15, 11, 14,
+      14, 12, 15, 15, 15, 15, 15, 15, 15, 13, 13, 15, 15, 13, 14,  2, 15,
+      15, 15, 15,  4, 14, 14,  1,  0, 15, 15, 12,  8, 15, 15, 15, 15, 15,
+      15, 12, 14,  1, 15, 15, 12, 14,  9, 15, 15, 15, 15, 15, 15,  7, 14,
+      14,  3, 15, 15, 15, 13,  1, 15, 15, 15, 15, 15, 15, 15, 15,  7, 14,
+      14, 14, 12, 15, 15, 15, 15, 15, 15, 15, 15, 13, 14, 13, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,  6, 14, 14, 14,  0, 15,
+       3, 14, 13, 15, 15, 15, 15, 15, 15, 15, 15, 11, 14, 14,  7, 15, 15,
+      15, 15, 15, 15,  6, 14, 12, 15, 15, 13, 14, 14, 15, 15, 15, 15, 15,
+      13, 14, 14,  2,  0, 15,  4, 14, 12, 15, 15, 15, 15, 15,  7, 14,  7,
+      15, 15,  8, 14,  0, 15, 15, 15, 15, 15, 15,  6, 14, 14,  7, 15, 15,
+      15,  1, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 13, 14, 14, 12, 15,
+      15, 15, 15, 15, 15, 15, 15, 13, 14,  1, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15,  1, 14, 14,  4, 15, 10, 14,  9, 15,
+      15, 15, 15, 15, 15, 15,  3,  7, 14, 14, 14, 10,  6, 11, 11,  6, 10,
+       1, 14,  3,  0,  6,  1, 14, 14,  9,  5, 15, 15, 15,  0,  2, 14, 14,
+       2,  0,  3, 14, 14, 12,  0, 15, 15,  4, 14,  1,  0,  5,  7, 14,  2,
+       5, 15, 15, 15, 15, 15, 15, 11, 14, 14, 14,  4,  0,  4, 14, 14,  4,
+       5, 15, 15, 15, 15, 15, 15, 15,  0,  2, 14, 12, 15, 15, 15, 15, 15,
+      15,  5,  6, 14, 14, 14,  6,  5, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15,  7, 14, 14,  2, 15,  1,  1, 15, 15, 15, 15, 15, 15,
+      15,  7, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,  2, 15, 14,
+      14, 14, 14, 14, 14, 14, 15, 15, 15, 15,  0,  7, 14, 14,  1,  7,  7,
+      14, 14, 14, 14, 14, 14, 13,  5,  4, 14, 14, 14, 14, 14,  9, 15, 15,
+      15, 15,  7, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,  9, 15, 15,
+      15, 15, 15, 15, 15,  5,  1, 12, 15, 15, 15, 15, 15,  9, 14, 14, 14,
+      14, 14, 14, 14, 11, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+       3, 14, 14, 14, 10, 14,  7, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15,  0, 11, 11,  3, 15,  0, 11,  4, 12,
+       6,  5, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15,  3,  3, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,  8, 14, 14,
+      14, 14,  3, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 12, 14, 14, 14,  8, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15,  5, 14, 14, 14,  4, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15,  2, 14, 14,  5, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 12,
+      14,  2, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,  3, 14,  4, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 11,  0, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15);

+ 63 - 0
packages/fcl-base/tests/utclzw.pas

@@ -0,0 +1,63 @@
+unit utclzw;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, fpcunit, testregistry, lzwstream;
+
+Type
+
+  { TTestLZW }
+
+  TTestLZW = CLass(TTestCase)
+  Published
+    Procedure TestFileC;
+  end;
+
+implementation
+
+{ TTestLZW }
+
+{$i filec.inc}
+{$i filed.inc}
+
+
+procedure TTestLZW.TestFileC;
+
+Var
+  Z : TLZWDecompressionStream;
+  C,D : TBytesStream;
+  B : TBytes;
+  I,R : Integer;
+
+begin
+  D:=Nil;
+  Z:=Nil;
+  C:=TBytesStream.Create([]);
+  try
+    C.WriteBuffer(filec,sizeof(filec));
+    C.Position:=0;
+    D:=TBytesStream.Create;
+    D.WriteBuffer(filed,sizeof(filed));
+    D.Position:=0;
+    Z:=TLZWDecompressionStream.Create(C,[zoTIFFCodes]);
+    Z.SourceOwner:=False;
+    SetLength(B,D.Size);
+    R:=Z.Read(B[0],D.Size);
+    AssertEquals('Correct length read',D.Size,R);
+    For I:=0 to Length(B)-1 do
+      AssertEquals('Byte '+IntToStr(I),PByte(D.Memory)[i],B[i]);
+  finally
+    C.Free;
+    D.Free;
+    Z.Free;
+  end;
+
+end;
+
+initialization
+  RegisterTest(TTestLZW);
+end.
+