File indexing completed on 2024-05-05 03:49:14
0001 // SPDX-FileCopyrightText: 2010 Jens-Michael Hoffmann <jmho@c-xx.com> 0002 // 0003 // SPDX-License-Identifier: LGPL-2.1-or-later 0004 0005 #include "SunLightBlending.h" 0006 0007 #include "MarbleDebug.h" 0008 #include "SunLocator.h" 0009 #include "TextureTile.h" 0010 #include "TileLoaderHelper.h" 0011 #include "MarbleGlobal.h" 0012 0013 #include <QImage> 0014 #include <QColor> 0015 0016 #include <cmath> 0017 0018 namespace Marble 0019 { 0020 0021 SunLightBlending::SunLightBlending( const SunLocator * sunLocator ) 0022 : Blending(), 0023 m_sunLocator( sunLocator ), 0024 m_levelZeroColumns( 0 ), 0025 m_levelZeroRows( 0 ) 0026 { 0027 } 0028 0029 SunLightBlending::~SunLightBlending() 0030 { 0031 } 0032 0033 void SunLightBlending::blend( QImage * const tileImage, TextureTile const * const top ) const 0034 { 0035 if ( tileImage->depth() != 32 ) 0036 return; 0037 0038 // TODO add support for 8-bit maps? 0039 // add sun shading 0040 const TileId id = top->id(); 0041 const qreal global_width = tileImage->width() 0042 * TileLoaderHelper::levelToColumn( m_levelZeroColumns, id.zoomLevel() ); 0043 const qreal global_height = tileImage->height() 0044 * TileLoaderHelper::levelToRow( m_levelZeroRows, id.zoomLevel() ); 0045 const qreal lon_scale = 2*M_PI / global_width; 0046 const qreal lat_scale = -M_PI / global_height; 0047 const int tileHeight = tileImage->height(); 0048 const int tileWidth = tileImage->width(); 0049 0050 // First we determine the supporting point interval for the interpolation. 0051 const int n = maxDivisor( 30, tileWidth ); 0052 const int ipRight = n * (int)( tileWidth / n ); 0053 0054 const QImage *nighttile = top->image(); 0055 0056 for ( int cur_y = 0; cur_y < tileHeight; ++cur_y ) { 0057 const qreal lat = lat_scale * ( id.y() * tileHeight + cur_y ) - 0.5*M_PI; 0058 const qreal a = sin( ( lat+DEG2RAD * m_sunLocator->getLat() )/2.0 ); 0059 const qreal c = cos(lat)*cos( -DEG2RAD * m_sunLocator->getLat() ); 0060 0061 QRgb* scanline = (QRgb*)tileImage->scanLine( cur_y ); 0062 const QRgb* nscanline = (QRgb*)nighttile->scanLine( cur_y ); 0063 0064 qreal lastShade = -10.0; 0065 0066 int cur_x = 0; 0067 0068 while ( cur_x < tileWidth ) { 0069 0070 const bool interpolate = ( cur_x != 0 && cur_x < ipRight && cur_x + n < tileWidth ); 0071 0072 qreal shade = 0; 0073 0074 if ( interpolate ) { 0075 const int check = cur_x + n; 0076 const qreal checklon = lon_scale * ( id.x() * tileWidth + check ); 0077 shade = m_sunLocator->shading( checklon, a, c ); 0078 0079 // if the shading didn't change across the interpolation 0080 // interval move on and don't change anything. 0081 if ( shade == lastShade && shade == 1.0 ) { 0082 scanline += n; 0083 nscanline += n; 0084 cur_x += n; 0085 continue; 0086 } 0087 if ( shade == lastShade && shade == 0.0 ) { 0088 for ( int t = 0; t < n; ++t ) { 0089 SunLocator::shadePixelComposite(*scanline, *nscanline, shade); 0090 ++scanline; 0091 ++nscanline; 0092 } 0093 cur_x += n; 0094 continue; 0095 } 0096 0097 qreal lon = lon_scale * (id.x() * tileWidth + cur_x); 0098 for ( int t = 0; t < n ; ++t ) { 0099 shade = m_sunLocator->shading( lon, a, c ); 0100 SunLocator::shadePixelComposite(*scanline, *nscanline, shade); 0101 ++scanline; 0102 ++nscanline; 0103 lon += lon_scale; 0104 } 0105 cur_x += n; 0106 } 0107 0108 else { 0109 // Make sure we don't exceed the image memory 0110 if ( cur_x < tileWidth ) { 0111 qreal lon = lon_scale * ( id.x() * tileWidth + cur_x ); 0112 shade = m_sunLocator->shading( lon, a, c ); 0113 SunLocator::shadePixelComposite(*scanline, *nscanline, shade); 0114 ++scanline; 0115 ++nscanline; 0116 ++cur_x; 0117 } 0118 } 0119 lastShade = shade; 0120 } 0121 } 0122 } 0123 0124 void SunLightBlending::setLevelZeroLayout( int levelZeroColumns, int levelZeroRows ) 0125 { 0126 m_levelZeroColumns = levelZeroColumns; 0127 m_levelZeroRows = levelZeroRows; 0128 } 0129 0130 // TODO: This should likely go into a math class in the future ... 0131 int SunLightBlending::maxDivisor( int maximum, int fullLength ) 0132 { 0133 // Find the optimal interpolation interval n for the 0134 // current image canvas width 0135 int best = 2; 0136 0137 int nEvalMin = fullLength; 0138 for ( int it = 1; it <= maximum; ++it ) { 0139 // The optimum is the interval which results in the least amount 0140 // supporting points taking into account the rest which can't 0141 // get used for interpolation. 0142 int nEval = fullLength / it + fullLength % it; 0143 if ( nEval < nEvalMin ) { 0144 nEvalMin = nEval; 0145 best = it; 0146 } 0147 } 0148 return best; 0149 } 0150 0151 }