{
    TODO Несколько уровней
    1. dtiff_base_reader -- модуль со всеми константами и SAX-читалкой
    2. dtiff_impl -- чтение всего, что можно, но под ifdef'ами
    3. dtiff, dtiff_minimal, dtiff_ultraminimal, dtiff_maximal etc -- инклудят
       в себя dtiff_impl с определённными ключиками
       + подключают себя в dtexture2d загрузчик

    StripsPerImage = floor((ImageLength + RowsPerStrip - 1) / RowsPerStrip)
}
{$MODE OBJFPC}
{$MODESWITCH AUTODEREF}
unit dtiff_reader;

interface

uses
  dtypes,
  dstrnum,
  dcolors,
  dstream,
  dstream_op,
  dbitstream,
  dlzw_decompressor,
  dtexture2d;

const
  TIFF_TYPE_BYTE                      = 1;
  TIFF_TYPE_ASCII                     = 2;
  TIFF_TYPE_SHORT                     = 3;
  TIFF_TYPE_LONG                      = 4;
  TIFF_TYPE_RATIONAL                  = 5;
  TIFF_TYPE_SBYTE                     = 6;
  TIFF_TYPE_UNDEFINED                 = 7;
  TIFF_TYPE_SSHORT                    = 8;
  TIFF_TYPE_SLONG                     = 9;
  TIFF_TYPE_SRATIONAL                 = 10;
  TIFF_TYPE_FLOAT                     = 11;
  TIFF_TYPE_DOUBLE                    = 12;

  // Pseudo-tags for NextEntry entry
  TIFF_TAG_END                        = -1;
  TIFF_TAG_CORRUPTED                  = -2;

  TIFF_TAG_NEW_SUBFILE_TYPE           = 254;
  TIFF_TAG_SUBFILE_TYPE               = 255;
  TIFF_TAG_IMAGE_WIDTH                = 256; // number of columns
  TIFF_TAG_IMAGE_LENGTH               = 257; // number of rows
  TIFF_TAG_BITS_PER_SAMPLE            = 258; // 4 or 8
  TIFF_TAG_COMPRESSION                = 259; // 1, 2 or 32773 for bilevel
  TIFF_TAG_PHOTOMETRIC_INTERPRETATION = 262; // 0 or 1 for bilevel
  TIFF_TAG_THRESHHOLDING              = 263;
  TIFF_TAG_CELL_WIDTH                 = 264;
  TIFF_TAG_CELL_LENGTH                = 265;
  TIFF_TAG_FILL_ORDER                 = 266;
  TIFF_TAG_DOCUMENT_NAME              = 269;
  TIFF_TAG_IMAGE_DESCRIPTION          = 270;
  TIFF_TAG_MAKE                       = 271;
  TIFF_TAG_MODEL                      = 272;
  TIFF_TAG_STRIP_OFFSETS              = 273; // byte offset of each strip
  TIFF_TAG_ORIENTATION                = 274;
  TIFF_TAG_SAMPLES_PER_PIXEL          = 277; // 3 (RGB) or more
  TIFF_TAG_ROWS_PER_STRIP             = 278; // rows in each strip (except last)
  TIFF_TAG_STRIP_BYTE_COUNTS          = 279; // size of each strip in bytes
  TIFF_TAG_MIN_SAMPLE_VALUE           = 280;
  TIFF_TAG_MAX_SAMPLE_VALUE           = 281;
  TIFF_TAG_XRESOLUTION                = 282;
  TIFF_TAG_YRESOLUTION                = 283;
  TIFF_TAG_PLANAR_CONFIGURATION       = 284;
  TIFF_TAG_PAGE_NAME                  = 285;
  TIFF_TAG_X_POSITION                 = 286;
  TIFF_TAG_Y_POSITION                 = 287;
  TIFF_TAG_FREE_OFFSETS               = 288;
  TIFF_TAG_FREE_BYTE_COUNTS           = 289;
  TIFF_TAG_GRAY_RESPONSE_UNIT         = 290;
  TIFF_TAG_GRAY_RESPONSE_CURVE        = 291;
  TIFF_TAG_T4_OPTIONS                 = 292;
  TIFF_TAG_T6_OPTIONS                 = 293;
  TIFF_TAG_RESOLUTION_UNIT            = 296; // 1, 2 or 3 for bilevel
  TIFF_TAG_PAGE_NUMBER                = 301;
  TIFF_TAG_SOFTWARE                   = 305;
  TIFF_TAG_DATE_TIME                  = 306;
  TIFF_TAG_ARTIST                     = 315;
  TIFF_TAG_HOST_COMPUTER              = 316;
  TIFF_TAG_PREDICTOR                  = 317;
  TIFF_TAG_WHITE_POINT                = 318;
  TIFF_TAG_PRIMARY_CHROMATICITIES     = 319;
  TIFF_TAG_COLOR_MAP                  = 320; // Index->RGB
  TIFF_TAG_HALFTONE_HINTS             = 321;
  TIFF_TAG_TILE_WIDTH                 = 322;
  TIFF_TAG_TILE_LENGTH                = 323;
  TIFF_TAG_TILE_OFFSETS               = 324;
  TIFF_TAG_TILE_BYTE_COUNTS           = 325;
  TIFF_TAG_INK_SET                    = 332;
  TIFF_TAG_INK_NAMES                  = 333;
  TIFF_TAG_NUMBER_OF_INKS             = 334;
  TIFF_TAG_DOT_RANGE                  = 336;
  TIFF_TAG_TARGET_PRINTER             = 337;
  TIFF_TAG_EXTRA_SAMPLES              = 338;
  TIFF_TAG_SAMPLE_FORMAT              = 339;
  TIFF_TAG_S_MIN_SAMPLE_VALUE         = 340;
  TIFF_TAG_S_MAX_SAMPLE_VALUE         = 341;
  TIFF_TAG_TRANSFER_RANGE             = 342;
  TIFF_TAG_JPEG_PROC                  = 512;
  TIFF_TAG_JPEG_INTERCHANGE_FORMAT    = 513;
  TIFF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = 514;
  TIFF_TAG_JPEG_RESTART_INTERVAL      = 515;
  TIFF_TAG_JPEG_LOSSLESS_PREDICTORS   = 517;
  TIFF_TAG_JPEG_POINT_TRANSFORMS      = 518;
  TIFF_TAG_JPEG_Q_TABLES              = 519;
  TIFF_TAG_JPEG_DC_TABLES             = 520;
  TIFF_TAG_JPEG_AC_TABLES             = 521;
  TIFF_TAG_YCbCrCoefficients          = 529;
  TIFF_TAG_YCbCrSubSampling           = 530;
  TIFF_TAG_YCbCrPositioning           = 531;
  TIFF_TAG_REFERENCE_BLACK_WHITE      = 532;
  TIFF_TAG_COPYRIGHT                  = 33432;

  // Compression values
  TIFF_COMPRESSION_UNCOMPRESSED       = 1;
  TIFF_COMPRESSION_CCITT_1D           = 2;
  TIFF_COMPRESSION_GROUP_3_FAX        = 3;
  TIFF_COMPRESSION_GROUP_4_FAX        = 4;
  TIFF_COMPRESSION_LZW                = 5;
  TIFF_COMPRESSION_JPEG               = 6;
  TIFF_COMPRESSION_PACK_BITS          = 32773;
  // PhotometricInterpretation values
  TIFF_PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO     = 0;
  TIFF_PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO     = 1;
  TIFF_PHOTOMETRIC_INTERPRETATION_RGB               = 2;
  TIFF_PHOTOMETRIC_INTERPRETATION_RGB_PALETTE       = 3;
  TIFF_PHOTOMETRIC_INTERPRETATION_TRANSPARENCY_MASK = 4;
  TIFF_PHOTOMETRIC_INTERPRETATION_CMYK              = 5;
  TIFF_PHOTOMETRIC_INTERPRETATION_YCbCr             = 6;
  TIFF_PHOTOMETRIC_INTERPRETATION_CIELab            = 8;
  // PlanarConfiguration values
  TIFF_PLANAR_CONFIGURATION_CHUNKY                  = 1; // RGBRGBRGBRGB...
  TIFF_PLANAR_CONFIGURATION_PLANAR                  = 2; // RRR..GGG...BBB...
  TIFF_PLANAR_CONFIGURATION_DEFAULT                 = TIFF_PLANAR_CONFIGURATION_CHUNKY;
  // ResulutionUnit values
  TIFF_RESOLUTION_UNIT_NO                           = 1;
  TIFF_RESOLUTION_UNIT_INCH                         = 2;
  TIFF_RESOLUTION_UNIT_CENTIMETER                   = 3;
  TIFF_RESOLUTION_UNIT_DEFAULT                      = TIFF_RESOLUTION_UNIT_INCH;
  // SubfileType
  TIFF_SUBFILE_TYPE_FULL_RESOLUTION                 = 1;
  TIFF_SUBFILE_TYPE_REDUCED_RESOLUTION              = 2;
  TIFF_SUBFILE_TYPE_SINGLE_PAGE                     = 3;
  // Threshholding
  TIFF_THRESHHOLDING_NO                             = 1;
  TIFF_THRESHHOLDING_DITHER_OR_HALFTONE             = 2;
  TIFF_THRESHHOLDING_RANDOMIZED                     = 3;
  TIFF_THRESHHOLDING_DEFAULT                        = TIFF_THRESHHOLDING_NO;
  // ExtraSamples
  TIFF_EXTRA_SAMPLES_UNSPECIFIED                    = 0;
  TIFF_EXTRA_SAMPLES_ASSOCIATED_ALPHA               = 1;
  TIFF_EXTRA_SAMPLES_UNASSOCIATED_ALPHA             = 2;
  // FillOrder
  TIFF_FILL_ORDER_LOWER_FIRST                       = 1;
  TIFF_FILL_ORDER_HIGHER_FIRST                      = 2;
  TIFF_FILL_ORDER_DEFAULT                           = TIFF_FILL_ORDER_LOWER_FIRST;
  // NewSubfileType
  TIFF_NEW_SUBFILE_TYPE_REDUCED_RESOLUTION_MASK     = 1;
  TIFF_NEW_SUBFILE_TYPE_SINGLE_PAGE_MASK            = 2;
  TIFF_NEW_SUBFILE_TYPE_TRANSPARENCY_MASK_MASK      = 4;
  TIFF_NEW_SUBFILE_TYPE_DEFAULT                     = 0;
  // Predictor
  TIFF_PREDICTOR_NO                                 = 1;
  TIFF_PREDICTOR_HORIZONTAL_DIFFERENCING            = 2;
  TIFF_PREDICTOR_DEFAULT                            = TIFF_PREDICTOR_NO;
  // LZW codes
  TIFF_LZW_CODE_CLEAR                               = 256;
  TIFF_LZW_CODE_EOI                                 = 257;
  // Orientation
  TIFF_ORIENTATION_TOP_LEFT                         = 1;
  TIFF_ORIENTATION_TOP_RIGHT                        = 2;
  TIFF_ORIENTATION_BOTTOM_RIGHT                     = 3;
  TIFF_ORIENTATION_BOTTOM_LEFT                      = 4;
  TIFF_ORIENTATION_LEFT_TOP                         = 5;
  TIFF_ORIENTATION_RIGHT_TOP                        = 6;
  TIFF_ORIENTATION_RIGHT_BOTTOM                     = 7;
  TIFF_ORIENTATION_LEFT_BOTTOM                      = 8;
  TIFF_ORIENTATION_DEFAULT                          = TIFF_ORIENTATION_TOP_LEFT;
  // InkSet
  TIFF_INK_SET_CMYK                                 = 1;
  TIFF_INK_SET_NOT_CMYK                             = 2;
  TIFF_INK_SET_DEFAULT                              = TIFF_INK_SET_CMYK;
  // SampleFormat
  TIFF_SAMPLE_FORMAT_UNSIGNED_INTEGER               = 1;
  TIFF_SAMPLE_FORMAT_SIGNED_INTEGER                 = 2; // complement?
  TIFF_SAMPLE_FORMAT_FLOATING_POINT                 = 3;
  TIFF_SAMPLE_FORMAT_UNDEFINED                      = 4;
  TIFF_SAMPLE_FORMAT_DEFAULT                        = TIFF_SAMPLE_FORMAT_UNSIGNED_INTEGER;
  // JPEGProc
  TIFF_JPEG_PROC_BASELINE                           = 1;
  TIFF_JPEG_PROC_LOSSLESS_HUFFMAN                   = 14;

