123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796 |
- //
- // The graphics engine GXScene https://github.com/glscene
- //
- unit GXS.HeightTileFile;
- (*
- Access to large tiled height data files.
- Performance vs Raw file accesses (for perfect tile match):
- Cached data:
- "Smooth" terrain 1:2 to 1:10
- Random terrain 1:1
- Non-cached data:
- "Smooth" terrain 1:100 to 1:1000
- Random terrain 1:100
- *)
- interface
- {$I Stage.Defines.inc}
- uses
- System.Classes,
- System.SysUtils,
- GXS.ApplicationFileIO;
- type
- TIntegerArray = array [0 .. MaxInt shr 3] of Integer;
- PIntegerArray = ^TIntegerArray;
- TSmallIntArray = array [0 .. MaxInt shr 2] of SmallInt;
- PSmallIntArray = ^TSmallIntArray;
- TShortIntArray = array [0 .. MaxInt shr 2] of ShortInt;
- PShortIntArray = ^TShortIntArray;
- TgxHeightTileInfo = packed record
- left, top, width, height: Integer;
- min, max, average: SmallInt;
- fileOffset: Int64; // offset to tile data in the file
- end;
- PgxHeightTileInfo = ^TgxHeightTileInfo;
- PPHeightTileInfo = ^PgxHeightTileInfo;
- TgxHeightTile = packed record
- info: TgxHeightTileInfo;
- data: array of SmallInt;
- end;
- PHeightTile = ^TgxHeightTile;
- THTFHeader = packed record
- FileVersion: array [0 .. 5] of AnsiChar;
- TileIndexOffset: Int64;
- SizeX, SizeY: Integer;
- TileSize: Integer;
- DefaultZ: SmallInt;
- end;
- const
- cHTFHashTableSize = 1023;
- cHTFQuadTableSize = 31;
- type
- // Interfaces a Tiled file
- TgxHeightTileFile = class(TObject)
- private
- FFile: TStream;
- FHeader: THTFHeader;
- FTileIndex: packed array of TgxHeightTileInfo;
- FTileMark: array of Cardinal;
- FLastMark: Cardinal;
- FHashTable: array [0 .. cHTFHashTableSize] of array of Integer;
- FQuadTable: array [0 .. cHTFQuadTableSize, 0 .. cHTFQuadTableSize]
- of array of Integer;
- FCreating: Boolean;
- FHeightTile: TgxHeightTile;
- FInBuf: array of ShortInt;
- protected
- function GetTiles(index: Integer): PgxHeightTileInfo;
- function QuadTableX(x: Integer): Integer;
- function QuadTableY(y: Integer): Integer;
- procedure PackTile(aWidth, aHeight: Integer; src: PSmallIntArray);
- procedure UnPackTile(source: PShortIntArray);
- property TileIndexOffset: Int64 read FHeader.TileIndexOffset
- write FHeader.TileIndexOffset;
- public
- (* Creates a new HTF file.
- Read and data access methods are not available when creating. *)
- constructor CreateNew(const fileName: String;
- aSizeX, aSizeY, aTileSize: Integer);
- constructor Create(const fileName: String);
- destructor Destroy; override;
- // Returns tile index for corresponding left/top.
- function GetTileIndex(aLeft, aTop: Integer): Integer;
- // Returns tile of corresponding left/top.
- function GetTile(aLeft, aTop: Integer; pTileInfo: PPHeightTileInfo = nil)
- : PHeightTile;
- (* Stores and compresses give tile data.
- aLeft and top MUST be a multiple of TileSize, aWidth and aHeight
- MUST be lower or equal to TileSize. *)
- procedure CompressTile(aLeft, aTop, aWidth, aHeight: Integer;
- aData: PSmallIntArray);
- (* Extract a single row from the HTF file.
- This is NOT the fastest way to access HTF data.
- All of the row must be contained in the world, otherwise result is undefined. *)
- procedure ExtractRow(x, y, len: Integer; dest: PSmallIntArray);
- // Returns the tile that contains x and y.
- function XYTileInfo(anX, anY: Integer): PgxHeightTileInfo;
- (* Returns the height at given coordinates.
- This is definetely NOT the fastest way to access HTF data and should
- only be used as utility function. *)
- function XYHeight(anX, anY: Integer): SmallInt;
- // Clears the list then add all tiles that overlap the rectangular area.
- procedure TilesInRect(aLeft, aTop, aRight, aBottom: Integer;
- destList: TList);
- function TileCount: Integer;
- property Tiles[index: Integer]: PgxHeightTileInfo read GetTiles;
- function IndexOfTile(aTile: PgxHeightTileInfo): Integer;
- function TileCompressedSize(tileIndex: Integer): Integer;
- property SizeX: Integer read FHeader.SizeX;
- property SizeY: Integer read FHeader.SizeY;
- (* Maximum width and height for a tile.
- Actual tiles may not be square, can assume random layouts, and may overlap. *)
- property TileSize: Integer read FHeader.TileSize;
- property DefaultZ: SmallInt read FHeader.DefaultZ write FHeader.DefaultZ;
- end;
- // ------------------------------------------------------------------
- implementation
- // ------------------------------------------------------------------
- const
- cFileVersion = 'HTF100';
- procedure FillSmallInt(p: PSmallInt; count: Integer; v: SmallInt);
- var
- I: Integer;
- begin
- for I := count - 1 downto 0 do
- begin
- p^ := v;
- Inc(p);
- end;
- end;
- // ------------------
- // ------------------ TgxHeightTileFile ------------------
- // ------------------
- constructor TgxHeightTileFile.CreateNew(const fileName: String;
- aSizeX, aSizeY, aTileSize: Integer);
- begin
- with FHeader do
- begin
- FileVersion := cFileVersion;
- SizeX := aSizeX;
- SizeY := aSizeY;
- TileSize := aTileSize;
- end;
- FFile := TFileStream.Create(fileName, fmCreate);
- FFile.Write(FHeader, SizeOf(FHeader));
- FCreating := True;
- SetLength(FHeightTile.data, aTileSize * aTileSize);
- end;
- constructor TgxHeightTileFile.Create(const fileName: String);
- var
- n, I, key, qx, qy: Integer;
- begin
- FFile := TFileStream.Create(fileName, fmOpenRead + fmShareDenyNone);
- // Read Header
- FFile.Read(FHeader, SizeOf(FHeader));
- if FHeader.FileVersion <> cFileVersion then
- raise Exception.Create('Invalid file type');
- // Read TileIndex
- FFile.Position := TileIndexOffset;
- FFile.Read(n, 4);
- SetLength(FTileIndex, n);
- FFile.Read(FTileIndex[0], SizeOf(TgxHeightTileInfo) * n);
- // Prepare HashTable & QuadTable
- for n := 0 to High(FTileIndex) do
- begin
- with FTileIndex[n] do
- begin
- key := left + (top shl 4);
- key := ((key and cHTFHashTableSize) + (key shr 10) + (key shr 20)) and
- cHTFHashTableSize;
- I := Length(FHashTable[key]);
- SetLength(FHashTable[key], I + 1);
- FHashTable[key][I] := n;
- for qx := QuadTableX(left) to QuadTableX(left + width - 1) do
- begin
- for qy := QuadTableY(top) to QuadTableY(top + height - 1) do
- begin
- I := Length(FQuadTable[qx, qy]);
- SetLength(FQuadTable[qx, qy], I + 1);
- FQuadTable[qx, qy][I] := n;
- end;
- end;
- end;
- end;
- FHeightTile.info.left := MaxInt; // mark as not loaded
- SetLength(FHeightTile.data, TileSize * TileSize);
- SetLength(FInBuf, TileSize * (TileSize + 1) * 2);
- SetLength(FTileMark, Length(FTileIndex));
- end;
- destructor TgxHeightTileFile.Destroy;
- var
- n: Integer;
- begin
- if FCreating then
- begin
- TileIndexOffset := FFile.Position;
- // write tile index
- n := Length(FTileIndex);
- FFile.Write(n, 4);
- FFile.Write(FTileIndex[0], SizeOf(TgxHeightTileInfo) * n);
- // write data size
- FFile.Position := 0;
- FFile.Write(FHeader, SizeOf(FHeader));
- end;
- FFile.Free;
- inherited Destroy;
- end;
- function TgxHeightTileFile.QuadTableX(x: Integer): Integer;
- begin
- Result := ((x * (cHTFQuadTableSize + 1)) div (SizeX + 1)) and
- cHTFQuadTableSize;
- end;
- function TgxHeightTileFile.QuadTableY(y: Integer): Integer;
- begin
- Result := ((y * (cHTFQuadTableSize + 1)) div (SizeY + 1)) and
- cHTFQuadTableSize;
- end;
- procedure TgxHeightTileFile.PackTile(aWidth, aHeight: Integer;
- src: PSmallIntArray);
- var
- packWidth: Integer;
- function DiffEncode(src: PSmallIntArray; dest: PShortIntArray): Cardinal;
- var
- I: Integer;
- v, delta: SmallInt;
- begin
- Result := Cardinal(dest);
- v := src[0];
- PSmallIntArray(dest)[0] := v;
- dest := PShortIntArray(Cardinal(dest) + 2);
- I := 1;
- while I < packWidth do
- begin
- delta := src[I] - v;
- v := src[I];
- if Abs(delta) <= 127 then
- begin
- dest[0] := ShortInt(delta);
- dest := PShortIntArray(Cardinal(dest) + 1);
- end
- else
- begin
- dest[0] := -128;
- dest := PShortIntArray(Cardinal(dest) + 1);
- PSmallIntArray(dest)[0] := v;
- dest := PShortIntArray(Cardinal(dest) + 2);
- end;
- Inc(I);
- end;
- Result := Cardinal(dest) - Result;
- end;
- function RLEEncode(src: PSmallIntArray; dest: PAnsiChar): Cardinal;
- var
- v: SmallInt;
- I, n: Integer;
- begin
- I := 0;
- Result := Cardinal(dest);
- while (I < packWidth) do
- begin
- v := src[I];
- Inc(I);
- n := 0;
- PSmallIntArray(dest)[0] := v;
- Inc(dest, 2);
- while (src[I] = v) and (I < packWidth) do
- begin
- Inc(n);
- if n = 255 then
- begin
- dest[0] := #255;
- Inc(dest);
- n := 0;
- end;
- Inc(I);
- end;
- if (I < packWidth) or (n > 0) then
- begin
- dest[0] := AnsiChar(n);
- Inc(dest);
- end;
- end;
- Result := Cardinal(dest) - Result;
- end;
- var
- y: Integer;
- p: PSmallIntArray;
- buf, bestBuf: array of Byte;
- bestLength, len: Integer;
- leftPack, rightPack: Byte;
- bestMethod: Byte; // 0=RAW, 1=Diff, 2=RLE
- av: Int64;
- v: SmallInt;
- begin
- SetLength(buf, TileSize * 4); // worst case situation
- SetLength(bestBuf, TileSize * 4); // worst case situation
- with FHeightTile.info do
- begin
- min := src[0];
- max := src[0];
- av := src[0];
- for y := 1 to aWidth * aHeight - 1 do
- begin
- v := src[y];
- if v < min then
- min := v
- else if v > max then
- max := v;
- av := av + v;
- end;
- average := av div (aWidth * aHeight);
- if min = max then
- Exit; // no need to store anything
- end;
- for y := 0 to aHeight - 1 do
- begin
- p := @src[aWidth * y];
- packWidth := aWidth;
- // Lookup leftPack
- leftPack := 0;
- while (leftPack < 255) and (packWidth > 0) and (p[0] = DefaultZ) do
- begin
- p := PSmallIntArray(Cardinal(p) + 2);
- Dec(packWidth);
- Inc(leftPack);
- end;
- // Lookup rightPack
- rightPack := 0;
- while (rightPack < 255) and (packWidth > 0) and
- (p[packWidth - 1] = DefaultZ) do
- begin
- Dec(packWidth);
- Inc(rightPack);
- end;
- // Default encoding = RAW
- bestLength := packWidth * 2;
- bestMethod := 0;
- Move(p^, bestBuf[0], bestLength);
- // Diff encoding
- len := DiffEncode(p, PShortIntArray(@buf[0]));
- if len < bestLength then
- begin
- bestLength := len;
- bestMethod := 1;
- Move(buf[0], bestBuf[0], bestLength);
- end;
- // RLE encoding
- len := RLEEncode(p, PAnsiChar(@buf[0]));
- if len < bestLength then
- begin
- bestLength := len;
- bestMethod := 2;
- Move(buf[0], bestBuf[0], bestLength);
- end;
- // Write to file
- if (leftPack or rightPack) = 0 then
- begin
- FFile.Write(bestMethod, 1);
- FFile.Write(bestBuf[0], bestLength);
- end
- else
- begin
- if leftPack > 0 then
- begin
- if rightPack > 0 then
- begin
- bestMethod := bestMethod + $C0;
- FFile.Write(bestMethod, 1);
- FFile.Write(leftPack, 1);
- FFile.Write(rightPack, 1);
- FFile.Write(bestBuf[0], bestLength);
- end
- else
- begin
- bestMethod := bestMethod + $80;
- FFile.Write(bestMethod, 1);
- FFile.Write(leftPack, 1);
- FFile.Write(bestBuf[0], bestLength);
- end;
- end
- else
- begin
- bestMethod := bestMethod + $40;
- FFile.Write(bestMethod, 1);
- FFile.Write(rightPack, 1);
- FFile.Write(bestBuf[0], bestLength);
- end;
- end;
- end;
- end;
- procedure TgxHeightTileFile.UnPackTile(source: PShortIntArray);
- var
- unpackWidth, tileWidth: Cardinal;
- src: PShortInt;
- dest: PSmallInt;
- procedure DiffDecode;
- var
- v: SmallInt;
- delta: SmallInt;
- locSrc: PShortInt;
- destEnd, locDest: PSmallInt;
- begin
- locSrc := PShortInt(Cardinal(src) - 1);
- locDest := dest;
- destEnd := PSmallInt(Cardinal(dest) + unpackWidth * 2);
- while Cardinal(locDest) < Cardinal(destEnd) do
- begin
- Inc(locSrc);
- v := PSmallInt(locSrc)^;
- Inc(locSrc, 2);
- locDest^ := v;
- Inc(locDest);
- while (Cardinal(locDest) < Cardinal(destEnd)) do
- begin
- delta := locSrc^;
- if delta <> -128 then
- begin
- v := v + delta;
- Inc(locSrc);
- locDest^ := v;
- Inc(locDest);
- end
- else
- Break;
- end;
- end;
- src := locSrc;
- dest := locDest;
- end;
- procedure RLEDecode;
- var
- n, j: Cardinal;
- v: SmallInt;
- locSrc: PShortInt;
- destEnd, locDest: PSmallInt;
- begin
- locSrc := src;
- locDest := dest;
- destEnd := PSmallInt(Cardinal(dest) + unpackWidth * 2);
- while Cardinal(locDest) < Cardinal(destEnd) do
- begin
- v := PSmallIntArray(locSrc)[0];
- Inc(locSrc, 2);
- repeat
- if Cardinal(locDest) = Cardinal(destEnd) - 2 then
- begin
- locDest^ := v;
- Inc(locDest);
- n := 0;
- end
- else
- begin
- n := Integer(locSrc^ and 255);
- Inc(locSrc);
- for j := 0 to n do
- begin
- locDest^ := v;
- Inc(locDest);
- end;
- end;
- until (n < 255) or (Cardinal(locDest) >= Cardinal(destEnd));
- end;
- src := locSrc;
- dest := locDest;
- end;
- var
- y: Integer;
- n: Byte;
- method: Byte;
- begin
- dest := @FHeightTile.data[0];
- with FHeightTile.info do
- begin
- if min = max then
- begin
- FillSmallInt(dest, width * height, min);
- Exit;
- end;
- tileWidth := width;
- end;
- src := PShortInt(source);
- n := 0;
- for y := 0 to FHeightTile.info.height - 1 do
- begin
- method := Byte(src^);
- Inc(src);
- unpackWidth := tileWidth;
- // Process left pack if any
- if (method and $80) <> 0 then
- begin
- n := PByte(src)^;
- Inc(src);
- FillSmallInt(dest, n, DefaultZ);
- Dec(unpackWidth, n);
- Inc(dest, n);
- end;
- // Read right pack if any
- if (method and $40) <> 0 then
- begin
- PByte(@n)^ := PByte(src)^;
- Inc(src);
- Dec(unpackWidth, n)
- end
- else
- n := 0;
- // Process main data
- case (method and $3F) of
- 1:
- DiffDecode;
- 2:
- RLEDecode;
- else
- Move(src^, dest^, unpackWidth * 2);
- Inc(src, unpackWidth * 2);
- Inc(dest, unpackWidth);
- end;
- // Process right pack if any
- if n > 0 then
- begin
- FillSmallInt(dest, n, DefaultZ);
- Inc(dest, n);
- end;
- end;
- end;
- function TgxHeightTileFile.GetTileIndex(aLeft, aTop: Integer): Integer;
- var
- I, key, n: Integer;
- p: PIntegerArray;
- begin
- Result := -1;
- key := aLeft + (aTop shl 4);
- key := ((key and cHTFHashTableSize) + (key shr 10) + (key shr 20)) and
- cHTFHashTableSize;
- n := Length(FHashTable[key]);
- if n > 0 then
- begin
- p := @FHashTable[key][0];
- for I := 0 to n - 1 do
- begin
- with FTileIndex[p[I]] do
- begin
- if (left = aLeft) and (top = aTop) then
- begin
- Result := p[I];
- Break;
- end;
- end;
- end;
- end;
- end;
- function TgxHeightTileFile.GetTile(aLeft, aTop: Integer;
- pTileInfo: PPHeightTileInfo = nil): PHeightTile;
- var
- I, n: Integer;
- tileInfo: PgxHeightTileInfo;
- begin
- with FHeightTile.info do
- if (left = aLeft) and (top = aTop) then
- begin
- Result := @FHeightTile;
- if Assigned(pTileInfo) then
- pTileInfo^ := @Result.info;
- Exit;
- end;
- I := GetTileIndex(aLeft, aTop);
- if I >= 0 then
- begin
- tileInfo := @FTileIndex[I];
- if Assigned(pTileInfo) then
- pTileInfo^ := tileInfo;
- if I < High(FTileIndex) then
- n := FTileIndex[I + 1].fileOffset - tileInfo.fileOffset
- else
- n := TileIndexOffset - tileInfo.fileOffset;
- Result := @FHeightTile;
- FHeightTile.info := tileInfo^;
- FFile.Position := tileInfo.fileOffset;
- FFile.Read(FInBuf[0], n);
- UnPackTile(@FInBuf[0]);
- end
- else
- begin
- Result := nil;
- if Assigned(pTileInfo) then
- pTileInfo^ := nil;
- end;
- end;
- procedure TgxHeightTileFile.CompressTile(aLeft, aTop, aWidth, aHeight: Integer;
- aData: PSmallIntArray);
- begin
- Assert(aWidth <= TileSize);
- Assert(aHeight <= TileSize);
- with FHeightTile.info do
- begin
- left := aLeft;
- top := aTop;
- width := aWidth;
- height := aHeight;
- fileOffset := FFile.Position;
- end;
- PackTile(aWidth, aHeight, aData);
- SetLength(FTileIndex, Length(FTileIndex) + 1);
- FTileIndex[High(FTileIndex)] := FHeightTile.info
- end;
- procedure TgxHeightTileFile.ExtractRow(x, y, len: Integer;
- dest: PSmallIntArray);
- var
- rx: Integer;
- n: Cardinal;
- tileInfo: PgxHeightTileInfo;
- tile: PHeightTile;
- begin
- while len > 0 do
- begin
- tileInfo := XYTileInfo(x, y);
- if not Assigned(tileInfo) then
- Exit;
- rx := x - tileInfo.left;
- n := Cardinal(tileInfo.width - rx);
- if n > Cardinal(len) then
- n := Cardinal(len);
- tile := GetTile(tileInfo.left, tileInfo.top);
- Move(tile.data[(y - tileInfo.top) * tileInfo.width + rx], dest^, n * 2);
- dest := PSmallIntArray(Cardinal(dest) + n * 2);
- Dec(len, n);
- Inc(x, n);
- end;
- end;
- function TgxHeightTileFile.XYTileInfo(anX, anY: Integer): PgxHeightTileInfo;
- var
- tileList: TList;
- begin
- tileList := TList.Create;
- try
- TilesInRect(anX, anY, anX + 1, anY + 1, tileList);
- if tileList.count > 0 then
- Result := PgxHeightTileInfo(tileList.First)
- else
- Result := nil;
- finally
- tileList.Free;
- end;
- end;
- function TgxHeightTileFile.XYHeight(anX, anY: Integer): SmallInt;
- var
- tileInfo: PgxHeightTileInfo;
- tile: PHeightTile;
- begin
- // Current tile per chance?
- with FHeightTile.info do
- begin
- if (left <= anX) and (left + width > anX) and (top <= anY) and
- (top + height > anY) then
- begin
- Result := FHeightTile.data[(anX - left) + (anY - top) * width];
- Exit;
- end;
- end;
- // Find corresponding tile if any
- tileInfo := XYTileInfo(anX, anY);
- if Assigned(tileInfo) then
- with tileInfo^ do
- begin
- tile := GetTile(left, top);
- Result := tile.data[(anX - left) + (anY - top) * width];
- end
- else
- Result := DefaultZ;
- end;
- procedure TgxHeightTileFile.TilesInRect(aLeft, aTop, aRight, aBottom: Integer;
- destList: TList);
- var
- I, n, qx, qy, idx: Integer;
- p: PIntegerArray;
- tileInfo: PgxHeightTileInfo;
- begin
- destList.count := 0;
- // Clamp to world
- if (aLeft > SizeX) or (aRight < 0) or (aTop > SizeY) or (aBottom < 0) then
- Exit;
- if aLeft < 0 then
- aLeft := 0;
- if aRight > SizeX then
- aRight := SizeX;
- if aTop < 0 then
- aTop := 0;
- if aBottom > SizeY then
- aBottom := SizeY;
- // Collect tiles on quads
- Inc(FLastMark);
- for qy := QuadTableY(aTop) to QuadTableY(aBottom) do
- begin
- for qx := QuadTableX(aLeft) to QuadTableX(aRight) do
- begin
- n := High(FQuadTable[qx, qy]);
- p := @FQuadTable[qx, qy][0];
- for I := 0 to n do
- begin
- idx := p[I];
- if FTileMark[idx] <> FLastMark then
- begin
- FTileMark[idx] := FLastMark;
- tileInfo := @FTileIndex[idx];
- with tileInfo^ do
- begin
- if (left <= aRight) and (top <= aBottom) and (aLeft < left + width)
- and (aTop < top + height) then
- destList.Add(tileInfo);
- end;
- end;
- end;
- end;
- end;
- end;
- function TgxHeightTileFile.TileCount: Integer;
- begin
- Result := Length(FTileIndex);
- end;
- function TgxHeightTileFile.GetTiles(index: Integer): PgxHeightTileInfo;
- begin
- Result := @FTileIndex[index];
- end;
- function TgxHeightTileFile.IndexOfTile(aTile: PgxHeightTileInfo): Integer;
- var
- c: Cardinal;
- begin
- c := Cardinal(aTile) - Cardinal(@FTileIndex[0]);
- if (c mod SizeOf(TgxHeightTileInfo)) = 0 then
- begin
- Result := (c div SizeOf(TgxHeightTileInfo));
- if (Result < 0) or (Result > High(FTileIndex)) then
- Result := -1;
- end
- else
- Result := -1;
- end;
- function TgxHeightTileFile.TileCompressedSize(tileIndex: Integer): Integer;
- begin
- if tileIndex < High(FTileIndex) then
- Result := FTileIndex[tileIndex + 1].fileOffset - FTileIndex[tileIndex]
- .fileOffset
- else
- Result := TileIndexOffset - FTileIndex[tileIndex].fileOffset;
- end;
- end.
|