File indexing completed on 2024-04-28 11:31:10
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2007 Carlos Licea <carlos _licea@hotmail.com> 0004 // SPDX-FileCopyrightText: 2008 Inge Wallin <inge@lysator.liu.se> 0005 // SPDX-FileCopyrightText: 2011 Bernhard Beschow <bbeschow@cs.tu-berlin.de> 0006 // 0007 0008 0009 // local 0010 #include"MercatorScanlineTextureMapper.h" 0011 0012 // posix 0013 #include <cmath> 0014 0015 // Qt 0016 #include <QRunnable> 0017 0018 // Marble 0019 #include "GeoPainter.h" 0020 #include "MarbleDebug.h" 0021 #include "ScanlineTextureMapperContext.h" 0022 #include "StackedTileLoader.h" 0023 #include "TextureColorizer.h" 0024 #include "ViewportParams.h" 0025 #include "MathHelper.h" 0026 #include "AbstractProjection.h" 0027 0028 using namespace Marble; 0029 0030 class MercatorScanlineTextureMapper::RenderJob : public QRunnable 0031 { 0032 public: 0033 RenderJob( StackedTileLoader *tileLoader, int tileLevel, QImage *canvasImage, const ViewportParams *viewport, MapQuality mapQuality, int yTop, int yBottom ); 0034 0035 void run() override; 0036 0037 private: 0038 StackedTileLoader *const m_tileLoader; 0039 const int m_tileLevel; 0040 QImage *const m_canvasImage; 0041 const ViewportParams *const m_viewport; 0042 const MapQuality m_mapQuality; 0043 const int m_yPaintedTop; 0044 const int m_yPaintedBottom; 0045 }; 0046 0047 MercatorScanlineTextureMapper::RenderJob::RenderJob( StackedTileLoader *tileLoader, int tileLevel, QImage *canvasImage, const ViewportParams *viewport, MapQuality mapQuality, int yTop, int yBottom ) 0048 : m_tileLoader( tileLoader ), 0049 m_tileLevel( tileLevel ), 0050 m_canvasImage( canvasImage ), 0051 m_viewport( viewport ), 0052 m_mapQuality( mapQuality ), 0053 m_yPaintedTop( yTop ), 0054 m_yPaintedBottom( yBottom ) 0055 { 0056 } 0057 0058 MercatorScanlineTextureMapper::MercatorScanlineTextureMapper( StackedTileLoader *tileLoader ) 0059 : TextureMapperInterface(), 0060 m_tileLoader( tileLoader ), 0061 m_radius( 0 ), 0062 m_oldYPaintedTop( 0 ) 0063 { 0064 } 0065 0066 void MercatorScanlineTextureMapper::mapTexture( GeoPainter *painter, 0067 const ViewportParams *viewport, 0068 int tileZoomLevel, 0069 const QRect &dirtyRect, 0070 TextureColorizer *texColorizer ) 0071 { 0072 if ( m_canvasImage.size() != viewport->size() || m_radius != viewport->radius() ) { 0073 const QImage::Format optimalFormat = ScanlineTextureMapperContext::optimalCanvasImageFormat( viewport ); 0074 0075 if ( m_canvasImage.size() != viewport->size() || m_canvasImage.format() != optimalFormat ) { 0076 m_canvasImage = QImage( viewport->size(), optimalFormat ); 0077 } 0078 0079 if ( !viewport->mapCoversViewport() ) { 0080 m_canvasImage.fill( 0 ); 0081 } 0082 0083 m_radius = viewport->radius(); 0084 m_repaintNeeded = true; 0085 } 0086 0087 if ( m_repaintNeeded ) { 0088 mapTexture( viewport, tileZoomLevel, painter->mapQuality() ); 0089 0090 if ( texColorizer ) { 0091 texColorizer->colorize( &m_canvasImage, viewport, painter->mapQuality() ); 0092 } 0093 0094 m_repaintNeeded = false; 0095 } 0096 0097 painter->drawImage( dirtyRect, m_canvasImage, dirtyRect ); 0098 } 0099 0100 void MercatorScanlineTextureMapper::mapTexture( const ViewportParams *viewport, int tileZoomLevel, MapQuality mapQuality ) 0101 { 0102 // Reset backend 0103 m_tileLoader->resetTilehash(); 0104 0105 // Initialize needed constants: 0106 0107 const int imageHeight = m_canvasImage.height(); 0108 0109 // Calculate y-range the represented by the center point, yTop and 0110 // what actually can be painted 0111 0112 qreal realYTop, realYBottom, dummyX; 0113 GeoDataCoordinates yNorth(0, viewport->currentProjection()->maxLat(), 0); 0114 GeoDataCoordinates ySouth(0, viewport->currentProjection()->minLat(), 0); 0115 viewport->screenCoordinates(yNorth, dummyX, realYTop ); 0116 viewport->screenCoordinates(ySouth, dummyX, realYBottom ); 0117 0118 const int yTop = qBound(qreal(0.0), realYTop, qreal(imageHeight)); 0119 int yPaintedTop = yTop; 0120 int yPaintedBottom = qBound(qreal(0.0), realYBottom, qreal(imageHeight)); 0121 0122 yPaintedTop = qBound(0, yPaintedTop, imageHeight); 0123 yPaintedBottom = qBound(0, yPaintedBottom, imageHeight); 0124 0125 const int numThreads = m_threadPool.maxThreadCount(); 0126 const int yStep = ( yPaintedBottom - yPaintedTop ) / numThreads; 0127 for ( int i = 0; i < numThreads; ++i ) { 0128 const int yStart = yPaintedTop + i * yStep; 0129 const int yEnd = (i == numThreads - 1) ? yPaintedBottom : yPaintedTop + (i + 1) * yStep; 0130 QRunnable *const job = new RenderJob( m_tileLoader, tileZoomLevel, &m_canvasImage, viewport, mapQuality, yStart, yEnd ); 0131 m_threadPool.start( job ); 0132 } 0133 0134 // Remove unused lines 0135 const int clearStart = ( yPaintedTop - m_oldYPaintedTop <= 0 ) ? yPaintedBottom : 0; 0136 const int clearStop = ( yPaintedTop - m_oldYPaintedTop <= 0 ) ? imageHeight : yTop; 0137 0138 QRgb * const itClearBegin = (QRgb*)( m_canvasImage.scanLine( clearStart ) ); 0139 QRgb * const itClearEnd = (QRgb*)( m_canvasImage.scanLine( clearStop ) ); 0140 0141 for ( QRgb * it = itClearBegin; it < itClearEnd; ++it ) { 0142 *(it) = 0; 0143 } 0144 0145 m_threadPool.waitForDone(); 0146 0147 m_oldYPaintedTop = yPaintedTop; 0148 0149 m_tileLoader->cleanupTilehash(); 0150 } 0151 0152 0153 void MercatorScanlineTextureMapper::RenderJob::run() 0154 { 0155 // Scanline based algorithm to do texture mapping 0156 0157 const int imageHeight = m_canvasImage->height(); 0158 const int imageWidth = m_canvasImage->width(); 0159 const qint64 radius = m_viewport->radius(); 0160 // Calculate how many degrees are being represented per pixel. 0161 const float rad2Pixel = (float)( 2 * radius ) / M_PI; 0162 const qreal pixel2Rad = 1.0/rad2Pixel; 0163 0164 const bool interlaced = ( m_mapQuality == LowQuality ); 0165 const bool highQuality = ( m_mapQuality == HighQuality 0166 || m_mapQuality == PrintQuality ); 0167 const bool printQuality = ( m_mapQuality == PrintQuality ); 0168 0169 // Evaluate the degree of interpolation 0170 const int n = ScanlineTextureMapperContext::interpolationStep( m_viewport, m_mapQuality ); 0171 0172 // Calculate translation of center point 0173 const qreal centerLon = m_viewport->centerLongitude(); 0174 const qreal centerLat = m_viewport->centerLatitude(); 0175 0176 const int yCenterOffset = (int)( asinh( tan( centerLat ) ) * rad2Pixel ); 0177 0178 qreal leftLon = + centerLon - ( imageWidth / 2 * pixel2Rad ); 0179 while ( leftLon < -M_PI ) leftLon += 2 * M_PI; 0180 while ( leftLon > M_PI ) leftLon -= 2 * M_PI; 0181 0182 const int maxInterpolationPointX = n * (int)( imageWidth / n - 1 ) + 1; 0183 0184 0185 // initialize needed variables that are modified during texture mapping: 0186 0187 ScanlineTextureMapperContext context( m_tileLoader, m_tileLevel ); 0188 0189 0190 // Scanline based algorithm to do texture mapping 0191 0192 for ( int y = m_yPaintedTop; y < m_yPaintedBottom; ++y ) { 0193 0194 QRgb * scanLine = (QRgb*)( m_canvasImage->scanLine( y ) ); 0195 0196 qreal lon = leftLon; 0197 const qreal lat = gd ( ( (imageHeight / 2 + yCenterOffset) - y ) 0198 * pixel2Rad ); 0199 0200 for ( int x = 0; x < imageWidth; ++x ) { 0201 // Prepare for interpolation 0202 bool interpolate = false; 0203 if ( x > 0 && x <= maxInterpolationPointX ) { 0204 x += n - 1; 0205 lon += (n - 1) * pixel2Rad; 0206 interpolate = !printQuality; 0207 } 0208 else { 0209 interpolate = false; 0210 } 0211 0212 if ( lon < -M_PI ) lon += 2 * M_PI; 0213 if ( lon > M_PI ) lon -= 2 * M_PI; 0214 0215 if ( interpolate ) { 0216 if (highQuality) 0217 context.pixelValueApproxF( lon, lat, scanLine, n ); 0218 else 0219 context.pixelValueApprox( lon, lat, scanLine, n ); 0220 0221 scanLine += ( n - 1 ); 0222 } 0223 0224 if ( x < imageWidth ) { 0225 if ( highQuality ) 0226 context.pixelValueF( lon, lat, scanLine ); 0227 else 0228 context.pixelValue( lon, lat, scanLine ); 0229 } 0230 0231 ++scanLine; 0232 lon += pixel2Rad; 0233 } 0234 0235 // copy scanline to improve performance 0236 if ( interlaced && y + 1 < m_yPaintedBottom ) { 0237 0238 const int pixelByteSize = m_canvasImage->bytesPerLine() / imageWidth; 0239 0240 memcpy( m_canvasImage->scanLine( y + 1 ), 0241 m_canvasImage->scanLine( y ), 0242 imageWidth * pixelByteSize ); 0243 ++y; 0244 } 0245 } 0246 }