type
TTiffHeader = packed record
  Marker: TUint32;
  Offset: TUint32;
  LittleEndian: Boolean;
end;

PTiffDirectoryEntry = ^TTiffDirectoryEntry;
TTiffDirectoryEntry = packed record
  Tag: TUint16;
  _Type: TUint16;
  Count: TUint32;
  Offset: TUint32;
end;

TTiffRational = packed record
  Numerator: TUint32;
  Denumerator: TUint32;
end;

TTiffSRational = packed record
  Numerator: TInt32;
  Denumerator: TInt32;
end;

PTiffReader = ^TTiffReader;
TTiffReader = object
private
  FData: TData;
  FStream: PStream;
  FReady: Boolean;
  FHeader: TTiffHeader;
  FNumberOfEntries: LongInt;
  FCurrentEntryIndex: LongInt;
  FEntry: TTiffDirectoryEntry;
public
  procedure Init(const Data: TData);
  procedure Reinit(const Data: TData);
  procedure Done;
  function IsHeaderCorrect: Boolean; inline;
  function IsLittleEndian: Boolean; inline;
public
  // Reading Entry
  function NextEntry: LongInt;
  function Entry: PTiffDirectoryEntry; inline;
  function CheckEntryType(Expected: TUint16): Boolean; inline;
  function EntryByte: TUint8; inline; overload;
  function EntryAscii: AnsiString; overload;
  function EntryShort: TUint16; inline; overload;
  function EntryLong: TUint32; inline; overload;
  function EntryRational: TTiffRational; overload;
  function EntrySByte: TInt8; inline; overload;
  //function EntryUndefined:
  function EntrySShort: TInt16; inline; overload;
  function EntrySLong: TInt32; inline; overload;
  function EntrySRational: TTiffSRational; overload;
  function EntryFloat: Single; inline; overload;
  function EntryDouble: Double; overload;
  // Reading Entry arrays
  function EntryByte(Index: LongInt): TUint8; overload;
  function EntryShort(Index: LongInt): TUint16; overload;
  function EntryLong(Index: LongInt): TUint32; overload;
  function EntryRational(Index: LongInt): TTiffRational; overload;
  function EntrySByte(Index: LongInt): TInt8; overload;
  function EntrySShort(Index: LongInt): TInt16; overload;
  function EntrySLong(Index: LongInt): TInt32; overload;
  function EntrySRational(Index: LongInt): TTiffSRational; overload;
  function EntryFloat(Index: LongInt): Single; overload;
  function EntryDouble(Index: LongInt): Double; overload;
  // Reading by offsets
  function GetOffsetByte(Offset: TUint32; Index: LongInt): TUint8;
  function GetOffsetAscii(Offset: TUint32): AnsiString;
  function GetOffsetShort(Offset: TUint32; Index: LongInt): TUint16;
  function GetOffsetLong(Offset: TUint32; Index: LongInt): TUint32;
  function GetOffsetRational(Offset: TUint32; Index: LongInt): TTiffRational;
  function GetOffsetSByte(Offset: TUint32; Index: LongInt): TInt8;
  //function GetOffsetUndefined(Offset: TUint32; Index: LongInt): TUint16;
  function GetOffsetSShort(Offset: TUint32; Index: LongInt): TInt16;
  function GetOffsetSLong(Offset: TUint32; Index: LongInt): TInt32;
  function GetOffsetSRational(Offset: TUint32; Index: LongInt): TTiffSRational;
  function GetOffsetFloat(Offset: TUint32; Index: LongInt): Single;
  function GetOffsetDouble(Offset: TUint32; Index: LongInt): Double;
  function ReadLzwData(Offset, Count: TUint32; var P: Pointer; var L: LongInt): Boolean;
