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 }