File indexing completed on 2025-01-05 03:59:38
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2010-2012 Bernhard Beschow <bbeschow@cs.tu-berlin.de> 0004 // 0005 0006 0007 // local 0008 #include "TileScalingTextureMapper.h" 0009 0010 // posix 0011 #include <cmath> 0012 0013 // Qt 0014 #include <qmath.h> 0015 0016 // Marble 0017 #include "GeoPainter.h" 0018 #include "ScanlineTextureMapperContext.h" 0019 #include "StackedTileLoader.h" 0020 #include "TextureColorizer.h" 0021 #include "TileLoaderHelper.h" 0022 #include "StackedTile.h" 0023 #include "MathHelper.h" 0024 #include "ViewportParams.h" 0025 0026 using namespace Marble; 0027 0028 TileScalingTextureMapper::TileScalingTextureMapper( StackedTileLoader *tileLoader, QObject *parent ) 0029 : QObject( parent ), 0030 TextureMapperInterface(), 0031 m_tileLoader( tileLoader ), 0032 m_cache( 100 ), 0033 m_radius( 0 ) 0034 { 0035 connect( tileLoader, SIGNAL(tileLoaded(TileId)), 0036 this, SLOT(removePixmap(TileId)) ); 0037 connect( tileLoader, SIGNAL(cleared()), 0038 this, SLOT(clearPixmaps()) ); 0039 } 0040 0041 void TileScalingTextureMapper::mapTexture( GeoPainter *painter, 0042 const ViewportParams *viewport, 0043 int tileZoomLevel, 0044 const QRect &dirtyRect, 0045 TextureColorizer *texColorizer ) 0046 { 0047 if ( viewport->radius() <= 0 ) 0048 return; 0049 0050 if ( texColorizer || m_radius != viewport->radius() ) { 0051 if ( m_canvasImage.size() != viewport->size() || m_radius != viewport->radius() ) { 0052 const QImage::Format optimalFormat = ScanlineTextureMapperContext::optimalCanvasImageFormat( viewport ); 0053 0054 if ( m_canvasImage.size() != viewport->size() || m_canvasImage.format() != optimalFormat ) { 0055 m_canvasImage = QImage( viewport->size(), optimalFormat ); 0056 } 0057 0058 if ( !viewport->mapCoversViewport() ) { 0059 m_canvasImage.fill( 0 ); 0060 } 0061 0062 m_repaintNeeded = true; 0063 } 0064 0065 if ( m_repaintNeeded ) { 0066 mapTexture( painter, viewport, tileZoomLevel, texColorizer ); 0067 0068 m_radius = viewport->radius(); 0069 m_repaintNeeded = false; 0070 } 0071 0072 painter->drawImage( dirtyRect, m_canvasImage, dirtyRect ); 0073 } else { 0074 mapTexture( painter, viewport, tileZoomLevel, texColorizer ); 0075 0076 m_radius = viewport->radius(); 0077 } 0078 } 0079 0080 void TileScalingTextureMapper::mapTexture( GeoPainter *painter, const ViewportParams *viewport, int tileZoomLevel, TextureColorizer *texColorizer ) 0081 { 0082 const int imageHeight = viewport->height(); 0083 const int imageWidth = viewport->width(); 0084 const qint64 radius = viewport->radius(); 0085 0086 const bool highQuality = ( painter->mapQuality() == HighQuality 0087 || painter->mapQuality() == PrintQuality ); 0088 0089 // Reset backend 0090 m_tileLoader->resetTilehash(); 0091 0092 // Calculate translation of center point 0093 const qreal centerLon = viewport->centerLongitude(); 0094 const qreal centerLat = viewport->centerLatitude(); 0095 0096 const int numTilesX = m_tileLoader->tileRowCount( tileZoomLevel ); 0097 const int numTilesY = m_tileLoader->tileColumnCount( tileZoomLevel ); 0098 Q_ASSERT( numTilesX > 0 ); 0099 Q_ASSERT( numTilesY > 0 ); 0100 0101 const qreal xNormalizedCenter = 0.5 + 0.5 * centerLon / M_PI; 0102 const int minTileX = qFloor( numTilesX * ( xNormalizedCenter - imageWidth/( 8.0 * radius ) ) ); 0103 const int maxTileX = numTilesX * ( xNormalizedCenter + imageWidth/( 8.0 * radius ) ); 0104 0105 const qreal yNormalizedCenter = 0.5 - 0.5 * asinh( tan( centerLat ) ) / M_PI; 0106 const int minTileY = qMax( qreal( numTilesY * ( yNormalizedCenter - imageHeight/( 8.0 * radius ) ) ), 0107 qreal( 0.0 ) ); 0108 const int maxTileY = qMin( qreal( numTilesY * ( yNormalizedCenter + imageHeight/( 8.0 * radius ) ) ), 0109 qreal( numTilesY - 1.0 ) ); 0110 0111 if ( m_radius != radius ) { 0112 m_cache.clear(); 0113 } 0114 0115 if ( texColorizer || m_radius != radius ) { 0116 QPainter imagePainter( &m_canvasImage ); 0117 imagePainter.setRenderHint( QPainter::SmoothPixmapTransform, highQuality ); 0118 0119 for ( int tileY = minTileY; tileY <= maxTileY; ++tileY ) { 0120 for ( int tileX = minTileX; tileX <= maxTileX; ++tileX ) { 0121 const qreal xLeft = ( 4.0 * radius ) * ( ( tileX ) / (qreal)numTilesX - xNormalizedCenter ) + ( imageWidth / 2.0 ); 0122 const qreal xRight = ( 4.0 * radius ) * ( ( tileX + 1 ) / (qreal)numTilesX - xNormalizedCenter ) + ( imageWidth / 2.0 ); 0123 const qreal yTop = ( 4.0 * radius ) * ( ( tileY ) / (qreal)numTilesY - yNormalizedCenter ) + ( imageHeight / 2.0 ); 0124 const qreal yBottom = ( 4.0 * radius ) * ( ( tileY + 1 ) / (qreal)numTilesY - yNormalizedCenter ) + ( imageHeight / 2.0 ); 0125 0126 const QRectF rect = QRectF( QPointF( xLeft, yTop ), QPointF( xRight, yBottom ) ); 0127 const TileId stackedId = TileId( 0, tileZoomLevel, ( ( tileX % numTilesX ) + numTilesX ) % numTilesX, tileY ); 0128 0129 const StackedTile *const tile = m_tileLoader->loadTile( stackedId ); 0130 0131 imagePainter.drawImage( rect, *tile->resultImage() ); 0132 } 0133 } 0134 0135 if ( texColorizer ) { 0136 texColorizer->colorize( &m_canvasImage, viewport, painter->mapQuality() ); 0137 } 0138 } else { 0139 painter->save(); 0140 painter->setRenderHint( QPainter::SmoothPixmapTransform, highQuality ); 0141 0142 for ( int tileY = minTileY; tileY <= maxTileY; ++tileY ) { 0143 for ( int tileX = minTileX; tileX <= maxTileX; ++tileX ) { 0144 const qreal xLeft = ( 4.0 * radius ) * ( ( tileX ) / (qreal)numTilesX - xNormalizedCenter ) + ( imageWidth / 2.0 ); 0145 const qreal xRight = ( 4.0 * radius ) * ( ( tileX + 1 ) / (qreal)numTilesX - xNormalizedCenter ) + ( imageWidth / 2.0 ); 0146 const qreal yTop = ( 4.0 * radius ) * ( ( tileY ) / (qreal)numTilesY - yNormalizedCenter ) + ( imageHeight / 2.0 ); 0147 const qreal yBottom = ( 4.0 * radius ) * ( ( tileY + 1 ) / (qreal)numTilesY - yNormalizedCenter ) + ( imageHeight / 2.0 ); 0148 0149 const QRectF rect = QRectF( QPointF( xLeft, yTop ), QPointF( xRight, yBottom ) ); 0150 const TileId stackedId = TileId( 0, tileZoomLevel, ( ( tileX % numTilesX ) + numTilesX ) % numTilesX, tileY ); 0151 const StackedTile *const tile = m_tileLoader->loadTile( stackedId ); // load tile here for every frame, otherwise cleanupTilehash() clears all visible tiles 0152 0153 const QSize size = QSize( qCeil( rect.right() - rect.left() ), qCeil( rect.bottom() - rect.top() ) ); 0154 const int cacheHash = 2 * ( size.width() % 2 ) + ( size.height() % 2 ); 0155 const TileId cacheId = TileId( cacheHash, stackedId.zoomLevel(), stackedId.x(), stackedId.y() ); 0156 0157 const QPixmap *const im_cached = m_cache[cacheId]; 0158 const QPixmap *im = im_cached; 0159 if ( im == nullptr ) { 0160 im = new QPixmap( QPixmap::fromImage( tile->resultImage()->scaled( size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) ) ); 0161 } 0162 painter->drawPixmap( rect.topLeft(), *im ); 0163 0164 if (im != im_cached) 0165 m_cache.insert( cacheId, im ); 0166 } 0167 } 0168 0169 painter->restore(); 0170 } 0171 0172 m_tileLoader->cleanupTilehash(); 0173 } 0174 0175 void TileScalingTextureMapper::removePixmap( const TileId &tileId ) 0176 { 0177 const TileId stackedTileId( 0, tileId.zoomLevel(), tileId.x(), tileId.y() ); 0178 for ( int i = 0; i < 4; ++i ) { 0179 const TileId id = TileId( i, stackedTileId.zoomLevel(), stackedTileId.x(), stackedTileId.y() ); 0180 0181 m_cache.remove( id ); 0182 } 0183 } 0184 0185 void TileScalingTextureMapper::clearPixmaps() 0186 { 0187 m_cache.clear(); 0188 } 0189 0190 #include "moc_TileScalingTextureMapper.cpp"