public
  // Some utils
  property NumberOfEntries: LongInt read FNumberOfEntries;
end;

implementation

procedure TTiffReader.Init(const Data: TData);
const
  TIFF_MARKER_INTEL:    array[0..3] of Byte = (Ord('I'), Ord('I'), 42,  0);
  TIFF_MARKER_MOTOROLA: array[0..3] of Byte = (Ord('M'), Ord('M'),  0, 42);
begin
  FData := Data;
  FStream := Data.Stream;
  FStream.Read(@FHeader, SizeOf(TUint32) + SizeOf(TUint32));
  with FHeader do
    if Marker = TUint32(TIFF_MARKER_INTEL) then begin
      FHeader.LittleEndian := True;
      FReady := True;
    end else if Marker = TUint32(TIFF_MARKER_MOTOROLA) then begin
      FHeader.LittleEndian := False;
      FReady := True;
    end else
      FReady := False;

  if IsHeaderCorrect then begin
    FStream.Position := FHeader.Offset;
    FNumberOfEntries := DecodeEndianUint16(FStream.ReadUint16, IsLittleEndian);
    FCurrentEntryIndex := -1;
    if FNumberOfEntries = 0 then
      FReady := False;
  end;
end;

procedure TTiffReader.Reinit(const Data: TData);
begin
  Done;
  Init(Data);
