File indexing completed on 2024-04-21 03:49:47

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 }