LazMapViewer: Fix incorrect painting of stretched and missing tiles when Cyclic is true.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9680 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2025-03-13 15:50:11 +00:00
parent 7cf32c3448
commit 870c3252cb
3 changed files with 94 additions and 46 deletions
components/lazmapviewer/source

View File

@ -498,7 +498,7 @@ end;
the rectangle coordinates to get an upscaled preview of the originally the rectangle coordinates to get an upscaled preview of the originally
requested tile. The function returns true in this case. requested tile. The function returns true in this case.
If the requested tile already is in the cache, or no containing tile is found If the requested tile already is in the cache, or no containing tile is found
the function returns false indicating that not preview image must be the function returns false indicating that no preview image must be
generated. } generated. }
function TPictureCache.GetPreviewFromCache(const MapProvider: TMapProvider; function TPictureCache.GetPreviewFromCache(const MapProvider: TMapProvider;
var TileId: TTileId; out ARect: TRect): boolean; var TileId: TTileId; out ARect: TRect): boolean;

View File

@ -122,7 +122,7 @@ type
procedure CalculateWin(var AWin: TMapWindow); procedure CalculateWin(var AWin: TMapWindow);
function DegreesToPixelsEPSG3395(const AWin: TMapWindow; APt: TRealPoint): TPoint; function DegreesToPixelsEPSG3395(const AWin: TMapWindow; APt: TRealPoint): TPoint;
function DegreesToPixelsEPSG3857(const AWin: TMapWindow; APt: TRealPoint): TPoint; function DegreesToPixelsEPSG3857(const AWin: TMapWindow; APt: TRealPoint): TPoint;
procedure Redraw(const aWin: TMapWindow; const paintOnly: Boolean = False); procedure Redraw(const aWin: TMapWindow; const PaintOnly: Boolean = False);
function CalculateVisibleTiles(const aWin: TMapWindow; out Area: TArea): Boolean; function CalculateVisibleTiles(const aWin: TMapWindow; out Area: TArea): Boolean;
function IsCurrentWin(const aWin: TMapWindow) : boolean; function IsCurrentWin(const aWin: TMapWindow) : boolean;
protected protected
@ -132,7 +132,7 @@ type
procedure evDownload(Data: TObject; Job: TJob); procedure evDownload(Data: TObject; Job: TJob);
procedure TileDownloaded(Data: PtrInt); procedure TileDownloaded(Data: PtrInt);
procedure EraseBackground(const R: TRect); procedure EraseBackground(const R: TRect);
procedure DrawTileFromCache(constref ATile: TTileId; constref AWin: TMapWindow); procedure DrawTileFromCache(constref ATile: TTileId; constref AWin: TMapWindow; out FoundInCache: Boolean);
procedure DrawStretchedTile(const TileId: TTileID; X, Y: Integer; TileImg: TPictureCacheItem; const R: TRect); procedure DrawStretchedTile(const TileId: TTileID; X, Y: Integer; TileImg: TPictureCacheItem; const R: TRect);
Procedure DrawTile(const TileId: TTileId; X,Y: integer; TileImg: TPictureCacheItem); Procedure DrawTile(const TileId: TTileId; X,Y: integer; TileImg: TPictureCacheItem);
Procedure DoDrag(Sender: TDragObj); Procedure DoDrag(Sender: TDragObj);
@ -1089,12 +1089,13 @@ begin
end; end;
procedure TMapViewerEngine.Redraw(const aWin: TMapWindow; procedure TMapViewerEngine.Redraw(const aWin: TMapWindow;
const paintOnly: Boolean); const PaintOnly: Boolean);
var var
TilesVis: TArea; TilesVis: TArea;
x, y, px, py: Integer; x, y, px, py: Integer;
iTile, numTiles, XShift: Integer; iTile, numTiles, XShift: Integer;
Tiles: TTileIdArray = nil; Tiles: TTileIdArray = nil;
foundInCache: Boolean;
tile: TTileID; tile: TTileID;
previewDrawn: Boolean; previewDrawn: Boolean;
previewImg: TPictureCacheItem; previewImg: TPictureCacheItem;
@ -1105,8 +1106,8 @@ var
lTile: TEnvTile; lTile: TEnvTile;
lJob: TEventJob; lJob: TEventJob;
begin begin
lTile:=TEnvTile.Create(Tiles[iTile], aWin); lTile := TEnvTile.Create(Tiles[iTile], aWin);
lJob := TEventJob.Create(@evDownload, lTile, False, // owns data lJob := TEventJob.Create(@evDownload, lTile, False, // owns data
GetTileName(Tiles[iTile])); GetTileName(Tiles[iTile]));
if not Queue.AddUniqueJob(lJob, Self) then if not Queue.AddUniqueJob(lJob, Self) then
begin begin
@ -1159,11 +1160,12 @@ begin
if not CalculateVisibleTiles(AWin, TilesVis) then if not CalculateVisibleTiles(AWin, TilesVis) then
EraseAround; EraseAround;
SetLength(Tiles, (TilesVis.Bottom - TilesVis.Top + 1) * (TilesVis.Right - TilesVis.Left + 1)); SetLength(Tiles, (TilesVis.Bottom - TilesVis.Top + 1) * (TilesVis.Right - TilesVis.Left + 1));
iTile := Low(Tiles); iTile := Low(Tiles);
numTiles := 1 shl AWin.Zoom; numTiles := 1 shl AWin.Zoom;
XShift := IfThen(aWin.X > 0, numTiles - aWin.X div TileSize.CX - 1, 0); XShift := IfThen(aWin.X > 0, numTiles - aWin.X div TileSize.CX - 1, 0);
for y := TilesVis.Top to TilesVis.Bottom do for Y := TilesVis.Top to TilesVis.Bottom do
for X := TilesVis.Left to TilesVis.Right do for X := TilesVis.Left to TilesVis.Right do
begin begin
if FCyclic then if FCyclic then
@ -1177,6 +1179,14 @@ begin
Tiles[iTile].Y := Y; Tiles[iTile].Y := Y;
Tiles[iTile].Z := AWin.Zoom; Tiles[iTile].Z := AWin.Zoom;
DrawTileFromCache(Tiles[iTile], AWin, foundInCache);
if (not foundInCache) and (not PaintOnly) and IsValidTile(AWin, Tiles[iTile]) then
begin
AddJob;
inc(iTile);
end;
(*
if Cache.InCache(AWin.MapProvider, Tiles[iTile]) then if Cache.InCache(AWin.MapProvider, Tiles[iTile]) then
DrawTileFromCache(Tiles[iTile], AWin) DrawTileFromCache(Tiles[iTile], AWin)
else else
@ -1202,12 +1212,15 @@ begin
if not previewDrawn then if not previewDrawn then
DrawTile(Tiles[iTile], px, py, nil); // Draw blank tile if preview cannot be generated DrawTile(Tiles[iTile], px, py, nil); // Draw blank tile if preview cannot be generated
// !!!!!!!!!!!!!!!!!!!!!!
if not paintOnly and IsValidTile(AWin, Tiles[iTile]) then if not paintOnly and IsValidTile(AWin, Tiles[iTile]) then
begin begin
AddJob; AddJob;
inc(iTile); inc(iTile);
end; end;
end; end;
*)
end; end;
SetLength(Tiles, iTile); SetLength(Tiles, iTile);
end; end;
@ -1416,47 +1429,83 @@ end;
procedure TMapViewerEngine.EraseBackground(const R: TRect); procedure TMapViewerEngine.EraseBackground(const R: TRect);
begin begin
if Assigned(FOnEraseBackground) then if Assigned(FOnEraseBackground) then
FOnEraseBackground(R); FOnEraseBackground(R);
end; end;
procedure TMapViewerEngine.DrawTileFromCache(constref ATile: TTileId; constref procedure TMapViewerEngine.DrawTileFromCache(constref ATile: TTileID;
AWin: TMapWindow); constref AWin: TMapWindow; out FoundInCache: Boolean);
var var
img: TPictureCacheItem; img: TPictureCacheItem;
X, Y: integer; X, Y: Integer;
worldWidth : Integer; worldWidth: Integer;
numTiles : Integer; numTiles: Integer;
baseX : Integer; baseX: Integer;
begin tile: TTileID;
if IsCurrentWin(AWin)then R: TRect;
normalDraw: Boolean = true;
procedure DrawTheTile(ATileID: TTileID; X, Y: Integer; AImg: TPictureCacheItem);
begin begin
Cache.GetFromCache(AWin.MapProvider, ATile, img); if normalDraw then
Y := AWin.Y + ATile.Y * TileSize.CY; // begin of Y DrawTile(ATileID, X, Y, AImg) // Covers the "missing tile" case when AImg = nil
if Cyclic then else
begin DrawStretchedTile(ATileID, X, Y, AImg, R);
baseX := AWin.X + ATile.X * TileSize.CX; // begin of X
numTiles := 1 shl AWin.Zoom;
worldWidth := numTiles * TileSize.CX;
// From the center to the left (western) hemisphere
X := baseX;
while (X + TileSize.CX >= 0) do
begin
DrawTile(ATile, X, Y, img);
X := X - worldWidth;
end;
// From the center to the right (eastern) hemisphere
X := baseX + worldWidth;
while ((X - TileSize.CX) <= AWin.Width) do
begin
DrawTile(ATile, X, Y, img);
X := X + worldWidth;
end;
end else
begin
X := AWin.X + ATile.X * TileSize.CX; // begin of X
DrawTile(ATile, X, Y, img);
end;
end; end;
begin
if not IsCurrentWin(AWin) then
exit;
// When no cache image can be found (img = nil) we will have to draw a "missing tile"
img := nil;
FoundInCache := false;
if Cache.InCache(AWin.MapProvider, ATile) then
begin
// Image is found in cache: Load it into "img". It can be drawn directly.
Cache.GetFromCache(AWin.MapProvider, ATile, img);
FoundInCache := true;
end else
if FDrawPreviewTiles then
begin
// Image is not found in cache, but there is another one which can be
// scaled to fit. Find the cache parameters for this image.
tile := ATile;
if Cache.GetPreviewFromCache(AWin.MapProvider, tile, R) then
begin
// Load cache image into "img". It must be stretch-drawn.
Cache.GetFromCache(AWin.MapProvider, tile, img);
normalDraw := false;
end;
end;
X := AWin.X + ATile.X * TileSize.CX; // begin of X
Y := AWin.Y + ATile.Y * TileSize.CY; // begin of Y
if Cyclic then
begin
baseX := X;
numTiles := 1 shl AWin.Zoom;
worldWidth := numTiles * TileSize.CX;
// Center, plus western hemisphere (left)
X := baseX;
while (X + TileSize.CX >= 0) do
begin
DrawTheTile(ATile, X, Y, img);
X := X - worldWidth;
end;
// From the center to the right (eastern) hemisphere
X := baseX + worldWidth;
while (X <= AWin.Width) do
begin
DrawTheTile(ATile, X, Y, img);
X := X + worldWidth;
end;
end
else
DrawTheTile(ATile, X, Y, img);
end; end;
function TMapViewerEngine.ValidProvider(const AProvider: String): Boolean; function TMapViewerEngine.ValidProvider(const AProvider: String): Boolean;