end;

procedure TTiffReader.Done;
begin
  FData.Done;
end;

function TTiffReader.IsHeaderCorrect: Boolean;
begin
  Result := FReady;
end;

function TTiffReader.IsLittleEndian: Boolean;
begin
  Result := FHeader.LittleEndian;
end;

function TTiffReader.NextEntry: LongInt;
var
  Offset: TUint32;
begin
  Inc(FCurrentEntryIndex);
  if FCurrentEntryIndex >= FNumberOfEntries then begin
    Offset := DecodeEndianUint32(FStream.ReadUint, IsLittleEndian);
    if Offset = 0 then
      Exit(TIFF_TAG_END);
    FStream.Position := Offset;
    FNumberOfEntries := DecodeEndianUint16(FStream.ReadUint16, IsLittleEndian);
    FCurrentEntryIndex := -1;
  end;
  if FStream.Read(@FEntry, SizeOf(FEntry)) < SizeOf(FEntry) then
    Exit(TIFF_TAG_CORRUPTED);
  Exit(FEntry.Tag);
end;

function TTiffReader.Entry: PTiffDirectoryEntry;
begin
  Result := @FEntry;
end;

function TTiffReader.CheckEntryType(Expected: TUint16): Boolean;
begin
  Result := FEntry._Type = Expected;
