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 }