File indexing completed on 2024-04-21 03:50:01

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"