end;

function TTiffReader.EntryByte: TUint8;
begin
  Result := TUint8(Pointer(@FEntry.Offset)^);
end;

function TTiffReader.EntryAscii: AnsiString;
begin
  Result := GetOffsetAscii(FEntry.Offset);
end;

function TTiffReader.EntryShort: TUint16;
begin
  Result := TUint16(Pointer(@FEntry.Offset)^);
end;

function TTiffReader.EntryLong: TUint32;
begin
  Result := TUint32(Pointer(@FEntry.Offset)^);
end;

function TTiffReader.EntryRational: TTiffRational;
begin
  Result := GetOffsetRational(Entry.Offset, 0);
end;

function TTiffReader.EntrySByte: TInt8;
begin
  Result := TInt8(Pointer(@FEntry.Offset)^);
end;

function TTiffReader.EntrySShort: TInt16;
begin
  Result := TInt16(Pointer(@FEntry.Offset)^);
end;

function TTiffReader.EntrySLong: TInt32;
begin
  Result := TInt32(Pointer(@FEntry.Offset)^);
end;

function TTiffReader.EntrySRational: TTiffSRational;
begin
  Result := GetOffsetSRational(Entry.Offset, 0);
end;

function TTiffReader.EntryFloat: Single;
begin
  Result := Single(Pointer(@FEntry.Offset)^);
