File indexing completed on 2024-03-24 03:52:16
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2007 Carlos Licea <carlos _licea@hotmail.com> 0004 // SPDX-FileCopyrightText: 2011 Bernhard Beschow <bbeschow@cs.tu-berlin.de> 0005 // 0006 0007 0008 // local 0009 #include"EquirectScanlineTextureMapper.h" 0010 0011 // posix 0012 #include <cmath> 0013 0014 // Qt 0015 #include <QRunnable> 0016 0017 // Marble 0018 #include "GeoPainter.h" 0019 #include "MarbleDebug.h" 0020 #include "ScanlineTextureMapperContext.h" 0021 #include "StackedTileLoader.h" 0022 #include "TextureColorizer.h" 0023 #include "ViewportParams.h" 0024 #include "AbstractProjection.h" 0025 0026 using namespace Marble; 0027 0028 class EquirectScanlineTextureMapper::RenderJob : public QRunnable 0029 { 0030 public: 0031 RenderJob( StackedTileLoader *tileLoader, int tileLevel, QImage *canvasImage, const ViewportParams *viewportParams, MapQuality mapQuality, int yTop, int yBottom ); 0032 0033 void run() override; 0034 0035 private: 0036 StackedTileLoader *const m_tileLoader; 0037 const int m_tileLevel; 0038 QImage *const m_canvasImage; 0039 const ViewportParams *const m_viewport; 0040 const MapQuality m_mapQuality; 0041 const int m_yPaintedTop; 0042 const int m_yPaintedBottom; 0043 }; 0044 0045 EquirectScanlineTextureMapper::RenderJob::RenderJob( StackedTileLoader *tileLoader, int tileLevel, QImage *canvasImage, const ViewportParams *viewport, MapQuality mapQuality, int yTop, int yBottom ) 0046 : m_tileLoader( tileLoader ), 0047 m_tileLevel( tileLevel ), 0048 m_canvasImage( canvasImage ), 0049 m_viewport( viewport ), 0050 m_mapQuality( mapQuality ), 0051 m_yPaintedTop( yTop ), 0052 m_yPaintedBottom( yBottom ) 0053 { 0054 } 0055 0056 0057 EquirectScanlineTextureMapper::EquirectScanlineTextureMapper( StackedTileLoader *tileLoader ) 0058 : TextureMapperInterface(), 0059 m_tileLoader( tileLoader ), 0060 m_radius( 0 ), 0061 m_oldYPaintedTop( 0 ) 0062 { 0063 } 0064 0065 void EquirectScanlineTextureMapper::mapTexture( GeoPainter *painter, 0066 const ViewportParams *viewport, 0067 int tileZoomLevel, 0068 const QRect &dirtyRect, 0069 TextureColorizer *texColorizer ) 0070 { 0071 if ( m_canvasImage.size() != viewport->size() || m_radius != viewport->radius() ) { 0072 const QImage::Format optimalFormat = ScanlineTextureMapperContext::optimalCanvasImageFormat( viewport ); 0073 0074 if ( m_canvasImage.size() != viewport->size() || m_canvasImage.format() != optimalFormat ) { 0075 m_canvasImage = QImage( viewport->size(), optimalFormat ); 0076 } 0077 0078 if ( !viewport->mapCoversViewport() ) { 0079 m_canvasImage.fill( 0 ); 0080 } 0081 0082 m_radius = viewport->radius(); 0083 m_repaintNeeded = true; 0084 } 0085 0086 if ( m_repaintNeeded ) { 0087 mapTexture( viewport, tileZoomLevel, painter->mapQuality() ); 0088 0089 if ( texColorizer ) { 0090 texColorizer->colorize( &m_canvasImage, viewport, painter->mapQuality() ); 0091 } 0092 0093 m_repaintNeeded = false; 0094 } 0095 0096 painter->drawImage( dirtyRect, m_canvasImage, dirtyRect ); 0097 } 0098 0099 void EquirectScanlineTextureMapper::mapTexture( const ViewportParams *viewport, int tileZoomLevel, MapQuality mapQuality ) 0100 { 0101 // Reset backend 0102 m_tileLoader->resetTilehash(); 0103 0104 // Initialize needed constants: 0105 0106 const int imageHeight = m_canvasImage.height(); 0107 0108 // Calculate y-range the represented by the center point, yTop and 0109 // what actually can be painted 0110 0111 qreal realYTop, realYBottom, dummyX; 0112 GeoDataCoordinates yNorth(0, viewport->currentProjection()->maxLat(), 0); 0113 GeoDataCoordinates ySouth(0, viewport->currentProjection()->minLat(), 0); 0114 viewport->screenCoordinates(yNorth, dummyX, realYTop ); 0115 viewport->screenCoordinates(ySouth, dummyX, realYBottom ); 0116 0117 const int yTop = qBound(qreal(0.0), realYTop, qreal(imageHeight)); 0118 int yPaintedTop = yTop; 0119 int yPaintedBottom = qBound(qreal(0.0), realYBottom, qreal(imageHeight)); 0120 0121 yPaintedTop = qBound(0, yPaintedTop, imageHeight); 0122 yPaintedBottom = qBound(0, yPaintedBottom, imageHeight); 0123 0124 const int numThreads = m_threadPool.maxThreadCount(); 0125 const int yStep = ( yPaintedBottom - yPaintedTop ) / numThreads; 0126 for ( int i = 0; i < numThreads; ++i ) { 0127 const int yStart = yPaintedTop + i * yStep; 0128 const int yEnd = (i == numThreads - 1) ? yPaintedBottom : yPaintedTop + (i + 1) * yStep; 0129 QRunnable *const job = new RenderJob( m_tileLoader, tileZoomLevel, &m_canvasImage, viewport, mapQuality, yStart, yEnd ); 0130 m_threadPool.start( job ); 0131 } 0132 0133 // Remove unused lines 0134 const int clearStart = ( yPaintedTop - m_oldYPaintedTop <= 0 ) ? yPaintedBottom : 0; 0135 const int clearStop = ( yPaintedTop - m_oldYPaintedTop <= 0 ) ? imageHeight : yTop; 0136 0137 QRgb * const itClearBegin = (QRgb*)( m_canvasImage.scanLine( clearStart ) ); 0138 QRgb * const itClearEnd = (QRgb*)( m_canvasImage.scanLine( clearStop ) ); 0139 0140 for ( QRgb * it = itClearBegin; it < itClearEnd; ++it ) { 0141 *(it) = 0; 0142 } 0143 0144 m_threadPool.waitForDone(); 0145 0146 m_oldYPaintedTop = yPaintedTop; 0147 0148 m_tileLoader->cleanupTilehash(); 0149 } 0150 0151 void EquirectScanlineTextureMapper::RenderJob::run() 0152 { 0153 // Scanline based algorithm to do texture mapping 0154 0155 const int imageHeight = m_canvasImage->height(); 0156 const int imageWidth = m_canvasImage->width(); 0157 const qint64 radius = m_viewport->radius(); 0158 // Calculate how many degrees are being represented per pixel. 0159 const qreal rad2Pixel = (qreal)( 2 * radius ) / M_PI; 0160 const float pixel2Rad = 1.0/rad2Pixel; // FIXME changing to qreal may crash Marble when the equator is visible 0161 0162 const bool interlaced = ( m_mapQuality == LowQuality ); 0163 const bool highQuality = ( m_mapQuality == HighQuality 0164 || m_mapQuality == PrintQuality ); 0165 const bool printQuality = ( m_mapQuality == PrintQuality ); 0166 0167 // Evaluate the degree of interpolation 0168 const int n = ScanlineTextureMapperContext::interpolationStep( m_viewport, m_mapQuality ); 0169 0170 // Calculate translation of center point 0171 const qreal centerLon = m_viewport->centerLongitude(); 0172 const qreal centerLat = m_viewport->centerLatitude(); 0173 0174 const int yCenterOffset = (int)( centerLat * rad2Pixel ); 0175 0176 const int yTop = imageHeight / 2 - radius + yCenterOffset; 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 = M_PI/2 - (y - yTop )* pixel2Rad; 0198 0199 for ( int x = 0; x < imageWidth; ++x ) { 0200 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 }