View File

@ -3098,7 +3098,7 @@ procedure TMapView.Paint;
const const
FREE_DRAG = 0; //(TILE_SIZE * TILE_SIZE) div 4; FREE_DRAG = 0; //(TILE_SIZE * TILE_SIZE) div 4;
procedure DrawCenter; procedure DrawCenter; deprecated 'Use plugin';
var var
C: TPoint; C: TPoint;
begin begin
@ -3136,7 +3136,7 @@ const
FAfterDrawObjectsEvent(Self); FAfterDrawObjectsEvent(Self);
DrawingEngine.PaintToCanvas(Canvas); DrawingEngine.PaintToCanvas(Canvas);
if DebugTiles then if DebugTiles then // DebugTiles is deprecated
DrawCenter; DrawCenter;
GetPluginManager.AfterPaint(Self); GetPluginManager.AfterPaint(Self);
@ -3154,7 +3154,7 @@ const
begin begin
DrawingEngine.PaintToCanvas(Canvas); DrawingEngine.PaintToCanvas(Canvas);
DrawingEngine.PaintToCanvas(Canvas, O); DrawingEngine.PaintToCanvas(Canvas, O);
if DebugTiles then if DebugTiles then // DebugTiles is deprecated
DrawCenter; DrawCenter;
end end
else else
@ -3681,7 +3681,6 @@ begin
DrawingEngine.FillPixels(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom, InactiveColor); DrawingEngine.FillPixels(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom, InactiveColor);
end; end;
procedure TMapView.DoDrawTileInfo(const TileID: TTileID; X, Y: Integer); procedure TMapView.DoDrawTileInfo(const TileID: TTileID; X, Y: Integer);
begin begin
DrawingEngine.PenColor := clGray; DrawingEngine.PenColor := clGray;