end;

function TTiffReader.EntryDouble: Double;
begin
  Result := GetOffsetDouble(Entry.Offset, 0);
end;

function TTiffReader.EntryByte(Index: LongInt): TUint8;
begin
  if FEntry.Count > 1 then begin
    Result := GetOffsetByte(Entry.Offset, Index);
  end else
    Result := TUint8(Pointer(@FEntry.Offset)^);
end;

function TTiffReader.EntryShort(Index: LongInt): TUint16;
begin
  if FEntry.Count > 1 then begin
    Result := GetOffsetShort(Entry.Offset, Index);
  end else
    Result := TUint16(Pointer(@FEntry.Offset)^);
end;

function TTiffReader.EntryLong(Index: LongInt): TUint32;
begin
  if FEntry.Count > 1 then begin
    Result := GetOffsetLong(Entry.Offset, Index);
  end else
    Result := TUint32(Pointer(@FEntry.Offset)^);
end;

function TTiffReader.EntryRational(Index: LongInt): TTiffRational;
begin
  Result := GetOffsetRational(Entry.Offset, Index);
end;

function TTiffReader.EntrySByte(Index: LongInt): TInt8;
begin
  if FEntry.Count > 1 then begin
    Result := GetOffsetSByte(Entry.Offset, Index);
  end else
    Result := TInt8(Pointer(@FEntry.Offset)^);
end;

function TTiffReader.EntrySShort(Index: LongInt): TInt16;
begin
  if FEntry.Count > 1 then begin
    Result := GetOffsetSShort(Entry.Offset, Index);
  end else
    Result := TInt16(Pointer(@FEntry.Offset)^);
end;

function TTiffReader.EntrySLong(Index: LongInt): TInt32;
begin
  if FEntry.Count > 1 then begin
    Result := GetOffsetSLong(Entry.Offset, Index);
  end else
    Result := TInt32(Pointer(@FEntry.Offset)^);
end;

function TTiffReader.EntrySRational(Index: LongInt): TTiffSRational;
begin
  Result := GetOffsetSRational(Entry.Offset, Index);
end;

function TTiffReader.EntryFloat(Index: LongInt): Single;
begin
  if FEntry.Count > 1 then begin
    Result := GetOffsetFloat(Entry.Offset, Index);
  end else
    Result := Single(Pointer(@FEntry.Offset)^);
end;

function TTiffReader.EntryDouble(Index: LongInt): Double;
begin
  Result := GetOffsetDouble(Entry.Offset, Index);
end;

function TTiffReader.GetOffsetByte(Offset: TUint32; Index: LongInt): TUint8;
var
  Saved: Int64;
begin
  Saved := FStream.Position;
  FStream.Position := Offset + Index * SizeOf(TUint8);
  Result := FStream.ReadByte;
  FStream.Position := Saved;
end;

function TTiffReader.GetOffsetAscii(Offset: TUint32): AnsiString;
var
  Saved: Int64;
  C: AnsiChar;
begin
  Saved := FStream.Position;
  FStream.Position := Offset;
  Result := '';
  C := AnsiChar(FStream.ReadByte);
  while C <> #0 do begin
    Result := Result + C;
    C := AnsiChar(FStream.ReadByte);
  end;
  FStream.Position := Saved;
end;

function TTiffReader.GetOffsetShort(Offset: TUint32; Index: LongInt): TUint16;
var
  Saved: Int64;
begin
  Saved := FStream.Position;
  FStream.Position := Offset + Index * SizeOf(TUint16);
  Result := DecodeEndianUint16(FStream.ReadUint16, IsLittleEndian);
  FStream.Position := Saved;
