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