end;

function TTiffReader.GetOffsetLong(Offset: TUint32; Index: LongInt): TUint32;
var
  Saved: Int64;
begin
  Saved := FStream.Position;
  FStream.Position := Offset + Index * SizeOf(TUint32);
  Result := DecodeEndianUint32(FStream.ReadUint, IsLittleEndian);
  FStream.Position := Saved;
end;

function TTiffReader.GetOffsetRational(Offset: TUint32; Index: LongInt): TTiffRational;
var
  Saved: Int64;
begin
  Saved := FStream.Position;
  FStream.Position := Offset + Index * SizeOf(TUint32);
  Result.Numerator := DecodeEndianUint32(FStream.ReadUint, IsLittleEndian);
  Result.Denumerator := DecodeEndianUint32(FStream.ReadUint, IsLittleEndian);
  FStream.Position := Saved;
end;

function TTiffReader.GetOffsetSByte(Offset: TUint32; Index: LongInt): TInt8;
var
  Saved: Int64;
begin
  Saved := FStream.Position;
  FStream.Position := Offset + Index * SizeOf(TInt8);
  Result := FStream.ReadInt8;
  FStream.Position := Saved;
end;

function TTiffReader.GetOffsetSShort(Offset: TUint32; Index: LongInt): TInt16;
var
  Saved: Int64;
begin
  Saved := FStream.Position;
  FStream.Position := Offset + Index * SizeOf(TInt16);
  Result := DecodeEndianInt16(FStream.ReadInt16, IsLittleEndian);
  FStream.Position := Saved;
end;

function TTiffReader.GetOffsetSLong(Offset: TUint32; Index: LongInt): TInt32;
var
  Saved: Int64;
begin
  Saved := FStream.Position;
  FStream.Position := Offset + Index * SizeOf(TInt32);
  Result := DecodeEndianInt32(FStream.ReadInt, IsLittleEndian);
  FStream.Position := Saved;
end;

function TTiffReader.GetOffsetSRational(Offset: TUint32; Index: LongInt): TTiffSRational;
var
  Saved: Int64;
begin
  Saved := FStream.Position;
  FStream.Position := Offset + Index * SizeOf(TInt32);
  Result.Numerator := DecodeEndianInt32(FStream.ReadInt, IsLittleEndian);
  Result.Denumerator := DecodeEndianInt32(FStream.ReadInt, IsLittleEndian);
  FStream.Position := Saved;
end;

function TTiffReader.GetOffsetFloat(Offset: TUint32; Index: LongInt): Single;
var
  Saved: Int64;
begin
  Saved := FStream.Position;
  FStream.Position := Offset + Index * SizeOf(Single);
  Result := FStream.ReadFloat;
  FStream.Position := Saved;
end;

function TTiffReader.GetOffsetDouble(Offset: TUint32; Index: LongInt): Double;
var
  Saved: Int64;
begin
  Saved := FStream.Position;
  FStream.Position := Offset + Index * SizeOf(Double);
  Result := FStream.ReadDouble;
  FStream.Position := Saved;
end;

// TODO refactor it and rename
type
TFoo = object
  S: PStream;
  L: LongInt;
  function Read(var Buffer; Count: LongInt): LongInt;
end;

function TFoo.Read(var Buffer; Count: LongInt): LongInt;
begin
  if Count < L then begin
    Result := S^.ReadVar(Buffer, Count);
    Dec(L, Count);
  end else begin
    Result := S^.ReadVar(Buffer, L);
    L := 0;
  end;
end;

function TTiffReader.ReadLzwData(Offset, Count: TUint32; var P: Pointer; var L: LongInt): Boolean;
var
  Saved: Int64;
  Foo: TFoo;
begin
  Saved := FStream.Position;
  FStream.Position := Offset;
  Foo.S := FStream;
  Foo.L := Count;
  //Writeln('Offset=', Offset, ' Count=', Count);
  Result := LZWDecompress(@Foo.Read, P, L);
  FStream.Position := Saved;
end;

end.
